diff --git a/rd_ui/app/login.html b/rd_ui/app/login.html index 1bf244b351..26ca68a5f8 100644 --- a/rd_ui/app/login.html +++ b/rd_ui/app/login.html @@ -13,6 +13,10 @@ + + + + @@ -26,13 +30,20 @@ - {{name}} +
+ {% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + {% endwith %}
{% if show_google_openid %} diff --git a/redash/controllers.py b/redash/controllers.py index 6ba4737cb5..7c845445ef 100644 --- a/redash/controllers.py +++ b/redash/controllers.py @@ -12,7 +12,7 @@ import logging from flask import render_template, send_from_directory, make_response, request, jsonify, redirect, \ - session, url_for, current_app + session, url_for, current_app, flash from flask.ext.restful import Resource, abort from flask_login import current_user, login_user, logout_user, login_required import sqlparse @@ -80,7 +80,7 @@ def login(): login_user(user, remember=remember) return redirect(request.args.get('next') or '/') except models.User.DoesNotExist: - pass + flash("Wrong username or password.") return render_template("login.html", name=settings.NAME, diff --git a/redash/google_oauth.py b/redash/google_oauth.py index b73ccdb9a8..72300ec945 100644 --- a/redash/google_oauth.py +++ b/redash/google_oauth.py @@ -1,25 +1,25 @@ import logging from flask.ext.login import login_user import requests -from flask import redirect, url_for, Blueprint +from flask import redirect, url_for, Blueprint, flash from flask_oauth import OAuth from redash import models, settings logger = logging.getLogger('google_oauth') oauth = OAuth() -request_token_params = {'scope': 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile', 'response_type': 'code'} -if settings.GOOGLE_APPS_DOMAIN: - request_token_params['hd'] = settings.GOOGLE_APPS_DOMAIN -else: +if not settings.GOOGLE_APPS_DOMAIN: logger.warning("No Google Apps domain defined, all Google accounts allowed.") google = oauth.remote_app('google', base_url='https://www.google.com/accounts/', authorize_url='https://accounts.google.com/o/oauth2/auth', request_token_url=None, - request_token_params=request_token_params, + request_token_params={ + 'scope': 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile', + 'response_type': 'code' + }, access_token_url='https://accounts.google.com/o/oauth2/token', access_token_method='POST', access_token_params={'grant_type': 'authorization_code'}, @@ -31,7 +31,7 @@ def get_user_profile(access_token): - headers = {'Authorization': 'OAuth '+access_token} + headers = {'Authorization': 'OAuth {}'.format(access_token)} response = requests.get('https://www.googleapis.com/oauth2/v1/userinfo', headers=headers) if response.status_code == 401: @@ -41,9 +41,17 @@ def get_user_profile(access_token): return response.json() +def verify_profile(profile): + if not settings.GOOGLE_APPS_DOMAIN: + return True + + domain = profile['email'].split('@')[-1] + return domain in settings.GOOGLE_APPS_DOMAIN + + def create_and_login_user(name, email): try: - user_object = models.User.get(models.User.email == email) + user_object = models.User.get_by_email(email) if user_object.name != name: logger.debug("Updating user name (%r -> %r)", user_object.name, name) user_object.name = name @@ -70,10 +78,17 @@ def authorized(resp): if access_token is None: logger.warning("Access token missing in call back request.") + flash("Validation error. Please retry.") return redirect(url_for('login')) profile = get_user_profile(access_token) if profile is None: + flash("Validation error. Please retry.") + return redirect(url_for('login')) + + if not verify_profile(profile): + logger.warning("User tried to login with unauthorized domain name: %s", profile['email']) + flash("Your Google Apps domain name isn't allowed.") return redirect(url_for('login')) create_and_login_user(profile['name'], profile['email']) diff --git a/redash/settings.py b/redash/settings.py index 4cb0366d62..2bef79cda1 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -32,6 +32,10 @@ def array_from_string(str): return array +def set_from_string(str): + return set(array_from_string(str)) + + def parse_boolean(str): return json.loads(str.lower()) @@ -60,7 +64,7 @@ def parse_boolean(str): # Google Apps domain to allow access from; any user with email in this Google Apps will be allowed # access -GOOGLE_APPS_DOMAIN = os.environ.get("REDASH_GOOGLE_APPS_DOMAIN", "") +GOOGLE_APPS_DOMAIN = set_from_string(os.environ.get("REDASH_GOOGLE_APPS_DOMAIN", "")) GOOGLE_CLIENT_ID = os.environ.get("REDASH_GOOGLE_CLIENT_ID", "") GOOGLE_CLIENT_SECRET = os.environ.get("REDASH_GOOGLE_CLIENT_SECRET", "")