Add the possibility to configure the OAuth2 claim which is used for the pgAdmin username. #5468

This feature provides the possibility to configure the Oauth2 claim
which should be used as a username. The key in the config.py is called
'OAUTH2_USERNAME_CLAIM'. If you don't provide a custom key, the email
is used as the username, like before. So it is completely backward
compatible.
pull/5556/head
Leon Maraite 2022-11-07 09:28:23 +01:00 committed by GitHub
parent c38face9fc
commit 6bc5808c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 8 deletions

View File

@ -33,6 +33,8 @@ and modify the values for the following parameters:
"OAUTH2_SCOPE", "Oauth scope, ex: 'openid email profile'. Note that an 'email' claim is required in the resulting profile." "OAUTH2_SCOPE", "Oauth scope, ex: 'openid email profile'. Note that an 'email' claim is required in the resulting profile."
"OAUTH2_ICON", "The Font-awesome icon to be placed on the oauth2 button, ex: fa-github" "OAUTH2_ICON", "The Font-awesome icon to be placed on the oauth2 button, ex: fa-github"
"OAUTH2_BUTTON_COLOR", "Oauth2 button color" "OAUTH2_BUTTON_COLOR", "Oauth2 button color"
"OAUTH2_USERNAME_CLAIM", "The claim which is used for the username. If the value is empty
the email is used as username, but if a value is provided, the claim has to exist. Ex: *oid* (for AzureAD)"
"OAUTH2_AUTO_CREATE_USER", "Set the value to *True* if you want to automatically "OAUTH2_AUTO_CREATE_USER", "Set the value to *True* if you want to automatically
create a pgAdmin user corresponding to a successfully authenticated Oauth2 user. create a pgAdmin user corresponding to a successfully authenticated Oauth2 user.
Please note that password is not stored in the pgAdmin database." Please note that password is not stored in the pgAdmin database."

View File

@ -16,7 +16,8 @@ New features
************ ************
| `Issue #1832 <https://github.com/pgadmin-org/pgadmin4/issues/1832>`_ - Added support for storing configurations of pgAdmin in an external database. | `Issue #1832 <https://github.com/pgadmin-org/pgadmin4/issues/1832>`_ - Added support for storing configurations of pgAdmin in an external database.
| `Issue #5468 <https://github.com/pgadmin-org/pgadmin4/issues/5468>`_ - Add the possibility to configure the Oauth2 claim which is used for the pgAdmin username.
Housekeeping Housekeeping
************ ************

View File

@ -11,6 +11,7 @@
# #
########################################################################## ##########################################################################
from pgadmin.utils import env, IS_WIN, fs_short_path
import builtins import builtins
import logging import logging
import os import os
@ -22,7 +23,6 @@ root = os.path.dirname(os.path.realpath(__file__))
if sys.path[0] != root: if sys.path[0] != root:
sys.path.insert(0, root) sys.path.insert(0, root)
from pgadmin.utils import env, IS_WIN, fs_short_path
########################################################################## ##########################################################################
# Application settings # Application settings
@ -754,6 +754,10 @@ OAUTH2_CONFIG = [
# Oauth scope, ex: 'openid email profile' # Oauth scope, ex: 'openid email profile'
# Note that an 'email' claim is required in the resulting profile # Note that an 'email' claim is required in the resulting profile
'OAUTH2_SCOPE': None, 'OAUTH2_SCOPE': None,
# The claim which is used for the username. If the value is empty the
# email is used as username, but if a value is provided,
# the claim has to exist.
'OAUTH2_USERNAME_CLAIM': None,
# Font-awesome icon, ex: fa-github # Font-awesome icon, ex: fa-github
'OAUTH2_ICON': None, 'OAUTH2_ICON': None,
# UI button colour, ex: #0000ff # UI button colour, ex: #0000ff

View File

@ -123,6 +123,23 @@ class OAuth2Authentication(BaseAuthentication):
[value for value in self.email_keys if value in profile.keys()] [value for value in self.email_keys if value in profile.keys()]
email = profile[email_key[0]] if (len(email_key) > 0) else None email = profile[email_key[0]] if (len(email_key) > 0) else None
username = email
username_claim = None
if 'OAUTH2_USERNAME_CLAIM' in self.oauth2_config[
self.oauth2_current_client]:
username_claim = self.oauth2_config[
self.oauth2_current_client
]['OAUTH2_USERNAME_CLAIM']
if username_claim is not None:
if username_claim in profile:
username = profile[username_claim]
else:
error_msg = "The claim '%s' is required to login into " \
"pgAdmin. Please update your Oauth2 profile." % (
username_claim)
current_app.logger.exception(error_msg)
return False, gettext(error_msg)
if not email or email == '': if not email or email == '':
current_app.logger.exception( current_app.logger.exception(
"An email id is required to login into pgAdmin. " "An email id is required to login into pgAdmin. "
@ -132,10 +149,10 @@ class OAuth2Authentication(BaseAuthentication):
"An email id is required to login into pgAdmin. " "An email id is required to login into pgAdmin. "
"Please update your Oauth2 profile.") "Please update your Oauth2 profile.")
user, msg = self.__auto_create_user(email) user, msg = self.__auto_create_user(username, email)
if user: if user:
user = db.session.query(User).filter_by( user = db.session.query(User).filter_by(
username=email, auth_source=OAUTH2).first() username=username, auth_source=OAUTH2).first()
current_app.login_manager.logout_view = \ current_app.login_manager.logout_view = \
OAuth2Authentication.LOGOUT_VIEW OAuth2Authentication.LOGOUT_VIEW
return login_user(user), None return login_user(user), None
@ -165,17 +182,17 @@ class OAuth2Authentication(BaseAuthentication):
return False, self.oauth2_clients[ return False, self.oauth2_clients[
self.oauth2_current_client].authorize_redirect(redirect_url) self.oauth2_current_client].authorize_redirect(redirect_url)
def __auto_create_user(self, email): def __auto_create_user(self, username, email):
if config.OAUTH2_AUTO_CREATE_USER: if config.OAUTH2_AUTO_CREATE_USER:
user = User.query.filter_by(username=email, user = User.query.filter_by(username=username,
auth_source=OAUTH2).first() auth_source=OAUTH2).first()
if not user: if not user:
return create_user({ return create_user({
'username': email, 'username': username,
'email': email, 'email': email,
'role': 2, 'role': 2,
'active': True, 'active': True,
'auth_source': OAUTH2 'auth_source': OAUTH2
}) })
return True, {'username': email} return True, {'username': username}