MSC3083: Check for space membership during a local join of restricted rooms. (#9735)
When joining a room with join rules set to 'restricted', check if the user is a member of the spaces defined in the 'allow' key of the join rules. This only applies to an experimental room version, as defined in MSC3083.pull/9771/head
parent
48d44ab142
commit
452991527a
|
@ -0,0 +1 @@
|
||||||
|
Add experimental support for [MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083): restricting room access via group membership.
|
|
@ -46,4 +46,4 @@ if [[ -n "$1" ]]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run the tests!
|
# Run the tests!
|
||||||
COMPLEMENT_BASE_IMAGE=complement-synapse go test -v -tags synapse_blacklist -count=1 $EXTRA_COMPLEMENT_ARGS ./tests
|
COMPLEMENT_BASE_IMAGE=complement-synapse go test -v -tags synapse_blacklist,msc3083 -count=1 $EXTRA_COMPLEMENT_ARGS ./tests
|
||||||
|
|
|
@ -20,7 +20,7 @@ from http import HTTPStatus
|
||||||
from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple
|
from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple
|
||||||
|
|
||||||
from synapse import types
|
from synapse import types
|
||||||
from synapse.api.constants import AccountDataTypes, EventTypes, Membership
|
from synapse.api.constants import AccountDataTypes, EventTypes, JoinRules, Membership
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
AuthError,
|
AuthError,
|
||||||
Codes,
|
Codes,
|
||||||
|
@ -29,6 +29,7 @@ from synapse.api.errors import (
|
||||||
SynapseError,
|
SynapseError,
|
||||||
)
|
)
|
||||||
from synapse.api.ratelimiting import Ratelimiter
|
from synapse.api.ratelimiting import Ratelimiter
|
||||||
|
from synapse.api.room_versions import RoomVersion
|
||||||
from synapse.events import EventBase
|
from synapse.events import EventBase
|
||||||
from synapse.events.snapshot import EventContext
|
from synapse.events.snapshot import EventContext
|
||||||
from synapse.types import JsonDict, Requester, RoomAlias, RoomID, StateMap, UserID
|
from synapse.types import JsonDict, Requester, RoomAlias, RoomID, StateMap, UserID
|
||||||
|
@ -178,6 +179,62 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
||||||
|
|
||||||
await self._invites_per_user_limiter.ratelimit(requester, invitee_user_id)
|
await self._invites_per_user_limiter.ratelimit(requester, invitee_user_id)
|
||||||
|
|
||||||
|
async def _can_join_without_invite(
|
||||||
|
self, state_ids: StateMap[str], room_version: RoomVersion, user_id: str
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Check whether a user can join a room without an invite.
|
||||||
|
|
||||||
|
When joining a room with restricted joined rules (as defined in MSC3083),
|
||||||
|
the membership of spaces must be checked during join.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
state_ids: The state of the room as it currently is.
|
||||||
|
room_version: The room version of the room being joined.
|
||||||
|
user_id: The user joining the room.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the user can join the room, false otherwise.
|
||||||
|
"""
|
||||||
|
# This only applies to room versions which support the new join rule.
|
||||||
|
if not room_version.msc3083_join_rules:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If there's no join rule, then it defaults to public (so this doesn't apply).
|
||||||
|
join_rules_event_id = state_ids.get((EventTypes.JoinRules, ""), None)
|
||||||
|
if not join_rules_event_id:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If the join rule is not restricted, this doesn't apply.
|
||||||
|
join_rules_event = await self.store.get_event(join_rules_event_id)
|
||||||
|
if join_rules_event.content.get("join_rule") != JoinRules.MSC3083_RESTRICTED:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If allowed is of the wrong form, then only allow invited users.
|
||||||
|
allowed_spaces = join_rules_event.content.get("allow", [])
|
||||||
|
if not isinstance(allowed_spaces, list):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Get the list of joined rooms and see if there's an overlap.
|
||||||
|
joined_rooms = await self.store.get_rooms_for_user(user_id)
|
||||||
|
|
||||||
|
# Pull out the other room IDs, invalid data gets filtered.
|
||||||
|
for space in allowed_spaces:
|
||||||
|
if not isinstance(space, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
space_id = space.get("space")
|
||||||
|
if not isinstance(space_id, str):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# The user was joined to one of the spaces specified, they can join
|
||||||
|
# this room!
|
||||||
|
if space_id in joined_rooms:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# The user was not in any of the required spaces.
|
||||||
|
return False
|
||||||
|
|
||||||
async def _local_membership_update(
|
async def _local_membership_update(
|
||||||
self,
|
self,
|
||||||
requester: Requester,
|
requester: Requester,
|
||||||
|
@ -235,9 +292,25 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
||||||
|
|
||||||
if event.membership == Membership.JOIN:
|
if event.membership == Membership.JOIN:
|
||||||
newly_joined = True
|
newly_joined = True
|
||||||
|
user_is_invited = False
|
||||||
if prev_member_event_id:
|
if prev_member_event_id:
|
||||||
prev_member_event = await self.store.get_event(prev_member_event_id)
|
prev_member_event = await self.store.get_event(prev_member_event_id)
|
||||||
newly_joined = prev_member_event.membership != Membership.JOIN
|
newly_joined = prev_member_event.membership != Membership.JOIN
|
||||||
|
user_is_invited = prev_member_event.membership == Membership.INVITE
|
||||||
|
|
||||||
|
# If the member is not already in the room and is not accepting an invite,
|
||||||
|
# check if they should be allowed access via membership in a space.
|
||||||
|
if (
|
||||||
|
newly_joined
|
||||||
|
and not user_is_invited
|
||||||
|
and not await self._can_join_without_invite(
|
||||||
|
prev_state_ids, event.room_version, user_id
|
||||||
|
)
|
||||||
|
):
|
||||||
|
raise AuthError(
|
||||||
|
403,
|
||||||
|
"You do not belong to any of the required spaces to join this room.",
|
||||||
|
)
|
||||||
|
|
||||||
# Only rate-limit if the user actually joined the room, otherwise we'll end
|
# Only rate-limit if the user actually joined the room, otherwise we'll end
|
||||||
# up blocking profile updates.
|
# up blocking profile updates.
|
||||||
|
|
Loading…
Reference in New Issue