From 1e792d58760237845a3fe9dbde4449dc0a771c6e Mon Sep 17 00:00:00 2001 From: Javier Romero Castro Date: Tue, 23 May 2023 17:15:55 +0200 Subject: [PATCH] oidc: adds group handler * closes https://github.com/CERNDocumentServer/cds-rdm/issues/16 --- invenio.cfg | 32 +++++++++++++++++++++++++++----- site/cds_rdm/oidc.py | 25 +++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/invenio.cfg b/invenio.cfg index e6998f1..02a8856 100644 --- a/invenio.cfg +++ b/invenio.cfg @@ -194,14 +194,26 @@ SECURITY_SEND_REGISTER_EMAIL = False # ------------------- # See https://github.com/inveniosoftware/invenio-oauthclient/blob/master/invenio_oauthclient/config.py from invenio_oauthclient.contrib.keycloak import KeycloakSettingsHelper -from cds_rdm.oidc import cern_info_serializer, confirm_registration_form +from cds_rdm.oidc import ( + cern_info_serializer, + confirm_registration_form, + cern_group_serializer, + cern_group_handler, +) +from urllib.parse import quote + +_base_url = os.environ.get("CERN_KEYCLOAK_BASE_URL", "https://keycloak-qa.cern.ch/") _keycloak_helper = KeycloakSettingsHelper( title="CERN", description="CERN SSO authentication", - base_url=os.environ.get("CERN_KEYCLOAK_BASE_URL", "https://keycloak-qa.cern.ch/"), + base_url=_base_url, realm="cern", app_key="CERN_APP_CREDENTIALS", + logout_url="{}auth/realms/cern/protocol/openid-connect/logout?redirect_uri={}".format( + _base_url, + quote(os.environ.get("INVENIO_SITE_UI_URL", SITE_UI_URL)) + ), ) OAUTHCLIENT_CERN_REALM_URL = _keycloak_helper.realm_url OAUTHCLIENT_CERN_USER_INFO_URL = _keycloak_helper.user_info_url @@ -210,9 +222,19 @@ OAUTHCLIENT_CERN_VERIFY_AUD = False OAUTHCLIENT_CERN_USER_INFO_FROM_ENDPOINT = False handlers = _keycloak_helper.get_handlers() -handlers["signup_handler"]["info_serializer"] = cern_info_serializer -rest_handlers = _keycloak_helper.get_handlers() -rest_handlers["signup_handler"]["info_serializer"] = cern_info_serializer +handlers["signup_handler"] = { + **handlers["signup_handler"], + "info_serializer": cern_info_serializer, + "groups_serializer": cern_group_serializer, + "groups": cern_group_handler, +} +rest_handlers = _keycloak_helper.get_rest_handlers() +rest_handlers["signup_handler"] = { + **rest_handlers["signup_handler"], + "info_serializer": cern_info_serializer, + "groups_serializer": cern_group_serializer, + "groups": cern_group_handler, +} OAUTHCLIENT_SIGNUP_FORM = confirm_registration_form diff --git a/site/cds_rdm/oidc.py b/site/cds_rdm/oidc.py index 6ba8f8d..4dfdfcb 100644 --- a/site/cds_rdm/oidc.py +++ b/site/cds_rdm/oidc.py @@ -3,21 +3,43 @@ from invenio_userprofiles.forms import confirm_register_form_preferences_factory from flask import current_app from werkzeug.local import LocalProxy +from invenio_oauthclient import current_oauthclient +from invenio_oauthclient.contrib.keycloak.handlers import get_user_info _security = LocalProxy(lambda: current_app.extensions["security"]) def confirm_registration_form(*args, **kwargs): Form = confirm_register_form_preferences_factory(_security.confirm_register_form) + class _Form(Form): password = None recaptcha = None submit = None # defined in the template + return _Form(*args, **kwargs) -def cern_info_serializer(remote, resp, token_user_info, user_info): +def cern_group_serializer(remote, groups, **kwargs): + """Serialize the groups response object.""" + serialized_groups = [] + # E-groups do have unique names and this name cannot be updated, therefore the name can act as an ID for invenio + for group_name in groups: + serialized_groups.append({"id": group_name, "name": group_name}) + + return serialized_groups + +def cern_group_handler(remote, resp): + """Retrieves groups from remote account.""" + token_user_info, user_info = get_user_info(remote, resp) + groups = token_user_info.get("groups", []) + handlers = current_oauthclient.signup_handlers[remote.name] + # `remote` param automatically injected via `make_handler` helper + return handlers["groups_serializer"](groups) + + +def cern_info_serializer(remote, resp, token_user_info, user_info): user_info = user_info or {} # prevent errors when accessing None.get(...) email = token_user_info.get("email") or user_info["email"] @@ -43,4 +65,3 @@ def cern_info_serializer(remote, resp, token_user_info, user_info): "external_id": cern_upn, "external_method": remote.name, } -