From babf62bae0b4153e64d57dba47b265f368a23746 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Sun, 19 Aug 2018 15:29:50 +0700 Subject: [PATCH] Fix Github/Google oAuth --- app/__init__.py | 88 ++----------------- app/models.py | 1 - app/oauth.py | 79 +++++++++++++++++ .../admin_setting_authentication.html | 4 - app/views.py | 41 ++++++--- ...468045_add_view_column_to_setting_table.py | 9 +- 6 files changed, 118 insertions(+), 104 deletions(-) create mode 100644 app/oauth.py diff --git a/app/__init__.py b/app/__init__.py index 326654c..805bf37 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -3,7 +3,7 @@ from flask import Flask, request, session, redirect, url_for from flask_login import LoginManager from flask_sqlalchemy import SQLAlchemy as SA from flask_migrate import Migrate - +from flask_oauthlib.client import OAuth # subclass SQLAlchemy to enable pool_pre_ping class SQLAlchemy(SA): @@ -26,89 +26,13 @@ logging = logger('powerdns-admin', app.config['LOG_LEVEL'], app.config['LOG_FILE login_manager = LoginManager() login_manager.init_app(app) -db = SQLAlchemy(app) -migrate = Migrate(app, db) # used for flask-migrate - -def enable_github_oauth(GITHUB_ENABLE): - if not GITHUB_ENABLE: - return None, None - from flask_oauthlib.client import OAuth - oauth = OAuth(app) - github = oauth.remote_app( - 'github', - consumer_key=app.config['GITHUB_OAUTH_KEY'], - consumer_secret=app.config['GITHUB_OAUTH_SECRET'], - request_token_params={'scope': app.config['GITHUB_OAUTH_SCOPE']}, - base_url=app.config['GITHUB_OAUTH_URL'], # API URL - request_token_url=None, - access_token_method='POST', - access_token_url=app.config['GITHUB_OAUTH_TOKEN'], - authorize_url=app.config['GITHUB_OAUTH_AUTHORIZE'] - ) - - @app.route('/user/authorized') - def authorized(): - session['github_oauthredir'] = url_for('.authorized', _external=True) - resp = github.authorized_response() - if resp is None: - return 'Access denied: reason=%s error=%s' % ( - request.args['error'], - request.args['error_description'] - ) - session['github_token'] = (resp['access_token'], '') - return redirect(url_for('.login')) - - @github.tokengetter - def get_github_oauth_token(): - return session.get('github_token') - - return oauth, github - - -oauth, github = enable_github_oauth(app.config.get('GITHUB_OAUTH_ENABLE')) - - -def enable_google_oauth(GOOGLE_ENABLE): - if not GOOGLE_ENABLE: - return None - from flask_oauthlib.client import OAuth - oauth = OAuth(app) - - google = oauth.remote_app( - 'google', - consumer_key=app.config['GOOGLE_OAUTH_CLIENT_ID'], - consumer_secret=app.config['GOOGLE_OAUTH_CLIENT_SECRET'], - request_token_params=app.config['GOOGLE_TOKEN_PARAMS'], - base_url=app.config['GOOGLE_BASE_URL'], - request_token_url=None, - access_token_method='POST', - access_token_url=app.config['GOOGLE_TOKEN_URL'], - authorize_url=app.config['GOOGLE_AUTHORIZE_URL'], - ) - - @app.route('/user/authorized') - def authorized(): - resp = google.authorized_response() - if resp is None: - return 'Access denied: reason=%s error=%s' % ( - request.args['error_reason'], - request.args['error_description'] - ) - session['google_token'] = (resp['access_token'], '') - return redirect(url_for('.login')) - - @google.tokengetter - def get_google_oauth_token(): - return session.get('google_token') - - return google - - -google = enable_google_oauth(app.config.get('GOOGLE_OAUTH_ENABLE')) - -from app import views, models +db = SQLAlchemy(app) # database +migrate = Migrate(app, db) # flask-migrate +oauth = OAuth(app) # oauth if app.config.get('SAML_ENABLED') and app.config.get('SAML_ENCRYPT'): from app.lib import certutil if not certutil.check_certificate(): certutil.create_self_signed_cert() + +from app import models, views diff --git a/app/models.py b/app/models.py index 41a4a13..9b3ab9b 100644 --- a/app/models.py +++ b/app/models.py @@ -1805,7 +1805,6 @@ class Setting(db.Model): 'google_oauth_enabled': False, 'google_oauth_client_id':'', 'google_oauth_client_secret':'', - 'google_redirect_uri': '/user/authorized', 'google_token_url': 'https://accounts.google.com/o/oauth2/token', 'google_token_params': {'scope': 'email profile'}, 'google_authorize_url':'https://accounts.google.com/o/oauth2/auth', diff --git a/app/oauth.py b/app/oauth.py new file mode 100644 index 0000000..9c01b8b --- /dev/null +++ b/app/oauth.py @@ -0,0 +1,79 @@ +from ast import literal_eval +from flask import request, session, redirect, url_for +from flask_oauthlib.client import OAuth + +from app import app, oauth +from app.models import Setting + +# TODO: +# - Replace Flask-OAuthlib by authlib +# - Fix flask-migrate issue with calling Setting() class during downgrade / upgrade +# - Fix github/google enabling (Currently need to reload the flask app) + +def github_oauth(): + if not Setting().get('github_oauth_enabled'): + return None + + github = oauth.remote_app( + 'github', + consumer_key = Setting().get('github_oauth_key'), + consumer_secret = Setting().get('github_oauth_secret'), + request_token_params = {'scope': Setting().get('github_oauth_scope')}, + base_url = Setting().get('github_oauth_api_url'), + request_token_url = None, + access_token_method = 'POST', + access_token_url = Setting().get('github_oauth_token_url'), + authorize_url = Setting().get('github_oauth_authorize_url') + ) + + @app.route('/github/authorized') + def github_authorized(): + session['github_oauthredir'] = url_for('.github_authorized', _external=True) + resp = github.authorized_response() + if resp is None: + return 'Access denied: reason=%s error=%s' % ( + request.args['error'], + request.args['error_description'] + ) + session['github_token'] = (resp['access_token'], '') + return redirect(url_for('.login')) + + @github.tokengetter + def get_github_oauth_token(): + return session.get('github_token') + + return github + + +def google_oauth(): + if not Setting().get('google_oauth_enabled'): + return None + + google = oauth.remote_app( + 'google', + consumer_key=Setting().get('google_oauth_client_id'), + consumer_secret=Setting().get('google_oauth_client_secret'), + request_token_params=literal_eval(Setting().get('google_token_params')), + base_url=Setting().get('google_base_url'), + request_token_url=None, + access_token_method='POST', + access_token_url=Setting().get('google_token_url'), + authorize_url=Setting().get('google_authorize_url'), + ) + + @app.route('/google/authorized') + def google_authorized(): + resp = google.authorized_response() + if resp is None: + return 'Access denied: reason=%s error=%s' % ( + request.args['error_reason'], + request.args['error_description'] + ) + session['google_token'] = (resp['access_token'], '') + return redirect(url_for('.login')) + + @google.tokengetter + def get_google_oauth_token(): + return session.get('google_token') + + return google diff --git a/app/templates/admin_setting_authentication.html b/app/templates/admin_setting_authentication.html index 32e9b05..34dbe81 100644 --- a/app/templates/admin_setting_authentication.html +++ b/app/templates/admin_setting_authentication.html @@ -159,10 +159,6 @@
ADVANCE -
- - -
diff --git a/app/views.py b/app/views.py index 8066293..335ca27 100644 --- a/app/views.py +++ b/app/views.py @@ -18,8 +18,9 @@ from werkzeug import secure_filename from werkzeug.security import gen_salt from .models import User, Account, Domain, Record, Role, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord -from app import app, login_manager, github, google +from app import app, login_manager from app.lib import utils +from app.oauth import github_oauth, google_oauth from app.decorators import admin_role_required, can_access_domain, can_configure_dnssec if app.config['SAML_ENABLED']: @@ -27,6 +28,8 @@ if app.config['SAML_ENABLED']: from onelogin.saml2.utils import OneLogin_Saml2_Utils logging = logger.getLogger(__name__) +google = google_oauth() +github = github_oauth() # FILTERS app.jinja_env.filters['display_record_name'] = utils.display_record_name @@ -147,16 +150,19 @@ def register(): @app.route('/google/login') def google_login(): - if not app.config.get('GOOGLE_OAUTH_ENABLE'): + if not Setting().get('google_oauth_enabled'): return abort(400) - return google.authorize(callback=url_for('authorized', _external=True)) + else: + return google.authorize(callback=url_for('google_authorized', _external=True)) @app.route('/github/login') def github_login(): - if not app.config.get('GITHUB_OAUTH_ENABLE'): + if not Setting().get('github_oauth_enabled'): return abort(400) - return github.authorize(callback=url_for('authorized', _external=True)) + else: + return github.authorize(callback=url_for('github_authorized', _external=True)) + @app.route('/saml/login') def saml_login(): @@ -167,6 +173,7 @@ def saml_login(): redirect_url=OneLogin_Saml2_Utils.get_self_url(req) + url_for('saml_authorized') return redirect(auth.login(return_to=redirect_url)) + @app.route('/saml/metadata') def saml_metadata(): if not app.config.get('SAML_ENABLED'): @@ -184,6 +191,7 @@ def saml_metadata(): resp = make_response(errors.join(', '), 500) return resp + @app.route('/saml/authorized', methods=['GET', 'POST']) def saml_authorized(): errors = [] @@ -268,6 +276,7 @@ def saml_authorized(): else: return render_template('errors/SAML.html', errors=errors) + @app.route('/login', methods=['GET', 'POST']) @login_manager.unauthorized_handler def login(): @@ -302,14 +311,19 @@ def login(): return redirect(url_for('index')) if 'github_token' in session: - me = github.get('user') - user_info = me.data - user = User.query.filter_by(username=user_info['name']).first() + me = github.get('user').data + + github_username = me['login'] + github_name = me['name'] + github_email = me['email'] + + user = User.query.filter_by(username=github_username).first() if not user: - # create user - user = User(username=user_info['name'], + user = User(username=github_username, plain_text_password=None, - email=user_info['email']) + firstname=github_name, + lastname='', + email=github_email) result = user.create_local_user() if not result['status']: @@ -387,6 +401,7 @@ def login(): except Exception as e: return render_template('register.html', error=e) + def clear_session(): session.pop('user_id', None) session.pop('github_token', None) @@ -394,6 +409,7 @@ def clear_session(): session.clear() logout_user() + @app.route('/logout') def logout(): if app.config.get('SAML_ENABLED') and 'samlSessionIndex' in session and app.config.get('SAML_LOGOUT'): @@ -409,6 +425,7 @@ def logout(): clear_session() return redirect(url_for('login')) + @app.route('/saml/sls') def saml_logout(): req = utils.prepare_flask_request(request) @@ -426,6 +443,7 @@ def saml_logout(): else: return render_template('errors/SAML.html', errors=errors) + @app.route('/dashboard', methods=['GET', 'POST']) @login_required def dashboard(): @@ -1386,7 +1404,6 @@ def admin_setting_authentication(): Setting().set('google_oauth_enabled', True if request.form.get('google_oauth_enabled') else False) Setting().set('google_oauth_client_id', request.form.get('google_oauth_client_id')) Setting().set('google_oauth_client_secret', request.form.get('google_oauth_client_secret')) - Setting().set('google_redirect_uri', request.form.get('google_redirect_uri')) Setting().set('google_token_url', request.form.get('google_token_url')) Setting().set('google_token_params', request.form.get('google_token_params')) Setting().set('google_authorize_url', request.form.get('google_authorize_url')) diff --git a/migrations/versions/59729e468045_add_view_column_to_setting_table.py b/migrations/versions/59729e468045_add_view_column_to_setting_table.py index afb5079..8fe195e 100644 --- a/migrations/versions/59729e468045_add_view_column_to_setting_table.py +++ b/migrations/versions/59729e468045_add_view_column_to_setting_table.py @@ -59,11 +59,10 @@ def update_data(): {'id': 32, 'name': 'google_oauth_enabled', 'value': 'False', 'view': 'authentication'}, {'id': 33, 'name': 'google_oauth_client_id', 'value': '', 'view': 'authentication'}, {'id': 34, 'name': 'google_oauth_client_secret', 'value': '', 'view': 'authentication'}, - {'id': 35, 'name': 'google_redirect_uri', 'value': '/user/authorized', 'view': 'authentication'}, - {'id': 36, 'name': 'google_token_url', 'value': 'https://accounts.google.com/o/oauth2/token', 'view': 'authentication'}, - {'id': 37, 'name': 'google_token_params', 'value': "{'scope': 'email profile'}", 'view': 'authentication'}, - {'id': 38, 'name': 'google_authorize_url', 'value': 'https://accounts.google.com/o/oauth2/auth', 'view': 'authentication'}, - {'id': 39, 'name': 'google_base_url', 'value': 'https://www.googleapis.com/oauth2/v1/', 'view': 'authentication'}, + {'id': 35, 'name': 'google_token_url', 'value': 'https://accounts.google.com/o/oauth2/token', 'view': 'authentication'}, + {'id': 36, 'name': 'google_token_params', 'value': "{'scope': 'email profile'}", 'view': 'authentication'}, + {'id': 37, 'name': 'google_authorize_url', 'value': 'https://accounts.google.com/o/oauth2/auth', 'view': 'authentication'}, + {'id': 38, 'name': 'google_base_url', 'value': 'https://www.googleapis.com/oauth2/v1/', 'view': 'authentication'}, ] )