mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Added support for OAuth 2 authentication. Fixes #5940
Initial patch sent by: Florian Sabonchi
This commit is contained in:
committed by
Akshay Joshi
parent
fff4060b31
commit
48ca83f31d
@@ -9,68 +9,33 @@
|
||||
|
||||
"""A blueprint module implementing the Authentication."""
|
||||
|
||||
import flask
|
||||
import pickle
|
||||
from flask import current_app, flash, Response, request, url_for,\
|
||||
render_template
|
||||
from flask_babelex import gettext
|
||||
from flask_security import current_user, login_required
|
||||
from flask_security.views import _security, _ctx
|
||||
from flask_security.utils import config_value, get_post_logout_redirect, \
|
||||
get_post_login_redirect, logout_user
|
||||
from pgadmin.utils.ajax import make_json_response, internal_server_error
|
||||
import os
|
||||
|
||||
from flask import session
|
||||
|
||||
import config
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.constants import KERBEROS, INTERNAL
|
||||
from pgadmin.utils.csrf import pgCSRFProtect
|
||||
import copy
|
||||
|
||||
from flask import current_app, flash, Response, request, url_for,\
|
||||
session, redirect
|
||||
from flask_babelex import gettext
|
||||
from flask_security.views import _security
|
||||
from flask_security.utils import get_post_logout_redirect, \
|
||||
get_post_login_redirect
|
||||
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.constants import KERBEROS, INTERNAL, OAUTH2, LDAP
|
||||
from pgadmin.authenticate.registry import AuthSourceRegistry
|
||||
|
||||
from .registry import AuthSourceRegistry
|
||||
|
||||
MODULE_NAME = 'authenticate'
|
||||
auth_obj = None
|
||||
|
||||
|
||||
class AuthenticateModule(PgAdminModule):
|
||||
def get_exposed_url_endpoints(self):
|
||||
return ['authenticate.login',
|
||||
'authenticate.kerberos_login',
|
||||
'authenticate.kerberos_logout',
|
||||
'authenticate.kerberos_update_ticket',
|
||||
'authenticate.kerberos_validate_ticket']
|
||||
return ['authenticate.login']
|
||||
|
||||
|
||||
blueprint = AuthenticateModule(MODULE_NAME, __name__, static_url_path='')
|
||||
|
||||
|
||||
@blueprint.route("/login/kerberos",
|
||||
endpoint="kerberos_login", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
def kerberos_login():
|
||||
logout_user()
|
||||
return Response(render_template("browser/kerberos_login.html",
|
||||
login_url=url_for('security.login'),
|
||||
))
|
||||
|
||||
|
||||
@blueprint.route("/logout/kerberos",
|
||||
endpoint="kerberos_logout", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
def kerberos_logout():
|
||||
logout_user()
|
||||
if 'KRB5CCNAME' in session:
|
||||
# Remove the credential cache
|
||||
cache_file_path = session['KRB5CCNAME'].split(":")[1]
|
||||
if os.path.exists(cache_file_path):
|
||||
os.remove(cache_file_path)
|
||||
|
||||
return Response(render_template("browser/kerberos_logout.html",
|
||||
login_url=url_for('security.login'),
|
||||
))
|
||||
|
||||
|
||||
@blueprint.route('/login', endpoint='login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
"""
|
||||
@@ -78,15 +43,20 @@ def login():
|
||||
The user input will be validated and authenticated.
|
||||
"""
|
||||
form = _security.login_form()
|
||||
auth_obj = AuthSourceManager(form, config.AUTHENTICATION_SOURCES)
|
||||
session['_auth_source_manager_obj'] = None
|
||||
|
||||
auth_obj = AuthSourceManager(form, copy.deepcopy(
|
||||
config.AUTHENTICATION_SOURCES))
|
||||
if OAUTH2 in config.AUTHENTICATION_SOURCES\
|
||||
and 'oauth2_button' in request.form:
|
||||
session['auth_obj'] = auth_obj
|
||||
|
||||
session['auth_source_manager'] = None
|
||||
# Validate the user
|
||||
if not auth_obj.validate():
|
||||
for field in form.errors:
|
||||
for error in form.errors[field]:
|
||||
flash(error, 'warning')
|
||||
return flask.redirect(get_post_logout_redirect())
|
||||
return redirect(get_post_logout_redirect())
|
||||
|
||||
# Authenticate the user
|
||||
status, msg = auth_obj.authenticate()
|
||||
@@ -94,34 +64,40 @@ def login():
|
||||
# Login the user
|
||||
status, msg = auth_obj.login()
|
||||
current_auth_obj = auth_obj.as_dict()
|
||||
|
||||
if not status:
|
||||
if current_auth_obj['current_source'] ==\
|
||||
KERBEROS:
|
||||
return flask.redirect('{0}?next={1}'.format(url_for(
|
||||
return redirect('{0}?next={1}'.format(url_for(
|
||||
'authenticate.kerberos_login'), url_for('browser.index')))
|
||||
|
||||
flash(msg, 'danger')
|
||||
return flask.redirect(get_post_logout_redirect())
|
||||
|
||||
session['_auth_source_manager_obj'] = current_auth_obj
|
||||
return flask.redirect(get_post_login_redirect())
|
||||
return redirect(get_post_logout_redirect())
|
||||
session['auth_source_manager'] = current_auth_obj
|
||||
if 'auth_obj' in session:
|
||||
session['auth_obj'] = None
|
||||
return redirect(get_post_login_redirect())
|
||||
|
||||
elif isinstance(msg, Response):
|
||||
return msg
|
||||
elif 'oauth2_button' in request.form and not isinstance(msg, str):
|
||||
return msg
|
||||
flash(msg, 'danger')
|
||||
response = flask.redirect(get_post_logout_redirect())
|
||||
response = redirect(get_post_logout_redirect())
|
||||
return response
|
||||
|
||||
|
||||
class AuthSourceManager():
|
||||
class AuthSourceManager:
|
||||
"""This class will manage all the authentication sources.
|
||||
"""
|
||||
|
||||
def __init__(self, form, sources):
|
||||
self.form = form
|
||||
self.auth_sources = sources
|
||||
self.source = None
|
||||
self.source_friendly_name = INTERNAL
|
||||
self.current_source = None
|
||||
self.current_source = INTERNAL
|
||||
self.update_auth_sources()
|
||||
|
||||
def as_dict(self):
|
||||
"""
|
||||
@@ -135,6 +111,14 @@ class AuthSourceManager():
|
||||
|
||||
return res
|
||||
|
||||
def update_auth_sources(self):
|
||||
for auth_src in [KERBEROS, OAUTH2]:
|
||||
if auth_src in self.auth_sources:
|
||||
if 'internal_button' in request.form:
|
||||
self.auth_sources.remove(auth_src)
|
||||
elif INTERNAL in self.auth_sources:
|
||||
self.auth_sources.remove(INTERNAL)
|
||||
|
||||
def set_current_source(self, source):
|
||||
self.current_source = source
|
||||
|
||||
@@ -170,36 +154,19 @@ class AuthSourceManager():
|
||||
msg = None
|
||||
for src in self.auth_sources:
|
||||
source = get_auth_sources(src)
|
||||
self.set_source(source)
|
||||
current_app.logger.debug(
|
||||
"Authentication initiated via source: %s" %
|
||||
source.get_source_name())
|
||||
|
||||
if self.form.data['email'] and self.form.data['password'] and \
|
||||
source.get_source_name() == KERBEROS:
|
||||
msg = gettext('pgAdmin internal user authentication'
|
||||
' is not enabled, please contact administrator.')
|
||||
continue
|
||||
|
||||
status, msg = source.authenticate(self.form)
|
||||
|
||||
# When server sends Unauthorized header to get the ticket over HTTP
|
||||
# OR When kerberos authentication failed while accessing pgadmin,
|
||||
# we need to break the loop as no need to authenticate further
|
||||
# even if the authentication sources set to multiple
|
||||
if not status:
|
||||
if (hasattr(msg, 'status') and
|
||||
msg.status == '401 UNAUTHORIZED') or\
|
||||
(source.get_source_name() ==
|
||||
KERBEROS and
|
||||
request.method == 'GET'):
|
||||
break
|
||||
|
||||
if status:
|
||||
self.set_source(source)
|
||||
self.set_current_source(source.get_source_name())
|
||||
if msg is not None and 'username' in msg:
|
||||
self.form._fields['email'].data = msg['username']
|
||||
return status, msg
|
||||
|
||||
return status, msg
|
||||
|
||||
def login(self):
|
||||
@@ -209,6 +176,13 @@ class AuthSourceManager():
|
||||
current_app.logger.debug(
|
||||
"Authentication and Login successfully done via source : %s" %
|
||||
self.source.get_source_name())
|
||||
|
||||
# Set the login, logout view as per source if available
|
||||
current_app.login_manager.login_view = getattr(
|
||||
self.source, 'LOGIN_VIEW', 'security.login')
|
||||
current_app.login_manager.logout_view = getattr(
|
||||
self.source, 'LOGOUT_VIEW', 'security.logout')
|
||||
|
||||
return status, msg
|
||||
|
||||
|
||||
@@ -239,58 +213,3 @@ def init_app(app):
|
||||
AuthSourceRegistry.load_modules(app)
|
||||
|
||||
return auth_sources
|
||||
|
||||
|
||||
@blueprint.route("/kerberos/update_ticket",
|
||||
endpoint="kerberos_update_ticket", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
def kerberos_update_ticket():
|
||||
"""
|
||||
Update the kerberos ticket.
|
||||
"""
|
||||
from werkzeug.datastructures import Headers
|
||||
headers = Headers()
|
||||
|
||||
authorization = request.headers.get("Authorization", None)
|
||||
|
||||
if authorization is None:
|
||||
# Send the Negotiate header to the client
|
||||
# if Kerberos ticket is not found.
|
||||
headers.add('WWW-Authenticate', 'Negotiate')
|
||||
return Response("Unauthorised", 401, headers)
|
||||
else:
|
||||
source = get_auth_sources(KERBEROS)
|
||||
auth_header = authorization.split()
|
||||
in_token = auth_header[1]
|
||||
|
||||
# Validate the Kerberos ticket
|
||||
status, context = source.negotiate_start(in_token)
|
||||
if status:
|
||||
return Response("Ticket updated successfully.")
|
||||
|
||||
return Response(context, 500)
|
||||
|
||||
|
||||
@blueprint.route("/kerberos/validate_ticket",
|
||||
endpoint="kerberos_validate_ticket", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
def kerberos_validate_ticket():
|
||||
"""
|
||||
Return the kerberos ticket lifetime left after getting the
|
||||
ticket from the credential cache
|
||||
"""
|
||||
import gssapi
|
||||
|
||||
try:
|
||||
del_creds = gssapi.Credentials(store={'ccache': session['KRB5CCNAME']})
|
||||
creds = del_creds.acquire(store={'ccache': session['KRB5CCNAME']})
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
return make_json_response(
|
||||
data={'ticket_lifetime': creds.lifetime},
|
||||
status=200
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"""Implements Internal Authentication"""
|
||||
|
||||
import six
|
||||
from flask import current_app
|
||||
from flask import current_app, flash
|
||||
from flask_security import login_user
|
||||
from abc import abstractmethod, abstractproperty
|
||||
from flask_babelex import gettext
|
||||
@@ -31,6 +31,8 @@ class BaseAuthentication(object):
|
||||
'PASSWORD_NOT_PROVIDED': gettext('Password not provided'),
|
||||
'INVALID_EMAIL': gettext('Email/Username is not valid')
|
||||
}
|
||||
LOGIN_VIEW = 'security.login'
|
||||
LOGOUT_VIEW = 'security.logout'
|
||||
|
||||
@abstractmethod
|
||||
def get_source_name(self):
|
||||
@@ -97,7 +99,7 @@ class InternalAuthentication(BaseAuthentication):
|
||||
"""User validation"""
|
||||
# validate the email id first
|
||||
if not validate_email(form.data['email']):
|
||||
form.errors['email'] = [self.messages('INVALID_EMAIL')]
|
||||
flash(self.messages('INVALID_EMAIL'), 'warning')
|
||||
return False
|
||||
# Flask security validation
|
||||
return form.validate_on_submit()
|
||||
|
||||
@@ -10,22 +10,28 @@
|
||||
"""A blueprint module implementing the Spnego/Kerberos authentication."""
|
||||
|
||||
import base64
|
||||
from os import environ, path
|
||||
from os import environ, path, remove
|
||||
|
||||
from werkzeug.datastructures import Headers
|
||||
from werkzeug.datastructures import Headers, MultiDict
|
||||
from flask_babelex import gettext
|
||||
from flask import Flask, request, Response, session,\
|
||||
current_app, render_template, flash
|
||||
from flask import request, Response, session,\
|
||||
current_app, render_template, flash, url_for
|
||||
from flask_security.views import _security
|
||||
from flask_security.utils import logout_user
|
||||
from flask_security import login_required
|
||||
|
||||
import config
|
||||
from pgadmin.model import User
|
||||
from pgadmin.tools.user_management import create_user
|
||||
from pgadmin.utils.constants import KERBEROS
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.ajax import make_json_response, internal_server_error
|
||||
|
||||
from flask_security.views import _security
|
||||
from werkzeug.datastructures import MultiDict
|
||||
|
||||
from .internal import BaseAuthentication
|
||||
from pgadmin.authenticate.internal import BaseAuthentication
|
||||
from pgadmin.authenticate import get_auth_sources
|
||||
from pgadmin.utils.csrf import pgCSRFProtect
|
||||
|
||||
|
||||
try:
|
||||
import gssapi
|
||||
@@ -46,8 +52,110 @@ if config.KRB_KTNAME and config.KRB_KTNAME != '<KRB5_KEYTAB_FILE>':
|
||||
environ['KRB5_KTNAME'] = config.KRB_KTNAME
|
||||
|
||||
|
||||
class KerberosModule(PgAdminModule):
|
||||
def register(self, app, options, first_registration=False):
|
||||
# Do not look for the sub_modules,
|
||||
# instead call blueprint.register(...) directly
|
||||
super(PgAdminModule, self).register(app, options, first_registration)
|
||||
|
||||
def get_exposed_url_endpoints(self):
|
||||
return ['kerberos.login',
|
||||
'kerberos.logout',
|
||||
'kerberos.update_ticket',
|
||||
'kerberos.validate_ticket']
|
||||
|
||||
|
||||
def init_app(app):
|
||||
MODULE_NAME = 'kerberos'
|
||||
|
||||
blueprint = KerberosModule(MODULE_NAME, __name__, static_url_path='')
|
||||
|
||||
@blueprint.route("/login",
|
||||
endpoint="login", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
def kerberos_login():
|
||||
logout_user()
|
||||
return Response(render_template("browser/kerberos_login.html",
|
||||
login_url=url_for('security.login'),
|
||||
))
|
||||
|
||||
@blueprint.route("/logout",
|
||||
endpoint="logout", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
def kerberos_logout():
|
||||
logout_user()
|
||||
if 'KRB5CCNAME' in session:
|
||||
# Remove the credential cache
|
||||
cache_file_path = session['KRB5CCNAME'].split(":")[1]
|
||||
if path.exists(cache_file_path):
|
||||
remove(cache_file_path)
|
||||
|
||||
return Response(render_template("browser/kerberos_logout.html",
|
||||
login_url=url_for('security.login'),
|
||||
))
|
||||
|
||||
@blueprint.route("/update_ticket",
|
||||
endpoint="update_ticket", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
def kerberos_update_ticket():
|
||||
"""
|
||||
Update the kerberos ticket.
|
||||
"""
|
||||
from werkzeug.datastructures import Headers
|
||||
headers = Headers()
|
||||
|
||||
authorization = request.headers.get("Authorization", None)
|
||||
|
||||
if authorization is None:
|
||||
# Send the Negotiate header to the client
|
||||
# if Kerberos ticket is not found.
|
||||
headers.add('WWW-Authenticate', 'Negotiate')
|
||||
return Response("Unauthorised", 401, headers)
|
||||
else:
|
||||
source = get_auth_sources(KERBEROS)
|
||||
auth_header = authorization.split()
|
||||
in_token = auth_header[1]
|
||||
|
||||
# Validate the Kerberos ticket
|
||||
status, context = source.negotiate_start(in_token)
|
||||
if status:
|
||||
return Response("Ticket updated successfully.")
|
||||
|
||||
return Response(context, 500)
|
||||
|
||||
@blueprint.route("/validate_ticket",
|
||||
endpoint="validate_ticket", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
def kerberos_validate_ticket():
|
||||
"""
|
||||
Return the kerberos ticket lifetime left after getting the
|
||||
ticket from the credential cache
|
||||
"""
|
||||
import gssapi
|
||||
|
||||
try:
|
||||
del_creds = gssapi.Credentials(store={
|
||||
'ccache': session['KRB5CCNAME']})
|
||||
creds = del_creds.acquire(store={'ccache': session['KRB5CCNAME']})
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
return make_json_response(
|
||||
data={'ticket_lifetime': creds.lifetime},
|
||||
status=200
|
||||
)
|
||||
|
||||
app.register_blueprint(blueprint)
|
||||
|
||||
|
||||
class KerberosAuthentication(BaseAuthentication):
|
||||
|
||||
LOGIN_VIEW = 'kerberos.login'
|
||||
LOGOUT_VIEW = 'kerberos.logout'
|
||||
|
||||
def get_source_name(self):
|
||||
return KERBEROS
|
||||
|
||||
@@ -85,7 +193,7 @@ class KerberosAuthentication(BaseAuthentication):
|
||||
if status:
|
||||
# Saving the first 15 characters of the kerberos key
|
||||
# to encrypt/decrypt database password
|
||||
session['kerberos_key'] = auth_header[1][0:15]
|
||||
session['pass_enc_key'] = auth_header[1][0:15]
|
||||
# Create user
|
||||
retval = self.__auto_create_user(
|
||||
str(negotiate.initiator_name))
|
||||
@@ -162,7 +270,7 @@ class KerberosAuthentication(BaseAuthentication):
|
||||
username = str(username)
|
||||
if config.KRB_AUTO_CREATE_USER:
|
||||
user = User.query.filter_by(
|
||||
username=username).first()
|
||||
username=username, auth_source=KERBEROS).first()
|
||||
if user is None:
|
||||
return create_user({
|
||||
'username': username,
|
||||
|
||||
172
web/pgadmin/authenticate/oauth2.py
Normal file
172
web/pgadmin/authenticate/oauth2.py
Normal file
@@ -0,0 +1,172 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""A blueprint module implementing the Oauth2 authentication."""
|
||||
|
||||
import config
|
||||
|
||||
from authlib.integrations.flask_client import OAuth
|
||||
from flask import current_app, url_for, session, request,\
|
||||
redirect, Flask, flash
|
||||
from flask_babelex import gettext
|
||||
from flask_security import login_user, current_user
|
||||
from flask_security.utils import get_post_logout_redirect, \
|
||||
get_post_login_redirect, logout_user
|
||||
|
||||
from pgadmin.authenticate.internal import BaseAuthentication
|
||||
from pgadmin.model import User
|
||||
from pgadmin.tools.user_management import create_user
|
||||
from pgadmin.utils.constants import OAUTH2
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.csrf import pgCSRFProtect
|
||||
from pgadmin.model import db
|
||||
|
||||
OAUTH2_LOGOUT = 'oauth2.logout'
|
||||
OAUTH2_AUTHORIZE = 'oauth2.authorize'
|
||||
|
||||
|
||||
class Oauth2Module(PgAdminModule):
|
||||
def register(self, app, options, first_registration=False):
|
||||
# Do not look for the sub_modules,
|
||||
# instead call blueprint.register(...) directly
|
||||
super(PgAdminModule, self).register(app, options, first_registration)
|
||||
|
||||
def get_exposed_url_endpoints(self):
|
||||
return [OAUTH2_AUTHORIZE,
|
||||
OAUTH2_LOGOUT]
|
||||
|
||||
|
||||
def init_app(app):
|
||||
MODULE_NAME = 'oauth2'
|
||||
|
||||
blueprint = Oauth2Module(MODULE_NAME, __name__, static_url_path='')
|
||||
|
||||
@blueprint.route('/authorize', endpoint="authorize",
|
||||
methods=['GET', 'POST'])
|
||||
@pgCSRFProtect.exempt
|
||||
def oauth_authorize():
|
||||
auth_obj = session['auth_obj']
|
||||
auth_obj.set_current_source(auth_obj.source.get_source_name())
|
||||
status, msg = auth_obj.login()
|
||||
if status:
|
||||
session['auth_source_manager'] = auth_obj.as_dict()
|
||||
session['auth_obj'] = None
|
||||
return redirect(get_post_login_redirect())
|
||||
logout_user()
|
||||
flash(msg)
|
||||
return redirect(get_post_login_redirect())
|
||||
|
||||
@blueprint.route('/logout', endpoint="logout",
|
||||
methods=['GET', 'POST'])
|
||||
@pgCSRFProtect.exempt
|
||||
def oauth_logout():
|
||||
if not current_user.is_authenticated:
|
||||
return redirect(get_post_logout_redirect())
|
||||
for key in list(session.keys()):
|
||||
session.pop(key)
|
||||
logout_user()
|
||||
return redirect(get_post_logout_redirect())
|
||||
|
||||
app.register_blueprint(blueprint)
|
||||
app.login_manager.logout_view = OAUTH2_LOGOUT
|
||||
|
||||
|
||||
class OAuth2Authentication(BaseAuthentication):
|
||||
"""OAuth Authentication Class"""
|
||||
|
||||
LOGOUT_VIEW = OAUTH2_LOGOUT
|
||||
|
||||
oauth_obj = OAuth(Flask(__name__))
|
||||
oauth2_clients = {}
|
||||
oauth2_config = {}
|
||||
|
||||
def __init__(self):
|
||||
for oauth2_config in config.OAUTH2_CONFIG:
|
||||
|
||||
OAuth2Authentication.oauth2_config[
|
||||
oauth2_config['OAUTH2_NAME']] = oauth2_config
|
||||
|
||||
OAuth2Authentication.oauth2_clients[
|
||||
oauth2_config['OAUTH2_NAME']
|
||||
] = OAuth2Authentication.oauth_obj.register(
|
||||
name=oauth2_config['OAUTH2_NAME'],
|
||||
client_id=oauth2_config['OAUTH2_CLIENT_ID'],
|
||||
client_secret=oauth2_config['OAUTH2_CLIENT_SECRET'],
|
||||
access_token_url=oauth2_config['OAUTH2_TOKEN_URL'],
|
||||
authorize_url=oauth2_config['OAUTH2_AUTHORIZATION_URL'],
|
||||
api_base_url=oauth2_config['OAUTH2_API_BASE_URL'],
|
||||
client_kwargs={'scope': 'email profile'}
|
||||
)
|
||||
|
||||
def get_source_name(self):
|
||||
return OAUTH2
|
||||
|
||||
def get_friendly_name(self):
|
||||
return self.oauth2_config[self.oauth2_current_client]['OAUTH2_NAME']
|
||||
|
||||
def validate(self, form):
|
||||
return True
|
||||
|
||||
def login(self, form):
|
||||
profile = self.get_user_profile()
|
||||
print(profile)
|
||||
if 'email' not in profile or not profile['email']:
|
||||
current_app.logger.exception(
|
||||
'An email is required for authentication'
|
||||
)
|
||||
return False, gettext(
|
||||
"An email is required for the oauth authentication.")
|
||||
|
||||
user, msg = self.__auto_create_user(profile)
|
||||
if user:
|
||||
user = db.session.query(User).filter_by(
|
||||
username=profile['email'], auth_source=OAUTH2).first()
|
||||
current_app.login_manager.logout_view = \
|
||||
OAuth2Authentication.LOGOUT_VIEW
|
||||
return login_user(user), None
|
||||
return False, msg
|
||||
|
||||
def get_user_profile(self):
|
||||
session['oauth2_token'] = self.oauth2_clients[
|
||||
self.oauth2_current_client].authorize_access_token()
|
||||
|
||||
session['pass_enc_key'] = session['oauth2_token']['access_token']
|
||||
|
||||
resp = self.oauth2_clients[self.oauth2_current_client].get(
|
||||
self.oauth2_config[
|
||||
self.oauth2_current_client]['OAUTH2_USERINFO_ENDPOINT'],
|
||||
token=session['oauth2_token']
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
def authenticate(self, form):
|
||||
self.oauth2_current_client = request.form['oauth2_button']
|
||||
redirect_url = url_for(OAUTH2_AUTHORIZE, _external=True)
|
||||
|
||||
if self.oauth2_current_client not in self.oauth2_clients:
|
||||
return False, gettext(
|
||||
"Please set the configuration parameters properly.")
|
||||
return False, self.oauth2_clients[
|
||||
self.oauth2_current_client].authorize_redirect(redirect_url)
|
||||
|
||||
def __auto_create_user(self, resp):
|
||||
if config.OAUTH2_AUTO_CREATE_USER:
|
||||
user = User.query.filter_by(username=resp['email'],
|
||||
auth_source=OAUTH2).first()
|
||||
if not user:
|
||||
return create_user({
|
||||
'username': resp['email'],
|
||||
'email': resp['email'],
|
||||
'role': 2,
|
||||
'active': True,
|
||||
'auth_source': OAUTH2
|
||||
})
|
||||
|
||||
return True, {'username': resp['email']}
|
||||
@@ -1,10 +1,19 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import url_for from 'sources/url_for';
|
||||
import userInfo from 'pgadmin.user_management.current_user';
|
||||
import pgConst from 'pgadmin.browser.constants';
|
||||
|
||||
function fetch_ticket() {
|
||||
// Fetch the Kerberos Updated ticket through SPNEGO
|
||||
return fetch(url_for('authenticate.kerberos_update_ticket')
|
||||
return fetch(url_for('kerberos.update_ticket')
|
||||
)
|
||||
.then(function(response){
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
@@ -18,7 +27,7 @@ function fetch_ticket() {
|
||||
function fetch_ticket_lifetime () {
|
||||
// Fetch the Kerberos ticket lifetime left
|
||||
|
||||
return fetch(url_for('authenticate.kerberos_validate_ticket')
|
||||
return fetch(url_for('kerberos.validate_ticket')
|
||||
)
|
||||
.then(
|
||||
function(response){
|
||||
|
||||
Reference in New Issue
Block a user