diff --git a/synapse/handlers/sso.py b/synapse/handlers/sso.py
index ee74289b6c..2795b282e5 100644
--- a/synapse/handlers/sso.py
+++ b/synapse/handlers/sso.py
@@ -33,17 +33,17 @@ from typing import (
Mapping,
NoReturn,
Optional,
+ Protocol,
Set,
)
from urllib.parse import urlencode
import attr
-from typing_extensions import Protocol
from twisted.web.iweb import IRequest
from twisted.web.server import Request
-from synapse.api.constants import LoginType
+from synapse.api.constants import LoginType, ProfileFields
from synapse.api.errors import Codes, NotFoundError, RedirectException, SynapseError
from synapse.config.sso import SsoAttributeRequirement
from synapse.handlers.device import DeviceHandler
@@ -81,8 +81,7 @@ class SsoIdentityProvider(Protocol):
An Identity Provider, or IdP, is an external HTTP service which authenticates a user
to say whether they should be allowed to log in, or perform a given action.
- Synapse supports various implementations of IdPs, including OpenID Connect, SAML,
- and CAS.
+ Synapse supports various implementations of IdPs, including OpenID Connect.
The main entry point is `handle_redirect_request`, which should return a URI to
redirect the user's browser to the IdP's authentication page.
@@ -97,7 +96,7 @@ class SsoIdentityProvider(Protocol):
def idp_id(self) -> str:
"""A unique identifier for this SSO provider
- Eg, "saml", "cas", "github"
+ Eg. "github"
"""
@property
@@ -157,7 +156,7 @@ class UserAttributes:
class UsernameMappingSession:
"""Data we track about SSO sessions"""
- # A unique identifier for this SSO provider, e.g. "oidc" or "saml".
+ # A unique identifier for this SSO provider, e.g. "oidc".
auth_provider_id: str
# An optional session ID from the IdP.
@@ -351,7 +350,7 @@ class SsoHandler:
Args:
auth_provider_id: A unique identifier for this SSO provider, e.g.
- "oidc" or "saml".
+ "oidc".
remote_user_id: The user ID according to the remote IdP. This might
be an e-mail address, a GUID, or some other form. It must be
unique and immutable.
@@ -418,7 +417,7 @@ class SsoHandler:
Args:
auth_provider_id: A unique identifier for this SSO provider, e.g.
- "oidc" or "saml".
+ "oidc".
remote_user_id: The unique identifier from the SSO provider.
@@ -634,7 +633,7 @@ class SsoHandler:
Args:
auth_provider_id: A unique identifier for this SSO provider, e.g.
- "oidc" or "saml".
+ "oidc".
remote_user_id: The unique identifier from the SSO provider.
@@ -704,7 +703,7 @@ class SsoHandler:
including a non-empty localpart.
auth_provider_id: A unique identifier for this SSO provider, e.g.
- "oidc" or "saml".
+ "oidc".
remote_user_id: The unique identifier from the SSO provider.
@@ -813,9 +812,10 @@ class SsoHandler:
# bail if user already has the same avatar
profile = await self._profile_handler.get_profile(user_id)
- if profile["avatar_url"] is not None:
- server_name = profile["avatar_url"].split("/")[-2]
- media_id = profile["avatar_url"].split("/")[-1]
+ if ProfileFields.AVATAR_URL in profile:
+ avatar_url_parts = profile[ProfileFields.AVATAR_URL].split("/")
+ server_name = avatar_url_parts[-2]
+ media_id = avatar_url_parts[-1]
if self._is_mine_server_name(server_name):
media = await self._media_repo.store.get_local_media(media_id) # type: ignore[has-type]
if media is not None and upload_name == media.upload_name:
@@ -855,12 +855,12 @@ class SsoHandler:
Given an SSO ID, retrieve the user ID for it and complete UIA.
Note that this requires that the user is mapped in the "user_external_ids"
- table. This will be the case if they have ever logged in via SAML or OIDC in
+ table. This will be the case if they have ever logged in via OIDC in
recentish synapse versions, but may not be for older users.
Args:
auth_provider_id: A unique identifier for this SSO provider, e.g.
- "oidc" or "saml".
+ "oidc".
remote_user_id: The unique identifier from the SSO provider.
ui_auth_session_id: The ID of the user-interactive auth session.
request: The request to complete.
@@ -1184,16 +1184,16 @@ class SsoHandler:
Args:
auth_provider_id: A unique identifier for this SSO provider, e.g.
- "oidc" or "saml".
+ "oidc".
auth_provider_session_id: The session ID from the provider to logout
expected_user_id: The user we're expecting to logout. If set, it will ignore
sessions belonging to other users and log an error.
"""
# It is expected that this is the main process.
- assert isinstance(
- self._device_handler, DeviceHandler
- ), "revoking SSO sessions can only be called on the main process"
+ assert isinstance(self._device_handler, DeviceHandler), (
+ "revoking SSO sessions can only be called on the main process"
+ )
# Invalidate any running user-mapping sessions
to_delete = []
@@ -1276,12 +1276,16 @@ def _check_attribute_requirement(
return False
# If the requirement is None, the attribute existing is enough.
- if req.value is None:
+ if req.value is None and req.one_of is None:
return True
values = attributes[req.attribute]
if req.value in values:
return True
+ if req.one_of:
+ for value in req.one_of:
+ if value in values:
+ return True
logger.info(
"SSO attribute %s did not match required value '%s' (was '%s')",
|