Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 6bc5c79

Browse files
committed
Check for proper membership during a local join.
1 parent 4609e58 commit 6bc5c79

File tree

1 file changed

+65
-1
lines changed

1 file changed

+65
-1
lines changed

synapse/handlers/room_member.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple
2121

2222
from synapse import types
23-
from synapse.api.constants import AccountDataTypes, EventTypes, Membership
23+
from synapse.api.constants import AccountDataTypes, EventTypes, JoinRules, Membership
2424
from synapse.api.errors import (
2525
AuthError,
2626
Codes,
@@ -178,6 +178,60 @@ async def ratelimit_invite(
178178

179179
await self._invites_per_user_limiter.ratelimit(requester, invitee_user_id)
180180

181+
async def _can_join_restricted_room(
182+
self, state_ids: StateMap[str], room_id: str, user_id: str
183+
) -> bool:
184+
"""
185+
Check whether a user can join a restricted room.
186+
187+
Args:
188+
state_ids: The state of the room as it currently is.
189+
room_id: The room being joined.
190+
user_id: The user joining the room.
191+
192+
Returns:
193+
True if the user can join the room, false otherwise.
194+
"""
195+
# This only applies to room versions which support the new join rule.
196+
room_version = await self.store.get_room_version(room_id)
197+
if not room_version.msc3083_join_rules:
198+
return True
199+
200+
# If there's no join rule, then it defaults to public (so this doesn't apply).
201+
join_rules_event_id = state_ids.get((EventTypes.JoinRules, ""), None)
202+
if not join_rules_event_id:
203+
return True
204+
205+
# If the join rule is not restricted, this doesn't apply.
206+
join_rules_event = await self.store.get_event(join_rules_event_id)
207+
if join_rules_event.content.get("join_rule") != JoinRules.MSC3083_RESTRICTED:
208+
return True
209+
210+
# If allowed is of the wrong form, then ignore it (and treat the room as public).
211+
allowed_spaces = join_rules_event.content.get("allow", [])
212+
if not isinstance(allowed_spaces, list):
213+
return True
214+
215+
# Get the list of joined rooms and see if there's an overlap.
216+
joined_rooms = await self.store.get_rooms_for_user(user_id)
217+
218+
# Pull out the other room IDs, invalid data gets filtered.
219+
for space in allowed_spaces:
220+
if not isinstance(space, dict):
221+
continue
222+
223+
soace_id = space.get("space")
224+
if not isinstance(soace_id, str):
225+
continue
226+
227+
# The user was joined to one of the spaces specified, they can join
228+
# this room!
229+
if soace_id in joined_rooms:
230+
return True
231+
232+
# The user was not in any of the required spaces.
233+
return False
234+
181235
async def _local_membership_update(
182236
self,
183237
requester: Requester,
@@ -239,6 +293,16 @@ async def _local_membership_update(
239293
prev_member_event = await self.store.get_event(prev_member_event_id)
240294
newly_joined = prev_member_event.membership != Membership.JOIN
241295

296+
# If the member is not already in the room, check if they should be
297+
# allowed access via membership in a space.
298+
if newly_joined and not await self._can_join_restricted_room(
299+
prev_state_ids, room_id, user_id
300+
):
301+
raise AuthError(
302+
403,
303+
"You do not belong to any of the required spaces to join this room.",
304+
)
305+
242306
# Only rate-limit if the user actually joined the room, otherwise we'll end
243307
# up blocking profile updates.
244308
if newly_joined and ratelimit:

0 commit comments

Comments
 (0)