feat: make the SCIM IPD id configurable

This commit is contained in:
Éloi Rivard 2024-11-20 10:13:49 +01:00
parent c48e7310a4
commit 36a600c4fb
No known key found for this signature in database
GPG key ID: 7EDA204EA57DD184
3 changed files with 50 additions and 17 deletions

View file

@ -256,6 +256,19 @@ class MSC3866Config:
require_approval_for_new_accounts: bool = False
@attr.s(auto_attribs=True, frozen=True, slots=True)
class MSC4098Config:
"""Configuration for MSC4098 (SCIM provisioning)"""
# Whether the SCIM provisioning API is enabled.
enabled: bool = False
# The ID of the IDP that will be associated with the SCIM 'externalId' parameter.
# This should be one of the values used in the SSO config.
# If unset, a default '__scim__' id will be used.
idp_id: Optional[str] = None
class ExperimentalConfig(Config):
"""Config section for enabling experimental features"""
@ -416,13 +429,18 @@ class ExperimentalConfig(Config):
)
# MSC4098: SCIM provisioning API
self.msc4098_enabled = experimental.get("msc4098", False)
if self.msc4098_enabled and self.msc3861.enabled:
try:
self.msc4098 = MSC4098Config(**experimental.get("msc4098", {}))
if self.msc4098.enabled and self.msc3861.enabled:
raise ConfigError(
"MSC3861 and MSC4098 are mutually exclusive. Please disable one or the"
"other.",
("experimental", "msc4098"),
)
except ValueError as exc:
raise ConfigError(
"MSC3861 and MSC4098 are mutually exclusive. Please disable one or the"
"other.",
("experimental", "msc4098"),
)
"Invalid MSC4098 configuration", ("experimental", "msc4098")
) from exc
# MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code
self.msc4108_enabled = experimental.get("msc4108_enabled", False)

View file

@ -84,7 +84,7 @@ if TYPE_CHECKING:
from synapse.server import HomeServer
SCIM_PREFIX = "/_synapse/admin/scim/v2"
SCIM_IDP_ID = "__scim__"
SCIM_DEFAULT_IDP_ID = "__scim__"
logger = logging.getLogger(__name__)
@ -99,7 +99,7 @@ class SCIMResource(JsonResource):
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
if not hs.config.experimental.msc4098_enabled:
if not hs.config.experimental.msc4098.enabled:
return
SchemaListServlet(hs).register(http_server)
@ -156,9 +156,10 @@ class SCIMServlet(RestServlet):
async def get_scim_external_id(self, user_id: str) -> Optional[str]:
"""Read the external id stored in the special SCIM IDP."""
scim_idp_id = self.hs.config.experimental.msc4098.idp_id or SCIM_DEFAULT_IDP_ID
external_ids = await self.store.get_external_ids_by_user(user_id)
for idp_id, external_id in external_ids:
if idp_id == SCIM_IDP_ID:
if idp_id == scim_idp_id:
return external_id
return None
@ -303,13 +304,16 @@ class UserServlet(SCIMServlet):
external_id = await self.get_scim_external_id(user_id)
if request_user.external_id != external_id:
scim_idp_id = (
self.hs.config.experimental.msc4098.idp_id or SCIM_DEFAULT_IDP_ID
)
if external_id:
await self.store.remove_user_external_id(
SCIM_IDP_ID, external_id, user_id
scim_idp_id, external_id, user_id
)
if request_user.external_id:
await self.store.record_user_external_id(
SCIM_IDP_ID, request_user.external_id, user_id
scim_idp_id, request_user.external_id, user_id
)
if request_user.photos and request_user.photos[0].value:
@ -451,8 +455,11 @@ class UserListServlet(SCIMServlet):
)
if request_user.external_id:
scim_idp_id = (
self.hs.config.experimental.msc4098.idp_id or SCIM_DEFAULT_IDP_ID
)
await self.store.record_user_external_id(
SCIM_IDP_ID, request_user.external_id, user_id
scim_idp_id, request_user.external_id, user_id
)
now_ts = self.hs.get_clock().time_msec()

View file

@ -8,7 +8,7 @@ import synapse.rest.scim
from synapse.config import ConfigError
from synapse.config.homeserver import HomeServerConfig
from synapse.rest.client import login
from synapse.rest.scim import HAS_SCIM2, SCIM_IDP_ID
from synapse.rest.scim import HAS_SCIM2, SCIM_DEFAULT_IDP_ID
from synapse.server import HomeServer
from synapse.types import JsonDict, UserID
from synapse.util import Clock
@ -58,7 +58,9 @@ class SCIMExperimentalFeatureTestCase(HomeserverTestCase):
config_dict = {
"experimental_features": {
"msc4098": True,
"msc4098": {
"enabled": True,
},
"msc3861": {"enabled": True},
},
**default_config("test"),
@ -90,7 +92,10 @@ class UserProvisioningTestCase(HomeserverTestCase):
def default_config(self) -> JsonDict:
conf = super().default_config()
conf.setdefault("experimental_features", {}).setdefault("msc4098", True)
msc4098_conf = conf.setdefault("experimental_features", {}).setdefault(
"msc4098", {}
)
msc4098_conf.setdefault("enabled", True)
return conf
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
@ -125,7 +130,7 @@ class UserProvisioningTestCase(HomeserverTestCase):
)
self.get_success(
self.store.record_user_external_id(
SCIM_IDP_ID, "IDP-user", self.user_user_id
SCIM_DEFAULT_IDP_ID, "IDP-user", self.user_user_id
)
)
@ -578,7 +583,10 @@ class SCIMMetadataTestCase(HomeserverTestCase):
def default_config(self) -> JsonDict:
conf = super().default_config()
conf.setdefault("experimental_features", {}).setdefault("msc4098", True)
msc4098_conf = conf.setdefault("experimental_features", {}).setdefault(
"msc4098", {}
)
msc4098_conf.setdefault("enabled", True)
return conf
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None: