From 03a8c3bb7ab03c88975ecec4b0cdb0c678fff91e Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Tue, 14 Jan 2025 21:07:51 +0700 Subject: [PATCH] Changes to make compatible with OAM (#64) * set client.allow["issuer_mismatch"] = True to make more compatible * add in changes to make work with OAM * fix ClientSettings import * fix missing init * fix dep * try test with domain set * fix setting key source * fix prepare_url * fix prepare_url * add property to interface to appear in control panel * fix control panel adapter for classic * fix label * change how jwik_uri is changed * fix how domain is set * fix formatting * tidy up code * fix flake8 --------- Co-authored-by: Mauro Amico --- setup.py | 1 + src/pas/plugins/oidc/controlpanel/classic.py | 8 ++++ src/pas/plugins/oidc/interfaces.py | 7 ++++ src/pas/plugins/oidc/plugins.py | 44 +++++++++++++++++++- src/pas/plugins/oidc/utils.py | 2 + tests/services/conftest.py | 1 + 6 files changed, 62 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8caf9a2..76ec194 100644 --- a/setup.py +++ b/setup.py @@ -61,6 +61,7 @@ "plone.protect", "plone.restapi>=8.34.0", "oic", + "requests", "z3c.form", ], extras_require={ diff --git a/src/pas/plugins/oidc/controlpanel/classic.py b/src/pas/plugins/oidc/controlpanel/classic.py index 0a3dfb8..b4ed98c 100644 --- a/src/pas/plugins/oidc/controlpanel/classic.py +++ b/src/pas/plugins/oidc/controlpanel/classic.py @@ -138,6 +138,14 @@ def user_property_as_userid(self): def user_property_as_userid(self, value): self.settings.user_property_as_userid = value + @property + def identity_domain_name(self): + return self.settings.identity_domain_name + + @identity_domain_name.setter + def identity_domain_name(self, value): + self.settings.identity_domain_name = value + class OIDCSettingsForm(controlpanel.RegistryEditForm): schema = IOIDCSettings diff --git a/src/pas/plugins/oidc/interfaces.py b/src/pas/plugins/oidc/interfaces.py index 18350fe..f0626ba 100644 --- a/src/pas/plugins/oidc/interfaces.py +++ b/src/pas/plugins/oidc/interfaces.py @@ -119,6 +119,13 @@ class IOIDCSettings(Interface): default="sub", ) + identity_domain_name = schema.TextLine( + title=_("Identity Domain Name"), + description=_("Required for Oracle Authentication Manager only"), + required=False, + default="", + ) + class IOIDCControlpanel(IControlpanel): """OIDC Control panel""" diff --git a/src/pas/plugins/oidc/plugins.py b/src/pas/plugins/oidc/plugins.py index 2e5ca41..8f61940 100644 --- a/src/pas/plugins/oidc/plugins.py +++ b/src/pas/plugins/oidc/plugins.py @@ -27,6 +27,7 @@ import itertools import plone.api as api +import requests import string @@ -69,6 +70,29 @@ class IOIDCPlugin(Interface): """ """ +class OAMClient(Client): + """Override so we can adjust the jwks_uri to add domain needed for OAM""" + + def __init__(self, *args, domain=None, **xargs): + super().__init__(self, *args, **xargs) + self.domain = domain + if domain: + session = requests.Session() + session.headers.update({"x-oauth-identity-domain-name": domain}) + self.settings.requests_session = session + + def handle_provider_config(self, pcr, issuer, keys=True, endpoints=True): + domain = self.domain + if domain: + # we need to modify jwks_uri in the provider_info to add the identityDomain for OAM + # gets used in https://github.com/CZ-NIC/pyoidc/blob/0bd1eadcefc5ccb7ef6c69d9b631537a7d3cfe30/src/oic/oauth2/__init__.py#L1132 + url = pcr["jwks_uri"] + req = requests.PreparedRequest() + req.prepare_url(url, dict(identityDomainName=domain)) + pcr["jwks_uri"] = req.url + return super().handle_provider_config(pcr, issuer, keys, endpoints) + + @implementer(IOIDCPlugin) class OIDCPlugin(BasePlugin): """PAS Plugin OpenID Connect.""" @@ -92,6 +116,7 @@ class OIDCPlugin(BasePlugin): use_deprecated_redirect_uri_for_logout = False use_modified_openid_schema = False user_property_as_userid = "sub" + identity_domain_name = "" _properties = ( dict(id="title", type="string", mode="w", label="Title"), @@ -160,6 +185,12 @@ class OIDCPlugin(BasePlugin): mode="w", label="User info property used as userid, default 'sub'", ), + dict( + id="identity_domain_name", + type="string", + mode="w", + label="Identity Domain Name (required for Oracle Authentication Manager only)", + ), ) def __init__(self, id, title=None): @@ -331,8 +362,19 @@ def _setupJWTTicket(self, user_id, user): # TODO: memoize (?) def get_oauth2_client(self): + domain = self.getProperty("identity_domain_name") try: - client = Client(client_authn_method=CLIENT_AUTHN_METHOD) + if domain: + client = OAMClient( + client_authn_method=CLIENT_AUTHN_METHOD, + domain=domain, + ) + else: + client = Client(client_authn_method=CLIENT_AUTHN_METHOD) + client.allow["issuer_mismatch"] = ( + True # Some providers aren't configured with configured and issuer urls the same even though they should. + ) + # registration_response = client.register(provider_info["registration_endpoint"], redirect_uris=...) # ... oic.exception.RegistrationError: {'error': 'insufficient_scope', # 'error_description': "Policy 'Trusted Hosts' rejected request to client-registration service. Details: Host not trusted."} diff --git a/src/pas/plugins/oidc/utils.py b/src/pas/plugins/oidc/utils.py index 8652c9f..ccf11f3 100644 --- a/src/pas/plugins/oidc/utils.py +++ b/src/pas/plugins/oidc/utils.py @@ -127,6 +127,8 @@ def authorization_flow_args(plugin: plugins.OIDCPlugin, session: Session) -> dic "nonce": session.get("nonce"), "redirect_uri": plugin.get_redirect_uris(), } + if plugin.getProperty("identity_domain_name"): + args["domain"] = plugin.getProperty("identity_domain_name", "") if plugin.getProperty("use_pkce"): # Build a random string of 43 to 128 characters # and send it in the request as a base64-encoded urlsafe string of the sha256 hash of that string diff --git a/tests/services/conftest.py b/tests/services/conftest.py index 882ea5c..5a70c9b 100644 --- a/tests/services/conftest.py +++ b/tests/services/conftest.py @@ -23,6 +23,7 @@ def keycloak(keycloak_service): "scope": ("openid", "profile", "email"), "redirect_uris": ("/login_oidc/oidc",), "create_restapi_ticket": True, + "identity_domain_name": "blah", # ensure non OAM SP ignores extra params/header }