mirror of
https://github.com/element-hq/synapse.git
synced 2026-10-25 17:03:55 +00:00
Return unable to authorise join if there are unrecognized restricted join rules (#20132)
Rooms may use custom restricted join `allow` rules for things like pre-validating joins before they're allowed into the room graph. However, currently any Synapse with invite permissions that doesn't know about the custom rule will throw a 403, which cancels the entire join process. By returning `M_UNABLE_TO_AUTHORISE_JOIN` instead, the origin server can keep trying other servers to join through. For reference, continuwuity already implements this correctly: https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/5ceed2c2bb4ad80160cd1b7365afd8fe388261eb/src/api/server/make_join.rs#L255-L256
This commit is contained in:
1 parent
a92a21600c
commit
6460a96d6f
4 files changed
+44
-19
No files matched your search
@@ -0,0 +1 @@
|
||||
Fixed joining rooms with unrecognized restricted join rules being rejected outright instead of returning the proper `M_UNABLE_TO_AUTHORISE_JOIN` error code. Contributed by @tulir @ Beeper.
|
||||
@@ -234,11 +234,21 @@ class EventAuthHandler:
|
||||
|
||||
# Get the rooms which allow access to this room and check if the user is
|
||||
# in any of them.
|
||||
allowed_rooms = await self.get_rooms_that_allow_join(state_ids)
|
||||
allowed_rooms, has_unknown_rules = await self.get_rooms_that_allow_join(
|
||||
state_ids
|
||||
)
|
||||
if not await self.is_user_in_rooms(allowed_rooms, user_id):
|
||||
# If this is a remote request, the user might be in an allowed room
|
||||
# that we do not know about.
|
||||
if not self._is_mine_id(user_id):
|
||||
# If there are unknown allow rules, there could be other servers
|
||||
# that are able to authorise the join. Alternatively, if this is
|
||||
# a remote request, the user might be in an allowed room that we
|
||||
# do not know about.
|
||||
if has_unknown_rules:
|
||||
raise SynapseError(
|
||||
400,
|
||||
"Unrecognized restricted join rules found.",
|
||||
Codes.UNABLE_AUTHORISE_JOIN,
|
||||
)
|
||||
elif not self._is_mine_id(user_id):
|
||||
for room_id in allowed_rooms:
|
||||
if not await self._store.is_host_joined(room_id, self._server_name):
|
||||
raise SynapseError(
|
||||
@@ -289,7 +299,7 @@ class EventAuthHandler:
|
||||
|
||||
async def get_rooms_that_allow_join(
|
||||
self, state_ids: StateMap[str]
|
||||
) -> StrCollection:
|
||||
) -> tuple[StrCollection, bool]:
|
||||
"""
|
||||
Generate a list of rooms in which membership allows access to a room.
|
||||
|
||||
@@ -297,12 +307,16 @@ class EventAuthHandler:
|
||||
state_ids: The current state of the room the user wishes to join
|
||||
|
||||
Returns:
|
||||
A collection of room IDs. Membership in any of the rooms in the list grants the ability to join the target room.
|
||||
A tuple of a collection of room IDs and a boolean indicating whether
|
||||
there are any unknown allow rules. Membership in any of the rooms in
|
||||
the list grants the ability to join the target room. If unknown rules
|
||||
are present, failure to authorise the join should not be treated as
|
||||
a permanent error.
|
||||
"""
|
||||
# If there's no join rule, then it defaults to invite (so this doesn't apply).
|
||||
join_rules_event_id = state_ids.get((EventTypes.JoinRules, ""), None)
|
||||
if not join_rules_event_id:
|
||||
return ()
|
||||
return (), False
|
||||
|
||||
# If the join rule is not restricted, this doesn't apply.
|
||||
join_rules_event = await self._store.get_event(join_rules_event_id)
|
||||
@@ -310,16 +324,18 @@ class EventAuthHandler:
|
||||
# If allowed is of the wrong form, then only allow invited users.
|
||||
allow_list = join_rules_event.content.get("allow", [])
|
||||
if not isinstance(allow_list, list):
|
||||
return ()
|
||||
return (), False
|
||||
|
||||
# Pull out the other room IDs, invalid data gets filtered.
|
||||
result = []
|
||||
has_unknown_rules = False
|
||||
for allow in allow_list:
|
||||
if not isinstance(allow, dict):
|
||||
continue
|
||||
|
||||
# If the type is unexpected, skip it.
|
||||
if allow.get("type") != RestrictedJoinRuleTypes.ROOM_MEMBERSHIP:
|
||||
has_unknown_rules = True
|
||||
continue
|
||||
|
||||
room_id = allow.get("room_id")
|
||||
@@ -328,7 +344,7 @@ class EventAuthHandler:
|
||||
|
||||
result.append(room_id)
|
||||
|
||||
return result
|
||||
return result, has_unknown_rules
|
||||
|
||||
async def is_user_in_rooms(self, room_ids: StrCollection, user_id: str) -> bool:
|
||||
"""
|
||||
|
||||
@@ -1379,9 +1379,15 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
||||
return True, list(servers_that_can_issue_invite)
|
||||
|
||||
# Ensure the member should be allowed access via membership in a room.
|
||||
await self.event_auth_handler.check_restricted_join_rules(
|
||||
state_before_join, room_version, user_id, previous_membership
|
||||
)
|
||||
try:
|
||||
await self.event_auth_handler.check_restricted_join_rules(
|
||||
state_before_join, room_version, user_id, previous_membership
|
||||
)
|
||||
except SynapseError as e:
|
||||
if e.errcode == Codes.UNABLE_AUTHORISE_JOIN:
|
||||
servers_that_can_issue_invite.discard(self.hs.hostname)
|
||||
return True, list(servers_that_can_issue_invite)
|
||||
raise
|
||||
|
||||
# If this is going to be a local join, additional information must
|
||||
# be included in the event content in order to efficiently validate
|
||||
|
||||
@@ -685,9 +685,10 @@ class RoomSummaryHandler:
|
||||
if await self._event_auth_handler.has_restricted_join_rules(
|
||||
state_ids, room_version
|
||||
):
|
||||
allowed_rooms = (
|
||||
await self._event_auth_handler.get_rooms_that_allow_join(state_ids)
|
||||
)
|
||||
(
|
||||
allowed_rooms,
|
||||
_,
|
||||
) = await self._event_auth_handler.get_rooms_that_allow_join(state_ids)
|
||||
if await self._event_auth_handler.is_user_in_rooms(
|
||||
allowed_rooms, requester
|
||||
):
|
||||
@@ -707,9 +708,10 @@ class RoomSummaryHandler:
|
||||
if await self._event_auth_handler.has_restricted_join_rules(
|
||||
state_ids, room_version
|
||||
):
|
||||
allowed_rooms = (
|
||||
await self._event_auth_handler.get_rooms_that_allow_join(state_ids)
|
||||
)
|
||||
(
|
||||
allowed_rooms,
|
||||
_,
|
||||
) = await self._event_auth_handler.get_rooms_that_allow_join(state_ids)
|
||||
for space_id in allowed_rooms:
|
||||
if await self._event_auth_handler.is_host_in_room(space_id, origin):
|
||||
return True
|
||||
@@ -822,7 +824,7 @@ class RoomSummaryHandler:
|
||||
if room_version and await self._event_auth_handler.has_restricted_join_rules(
|
||||
join_rules_state_ids, room_version
|
||||
):
|
||||
allowed_rooms = await self._event_auth_handler.get_rooms_that_allow_join(
|
||||
allowed_rooms, _ = await self._event_auth_handler.get_rooms_that_allow_join(
|
||||
join_rules_state_ids
|
||||
)
|
||||
if allowed_rooms:
|
||||
|
||||
Reference in new issue
Block a user