diff --git a/oauthenticator/cilogon.py b/oauthenticator/cilogon.py index 8317e776..8403a1b0 100644 --- a/oauthenticator/cilogon.py +++ b/oauthenticator/cilogon.py @@ -2,6 +2,7 @@ A JupyterHub authenticator class for use with CILogon as an identity provider. """ import os +from fnmatch import fnmatch from urllib.parse import urlparse import jsonschema @@ -174,6 +175,11 @@ def _validate_scope(self, proposal): address enables users to be allowed if their `username_claim` ends with `@` followed by a domain in this list. + Use of wildcards `*` and a bit more is supported via Python's + `fnmatch` function since version 16.2. Setting `allowed_domains` to + `["jupyter.org", "*.jupyter.org"]` would for example allow users + with `jovyan@jupyter.org` or `jovyan@hub.jupyter.org` usernames. + .. versionchanged:: 15.0 Changed format from a list to a dictionary. @@ -366,8 +372,17 @@ async def check_allowed(self, username, auth_model): if idp_allowed_domains: unprocessed_username = self._user_info_to_unprocessed_username(user_info) user_domain = unprocessed_username.split("@", 1)[1].lower() - if user_domain in idp_allowed_domains: - return True + + for ad in idp_allowed_domains: + # fnmatch allow us to use wildcards like * and ?, but + # not the full regex. For simple domain matching this is + # good enough. If we were to use regexes instead, people + # will have to escape all their '.'s, and since that is + # actually going to match 'any character' it is a + # possible security hole. For details see + # https://docs.python.org/3/library/fnmatch.html. + if fnmatch(user_domain, ad): + return True # users should be explicitly allowed via config, otherwise they aren't return False diff --git a/oauthenticator/tests/test_cilogon.py b/oauthenticator/tests/test_cilogon.py index c638a0e0..3081dd22 100644 --- a/oauthenticator/tests/test_cilogon.py +++ b/oauthenticator/tests/test_cilogon.py @@ -231,6 +231,19 @@ async def test_cilogon( False, None, ), + ( + "A - allowed by allowed_domains via a wildcard", + { + "username_derivation": { + "username_claim": "email", + }, + "allowed_domains": ["allowed-domain.org", "*.allowed-domain.org"], + }, + {}, + "user1@sub.allowed-domain.org", + True, + None, + ), # test of allowed_users and admin_users together with # username_derivation actions to verify the final usernames is what # matters when describing allowed_users and admin_users