Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CILogon] Add idp config allowed_domains_claim for use with allowed_domains #702

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions oauthenticator/cilogon.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,28 @@ def _validate_scope(self, proposal):
Configuring this allows all users authenticating with this identity
provider.
* `allowed_domains`: list of strings
Configuring this together with a `username_claim` that is an email
address enables users to be allowed if their `username_claim` ends
with `@` followed by a domain in this list.
Allows users associated with a listed domain to sign in.

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 `[email protected]` or `[email protected]` usernames.

The domain the user is associated with is based on the username by
default in version 16, but this can be reconfigured to be based on a
claim in the `userinfo` response via `allowed_domains_claim`. The
domain is treated case insensitive and can either be directly
specified by the claim's value or extracted from an email string.
* `allowed_domains_claim`: string (optional)
This configuration represents the claim in the `userinfo` response
to identify a domain that could allow a user to sign in via
`allowed_domains`.

The claim can defaults to the username claim in version 16, but this
will change to "email" in version 17.

.. versionadded:: 16.2

.. versionchanged:: 15.0

Changed format from a list to a dictionary.
Expand Down Expand Up @@ -236,6 +249,11 @@ def _validate_allowed_idps(self, proposal):
"See https://cilogon.org/idplist for the list of EntityIDs of each IDP."
)

# Make allowed_domains lowercase
idp_config["allowed_domains"] = [
ad.lower() for ad in idp_config.get("allowed_domains", [])
]

return idps

skin = Unicode(
Expand Down Expand Up @@ -391,8 +409,20 @@ async def check_allowed(self, username, auth_model):

idp_allowed_domains = self.allowed_idps[user_idp].get("allowed_domains")
if idp_allowed_domains:
unprocessed_username = self._user_info_to_unprocessed_username(user_info)
user_domain = unprocessed_username.split("@", 1)[1].lower()
idp_allowed_domains_claim = self.allowed_idps[user_idp].get(
"allowed_domains_claim"
)
if idp_allowed_domains_claim:
raw_user_domain = user_info.get(idp_allowed_domains_claim)
if not raw_user_domain:
message = f"Configured allowed_domains_claim {idp_allowed_domains_claim} for {user_idp} was not found in the response {user_info.keys()}"
self.log.error(message)
raise web.HTTPError(500, message)
else:
raw_user_domain = self._user_info_to_unprocessed_username(user_info)

# refine a domain from a string that possibly looks like an email
user_domain = raw_user_domain.split("@")[-1].lower()

for ad in idp_allowed_domains:
# fnmatch allow us to use wildcards like * and ?, but
Expand Down
2 changes: 2 additions & 0 deletions oauthenticator/schemas/cilogon-schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ properties:
type: array
items:
type: string
allowed_domains_claim:
type: string
username_derivation:
type: object
additionalProperties: false
Expand Down
16 changes: 15 additions & 1 deletion oauthenticator/tests/test_cilogon.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ async def test_cilogon(
"username_derivation": {
"username_claim": "email",
},
"allowed_domains": ["allowed-domain.org"],
"allowed_domains": ["ALLOWED-domain.org"],
},
{},
"[email protected]",
Expand Down Expand Up @@ -244,6 +244,20 @@ async def test_cilogon(
True,
None,
),
(
"B - allowed by allowed_domains and allowed_domains_claim",
{
"username_derivation": {
"username_claim": "email",
},
"allowed_domains": ["allowed-domain.org", "*.allowed-domain.org"],
"allowed_domains_claim": "email",
},
{},
"[email protected]",
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
Expand Down