mirror of
https://github.com/element-hq/synapse.git
synced 2024-12-18 17:10:43 +03:00
feat: make the SCIM IPD id configurable
This commit is contained in:
parent
c48e7310a4
commit
36a600c4fb
3 changed files with 50 additions and 17 deletions
|
@ -256,6 +256,19 @@ class MSC3866Config:
|
||||||
require_approval_for_new_accounts: bool = False
|
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):
|
class ExperimentalConfig(Config):
|
||||||
"""Config section for enabling experimental features"""
|
"""Config section for enabling experimental features"""
|
||||||
|
|
||||||
|
@ -416,13 +429,18 @@ class ExperimentalConfig(Config):
|
||||||
)
|
)
|
||||||
|
|
||||||
# MSC4098: SCIM provisioning API
|
# MSC4098: SCIM provisioning API
|
||||||
self.msc4098_enabled = experimental.get("msc4098", False)
|
try:
|
||||||
if self.msc4098_enabled and self.msc3861.enabled:
|
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(
|
raise ConfigError(
|
||||||
"MSC3861 and MSC4098 are mutually exclusive. Please disable one or the"
|
"Invalid MSC4098 configuration", ("experimental", "msc4098")
|
||||||
"other.",
|
) from exc
|
||||||
("experimental", "msc4098"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code
|
# MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code
|
||||||
self.msc4108_enabled = experimental.get("msc4108_enabled", False)
|
self.msc4108_enabled = experimental.get("msc4108_enabled", False)
|
||||||
|
|
|
@ -84,7 +84,7 @@ if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
|
||||||
SCIM_PREFIX = "/_synapse/admin/scim/v2"
|
SCIM_PREFIX = "/_synapse/admin/scim/v2"
|
||||||
SCIM_IDP_ID = "__scim__"
|
SCIM_DEFAULT_IDP_ID = "__scim__"
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ class SCIMResource(JsonResource):
|
||||||
|
|
||||||
|
|
||||||
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
||||||
if not hs.config.experimental.msc4098_enabled:
|
if not hs.config.experimental.msc4098.enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
SchemaListServlet(hs).register(http_server)
|
SchemaListServlet(hs).register(http_server)
|
||||||
|
@ -156,9 +156,10 @@ class SCIMServlet(RestServlet):
|
||||||
async def get_scim_external_id(self, user_id: str) -> Optional[str]:
|
async def get_scim_external_id(self, user_id: str) -> Optional[str]:
|
||||||
"""Read the external id stored in the special SCIM IDP."""
|
"""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)
|
external_ids = await self.store.get_external_ids_by_user(user_id)
|
||||||
for idp_id, external_id in external_ids:
|
for idp_id, external_id in external_ids:
|
||||||
if idp_id == SCIM_IDP_ID:
|
if idp_id == scim_idp_id:
|
||||||
return external_id
|
return external_id
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
@ -303,13 +304,16 @@ class UserServlet(SCIMServlet):
|
||||||
|
|
||||||
external_id = await self.get_scim_external_id(user_id)
|
external_id = await self.get_scim_external_id(user_id)
|
||||||
if request_user.external_id != external_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:
|
if external_id:
|
||||||
await self.store.remove_user_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:
|
if request_user.external_id:
|
||||||
await self.store.record_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:
|
if request_user.photos and request_user.photos[0].value:
|
||||||
|
@ -451,8 +455,11 @@ class UserListServlet(SCIMServlet):
|
||||||
)
|
)
|
||||||
|
|
||||||
if request_user.external_id:
|
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(
|
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()
|
now_ts = self.hs.get_clock().time_msec()
|
||||||
|
|
|
@ -8,7 +8,7 @@ import synapse.rest.scim
|
||||||
from synapse.config import ConfigError
|
from synapse.config import ConfigError
|
||||||
from synapse.config.homeserver import HomeServerConfig
|
from synapse.config.homeserver import HomeServerConfig
|
||||||
from synapse.rest.client import login
|
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.server import HomeServer
|
||||||
from synapse.types import JsonDict, UserID
|
from synapse.types import JsonDict, UserID
|
||||||
from synapse.util import Clock
|
from synapse.util import Clock
|
||||||
|
@ -58,7 +58,9 @@ class SCIMExperimentalFeatureTestCase(HomeserverTestCase):
|
||||||
|
|
||||||
config_dict = {
|
config_dict = {
|
||||||
"experimental_features": {
|
"experimental_features": {
|
||||||
"msc4098": True,
|
"msc4098": {
|
||||||
|
"enabled": True,
|
||||||
|
},
|
||||||
"msc3861": {"enabled": True},
|
"msc3861": {"enabled": True},
|
||||||
},
|
},
|
||||||
**default_config("test"),
|
**default_config("test"),
|
||||||
|
@ -90,7 +92,10 @@ class UserProvisioningTestCase(HomeserverTestCase):
|
||||||
|
|
||||||
def default_config(self) -> JsonDict:
|
def default_config(self) -> JsonDict:
|
||||||
conf = super().default_config()
|
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
|
return conf
|
||||||
|
|
||||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||||
|
@ -125,7 +130,7 @@ class UserProvisioningTestCase(HomeserverTestCase):
|
||||||
)
|
)
|
||||||
self.get_success(
|
self.get_success(
|
||||||
self.store.record_user_external_id(
|
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:
|
def default_config(self) -> JsonDict:
|
||||||
conf = super().default_config()
|
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
|
return conf
|
||||||
|
|
||||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||||
|
|
Loading…
Reference in a new issue