Fix Github/Google oAuth

This commit is contained in:
Khanh Ngo 2018-08-19 15:29:50 +07:00
parent ada6f844ff
commit babf62bae0
No known key found for this signature in database
GPG Key ID: B9AE3BAF6D5A7B22
6 changed files with 118 additions and 104 deletions

View File

@ -3,7 +3,7 @@ from flask import Flask, request, session, redirect, url_for
from flask_login import LoginManager from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy as SA from flask_sqlalchemy import SQLAlchemy as SA
from flask_migrate import Migrate from flask_migrate import Migrate
from flask_oauthlib.client import OAuth
# subclass SQLAlchemy to enable pool_pre_ping # subclass SQLAlchemy to enable pool_pre_ping
class SQLAlchemy(SA): class SQLAlchemy(SA):
@ -26,89 +26,13 @@ logging = logger('powerdns-admin', app.config['LOG_LEVEL'], app.config['LOG_FILE
login_manager = LoginManager() login_manager = LoginManager()
login_manager.init_app(app) login_manager.init_app(app)
db = SQLAlchemy(app) db = SQLAlchemy(app) # database
migrate = Migrate(app, db) # used for flask-migrate migrate = Migrate(app, db) # flask-migrate
oauth = OAuth(app) # oauth
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
if app.config.get('SAML_ENABLED') and app.config.get('SAML_ENCRYPT'): if app.config.get('SAML_ENABLED') and app.config.get('SAML_ENCRYPT'):
from app.lib import certutil from app.lib import certutil
if not certutil.check_certificate(): if not certutil.check_certificate():
certutil.create_self_signed_cert() certutil.create_self_signed_cert()
from app import models, views

View File

@ -1805,7 +1805,6 @@ class Setting(db.Model):
'google_oauth_enabled': False, 'google_oauth_enabled': False,
'google_oauth_client_id':'', 'google_oauth_client_id':'',
'google_oauth_client_secret':'', 'google_oauth_client_secret':'',
'google_redirect_uri': '/user/authorized',
'google_token_url': 'https://accounts.google.com/o/oauth2/token', 'google_token_url': 'https://accounts.google.com/o/oauth2/token',
'google_token_params': {'scope': 'email profile'}, 'google_token_params': {'scope': 'email profile'},
'google_authorize_url':'https://accounts.google.com/o/oauth2/auth', 'google_authorize_url':'https://accounts.google.com/o/oauth2/auth',

79
app/oauth.py Normal file
View File

@ -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

View File

@ -159,10 +159,6 @@
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>ADVANCE</legend> <legend>ADVANCE</legend>
<div class="form-group">
<label for="google_redirect_uri">Redirect URI</label>
<input type="text" class="form-control" name="google_redirect_uri" id="google_redirect_uri" placeholder="e.g. /user/authorized" value="{{ SETTING.get('google_redirect_uri') }}">
</div>
<div class="form-group"> <div class="form-group">
<label for="google_token_url">Token URL</label> <label for="google_token_url">Token URL</label>
<input type="text" class="form-control" name="google_token_url" id="google_token_url" placeholder="e.g. https://accounts.google.com/o/oauth2/token" value="{{ SETTING.get('google_token_url') }}"> <input type="text" class="form-control" name="google_token_url" id="google_token_url" placeholder="e.g. https://accounts.google.com/o/oauth2/token" value="{{ SETTING.get('google_token_url') }}">

View File

@ -18,8 +18,9 @@ from werkzeug import secure_filename
from werkzeug.security import gen_salt from werkzeug.security import gen_salt
from .models import User, Account, Domain, Record, Role, Server, History, Anonymous, Setting, DomainSetting, DomainTemplate, DomainTemplateRecord 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.lib import utils
from app.oauth import github_oauth, google_oauth
from app.decorators import admin_role_required, can_access_domain, can_configure_dnssec from app.decorators import admin_role_required, can_access_domain, can_configure_dnssec
if app.config['SAML_ENABLED']: if app.config['SAML_ENABLED']:
@ -27,6 +28,8 @@ if app.config['SAML_ENABLED']:
from onelogin.saml2.utils import OneLogin_Saml2_Utils from onelogin.saml2.utils import OneLogin_Saml2_Utils
logging = logger.getLogger(__name__) logging = logger.getLogger(__name__)
google = google_oauth()
github = github_oauth()
# FILTERS # FILTERS
app.jinja_env.filters['display_record_name'] = utils.display_record_name app.jinja_env.filters['display_record_name'] = utils.display_record_name
@ -147,16 +150,19 @@ def register():
@app.route('/google/login') @app.route('/google/login')
def google_login(): def google_login():
if not app.config.get('GOOGLE_OAUTH_ENABLE'): if not Setting().get('google_oauth_enabled'):
return abort(400) 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') @app.route('/github/login')
def github_login(): def github_login():
if not app.config.get('GITHUB_OAUTH_ENABLE'): if not Setting().get('github_oauth_enabled'):
return abort(400) 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') @app.route('/saml/login')
def 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') redirect_url=OneLogin_Saml2_Utils.get_self_url(req) + url_for('saml_authorized')
return redirect(auth.login(return_to=redirect_url)) return redirect(auth.login(return_to=redirect_url))
@app.route('/saml/metadata') @app.route('/saml/metadata')
def saml_metadata(): def saml_metadata():
if not app.config.get('SAML_ENABLED'): if not app.config.get('SAML_ENABLED'):
@ -184,6 +191,7 @@ def saml_metadata():
resp = make_response(errors.join(', '), 500) resp = make_response(errors.join(', '), 500)
return resp return resp
@app.route('/saml/authorized', methods=['GET', 'POST']) @app.route('/saml/authorized', methods=['GET', 'POST'])
def saml_authorized(): def saml_authorized():
errors = [] errors = []
@ -268,6 +276,7 @@ def saml_authorized():
else: else:
return render_template('errors/SAML.html', errors=errors) return render_template('errors/SAML.html', errors=errors)
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
@login_manager.unauthorized_handler @login_manager.unauthorized_handler
def login(): def login():
@ -302,14 +311,19 @@ def login():
return redirect(url_for('index')) return redirect(url_for('index'))
if 'github_token' in session: if 'github_token' in session:
me = github.get('user') me = github.get('user').data
user_info = me.data
user = User.query.filter_by(username=user_info['name']).first() github_username = me['login']
github_name = me['name']
github_email = me['email']
user = User.query.filter_by(username=github_username).first()
if not user: if not user:
# create user user = User(username=github_username,
user = User(username=user_info['name'],
plain_text_password=None, plain_text_password=None,
email=user_info['email']) firstname=github_name,
lastname='',
email=github_email)
result = user.create_local_user() result = user.create_local_user()
if not result['status']: if not result['status']:
@ -387,6 +401,7 @@ def login():
except Exception as e: except Exception as e:
return render_template('register.html', error=e) return render_template('register.html', error=e)
def clear_session(): def clear_session():
session.pop('user_id', None) session.pop('user_id', None)
session.pop('github_token', None) session.pop('github_token', None)
@ -394,6 +409,7 @@ def clear_session():
session.clear() session.clear()
logout_user() logout_user()
@app.route('/logout') @app.route('/logout')
def logout(): def logout():
if app.config.get('SAML_ENABLED') and 'samlSessionIndex' in session and app.config.get('SAML_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() clear_session()
return redirect(url_for('login')) return redirect(url_for('login'))
@app.route('/saml/sls') @app.route('/saml/sls')
def saml_logout(): def saml_logout():
req = utils.prepare_flask_request(request) req = utils.prepare_flask_request(request)
@ -426,6 +443,7 @@ def saml_logout():
else: else:
return render_template('errors/SAML.html', errors=errors) return render_template('errors/SAML.html', errors=errors)
@app.route('/dashboard', methods=['GET', 'POST']) @app.route('/dashboard', methods=['GET', 'POST'])
@login_required @login_required
def dashboard(): 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_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_id', request.form.get('google_oauth_client_id'))
Setting().set('google_oauth_client_secret', request.form.get('google_oauth_client_secret')) 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_url', request.form.get('google_token_url'))
Setting().set('google_token_params', request.form.get('google_token_params')) Setting().set('google_token_params', request.form.get('google_token_params'))
Setting().set('google_authorize_url', request.form.get('google_authorize_url')) Setting().set('google_authorize_url', request.form.get('google_authorize_url'))

View File

@ -59,11 +59,10 @@ def update_data():
{'id': 32, 'name': 'google_oauth_enabled', 'value': 'False', 'view': 'authentication'}, {'id': 32, 'name': 'google_oauth_enabled', 'value': 'False', 'view': 'authentication'},
{'id': 33, 'name': 'google_oauth_client_id', 'value': '', 'view': 'authentication'}, {'id': 33, 'name': 'google_oauth_client_id', 'value': '', 'view': 'authentication'},
{'id': 34, 'name': 'google_oauth_client_secret', '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': 35, 'name': 'google_token_url', 'value': 'https://accounts.google.com/o/oauth2/token', 'view': 'authentication'},
{'id': 36, '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_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_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'},
{'id': 39, 'name': 'google_base_url', 'value': 'https://www.googleapis.com/oauth2/v1/', 'view': 'authentication'},
] ]
) )