mirror of
https://github.com/element-hq/synapse.git
synced 2026-10-23 19:33:44 +00:00
When Matrix Authentication Service (MAS) integration is enabled, allow MAS to set the user locked status in Synapse. (#19554)
Companion PR: https://github.com/element-hq/matrix-authentication-service/pull/5550 to 1) send this flag and 2) provision users proactively when their lock status changes. --- Currently Synapse and MAS have two independent user lock implementations. This PR makes it so that MAS can push its lock status to Synapse when 'provisioning' the user. Having the lock status in Synapse is useful for removing users from the user directory when they are locked. There is otherwise no authentication requirement to have it in Synapse; the enforcement is done by MAS at token introspection time. --------- Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
This commit is contained in:
1 parent
a71c468b04
commit
3aa948c50c
3 files changed
+74
No files matched your search
@@ -0,0 +1 @@
|
||||
When Matrix Authentication Service (MAS) integration is enabled, allow MAS to set the user locked status in Synapse.
|
||||
@@ -112,6 +112,18 @@ class MasProvisionUserResource(MasBaseResource):
|
||||
unset_emails: StrictBool = False
|
||||
set_emails: list[StrictStr] | None = None
|
||||
|
||||
locked: StrictBool | None = None
|
||||
"""
|
||||
True to lock user. False to unlock. None to leave the same.
|
||||
|
||||
This is mostly for informational purposes; if the user's account is locked in MAS
|
||||
but not in Synapse, the token introspection response will prevent them from using
|
||||
their account.
|
||||
|
||||
However, having a local copy of the locked state in Synapse is useful for excluding
|
||||
the user from the user directory.
|
||||
"""
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def validate_exclusive(cls, values: Any) -> Any:
|
||||
@@ -206,6 +218,9 @@ class MasProvisionUserResource(MasBaseResource):
|
||||
validated_at=current_time,
|
||||
)
|
||||
|
||||
if body.locked is not None:
|
||||
await self.store.set_user_locked_status(user_id.to_string(), body.locked)
|
||||
|
||||
if body.unset_avatar_url:
|
||||
await self.profile_handler.set_avatar_url(
|
||||
target_user=user_id,
|
||||
|
||||
@@ -362,6 +362,64 @@ class MasProvisionUserResource(BaseTestCase):
|
||||
)
|
||||
self.assertEqual(channel.code, 400, f"Should fail for content: {content}")
|
||||
|
||||
def test_lock_and_unlock(self) -> None:
|
||||
store = self.hs.get_datastores().main
|
||||
|
||||
# Create a user in the locked state
|
||||
alice = UserID("alice", "test")
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
"/_synapse/mas/provision_user",
|
||||
shorthand=False,
|
||||
access_token=self.SHARED_SECRET,
|
||||
content={
|
||||
"localpart": alice.localpart,
|
||||
"locked": True,
|
||||
},
|
||||
)
|
||||
# This created the user, hence the 201 status code
|
||||
self.assertEqual(channel.code, 201, channel.json_body)
|
||||
self.assertEqual(channel.json_body, {})
|
||||
self.assertTrue(
|
||||
self.get_success(store.get_user_locked_status(alice.to_string()))
|
||||
)
|
||||
|
||||
# Then transition from locked to unlocked
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
"/_synapse/mas/provision_user",
|
||||
shorthand=False,
|
||||
access_token=self.SHARED_SECRET,
|
||||
content={
|
||||
"localpart": alice.localpart,
|
||||
"locked": False,
|
||||
},
|
||||
)
|
||||
# This updated the user, hence the 200 status code
|
||||
self.assertEqual(channel.code, 200, channel.json_body)
|
||||
self.assertEqual(channel.json_body, {})
|
||||
self.assertFalse(
|
||||
self.get_success(store.get_user_locked_status(alice.to_string()))
|
||||
)
|
||||
|
||||
# And back from unlocked to locked
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
"/_synapse/mas/provision_user",
|
||||
shorthand=False,
|
||||
access_token=self.SHARED_SECRET,
|
||||
content={
|
||||
"localpart": alice.localpart,
|
||||
"locked": True,
|
||||
},
|
||||
)
|
||||
# This updated the user, hence the 200 status code
|
||||
self.assertEqual(channel.code, 200, channel.json_body)
|
||||
self.assertEqual(channel.json_body, {})
|
||||
self.assertTrue(
|
||||
self.get_success(store.get_user_locked_status(alice.to_string()))
|
||||
)
|
||||
|
||||
|
||||
@skip_unless(HAS_AUTHLIB, "requires authlib")
|
||||
class MasIsLocalpartAvailableResource(BaseTestCase):
|
||||
|
||||
Reference in new issue
Block a user