Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/11668.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for [MSC3613](https://github.com/matrix-org/matrix-doc/pull/3613): Combined join rules.
32 changes: 32 additions & 0 deletions synapse/api/room_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class RoomVersion:
msc2716_historical: bool
# MSC2716: Adds support for redacting "insertion", "chunk", and "marker" events
msc2716_redactions: bool
# MSC3613: Allows for concurrent join rules in a simplified manner
msc3613_simplified_join_rules: bool


class RoomVersions:
Expand All @@ -99,6 +101,7 @@ class RoomVersions:
msc2403_knocking=False,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
V2 = RoomVersion(
"2",
Expand All @@ -115,6 +118,7 @@ class RoomVersions:
msc2403_knocking=False,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
V3 = RoomVersion(
"3",
Expand All @@ -131,6 +135,7 @@ class RoomVersions:
msc2403_knocking=False,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
V4 = RoomVersion(
"4",
Expand All @@ -147,6 +152,7 @@ class RoomVersions:
msc2403_knocking=False,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
V5 = RoomVersion(
"5",
Expand All @@ -163,6 +169,7 @@ class RoomVersions:
msc2403_knocking=False,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
V6 = RoomVersion(
"6",
Expand All @@ -179,6 +186,7 @@ class RoomVersions:
msc2403_knocking=False,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
MSC2176 = RoomVersion(
"org.matrix.msc2176",
Expand All @@ -195,6 +203,7 @@ class RoomVersions:
msc2403_knocking=False,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
V7 = RoomVersion(
"7",
Expand All @@ -211,6 +220,7 @@ class RoomVersions:
msc2403_knocking=True,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
V8 = RoomVersion(
"8",
Expand All @@ -227,6 +237,7 @@ class RoomVersions:
msc2403_knocking=True,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
V9 = RoomVersion(
"9",
Expand All @@ -243,6 +254,7 @@ class RoomVersions:
msc2403_knocking=True,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=False,
)
MSC2716v3 = RoomVersion(
"org.matrix.msc2716v3",
Expand All @@ -259,6 +271,25 @@ class RoomVersions:
msc2403_knocking=True,
msc2716_historical=True,
msc2716_redactions=True,
msc3613_simplified_join_rules=False,
)
MSC3613 = RoomVersion(
# v9 + MSC3613
"org.matrix.msc3613",
RoomDisposition.UNSTABLE,
EventFormatVersions.V3,
StateResolutionVersions.V2,
enforce_key_validity=True,
special_case_aliases_auth=False,
strict_canonicaljson=True,
limit_notifications_power_levels=True,
msc2176_redaction_rules=False,
msc3083_join_rules=True,
msc3375_redaction_rules=True,
msc2403_knocking=True,
msc2716_historical=False,
msc2716_redactions=False,
msc3613_simplified_join_rules=True,
)


Expand All @@ -276,6 +307,7 @@ class RoomVersions:
RoomVersions.V8,
RoomVersions.V9,
RoomVersions.MSC2716v3,
RoomVersions.MSC3613,
)
}

Expand Down
22 changes: 11 additions & 11 deletions synapse/event_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
RoomVersion,
)
from synapse.types import StateMap, UserID, get_domain_from_id
from synapse.util.join_rules import is_join_rule as is_join_rule_in_version

if typing.TYPE_CHECKING:
# conditional imports to avoid import cycle
Expand Down Expand Up @@ -339,11 +340,10 @@ def _is_membership_change_allowed(
target_banned = target and target.membership == Membership.BAN

key = (EventTypes.JoinRules, "")
join_rule_event = auth_events.get(key)
if join_rule_event:
join_rule = join_rule_event.content.get("join_rule", JoinRules.INVITE)
else:
join_rule = JoinRules.INVITE
join_rule_event: Optional[EventBase] = auth_events.get(key)

def is_join_rule(rule: JoinRules) -> bool:
return is_join_rule_in_version(room_version, join_rule_event, rule)

user_level = get_user_power_level(event.user_id, auth_events)
target_level = get_user_power_level(target_user_id, auth_events)
Expand All @@ -360,7 +360,7 @@ def _is_membership_change_allowed(
"target_banned": target_banned,
"target_in_room": target_in_room,
"membership": membership,
"join_rule": join_rule,
"join_rules": join_rule_event.content if join_rule_event else "unset",
"target_user_id": target_user_id,
"event.user_id": event.user_id,
},
Expand Down Expand Up @@ -412,9 +412,9 @@ def _is_membership_change_allowed(
raise AuthError(403, "Cannot force another user to join.")
elif target_banned:
raise AuthError(403, "You are banned from this room")
elif join_rule == JoinRules.PUBLIC:
elif is_join_rule(JoinRules.PUBLIC):
pass
elif room_version.msc3083_join_rules and join_rule == JoinRules.RESTRICTED:
elif room_version.msc3083_join_rules and is_join_rule(JoinRules.RESTRICTED):
# This is the same as public, but the event must contain a reference
# to the server who authorised the join. If the event does not contain
# the proper content it is rejected.
Expand All @@ -440,8 +440,8 @@ def _is_membership_change_allowed(
if authorising_user_level < invite_level:
raise AuthError(403, "Join event authorised by invalid server.")

elif join_rule == JoinRules.INVITE or (
room_version.msc2403_knocking and join_rule == JoinRules.KNOCK
elif is_join_rule(JoinRules.INVITE) or (
room_version.msc2403_knocking and is_join_rule(JoinRules.KNOCK)
):
if not caller_in_room and not caller_invited:
raise AuthError(403, "You are not invited to this room.")
Expand All @@ -462,7 +462,7 @@ def _is_membership_change_allowed(
if user_level < ban_level or user_level <= target_level:
raise AuthError(403, "You don't have permission to ban")
elif room_version.msc2403_knocking and Membership.KNOCK == membership:
if join_rule != JoinRules.KNOCK:
if not is_join_rule(JoinRules.KNOCK):
raise AuthError(403, "You don't have permission to knock")
elif target_user_id != event.user_id:
raise AuthError(403, "You cannot knock for other users")
Expand Down
5 changes: 5 additions & 0 deletions synapse/events/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ def add_fields(*fields: str) -> None:
add_fields(EventContentFields.MSC2716_BATCH_ID)
elif room_version.msc2716_redactions and event_type == EventTypes.MSC2716_MARKER:
add_fields(EventContentFields.MSC2716_MARKER_INSERTION)
elif (
room_version.msc3613_simplified_join_rules
and event_type == EventTypes.JoinRules
):
add_fields("join_rules")

allowed_fields = {k: v for k, v in event_dict.items() if k in allowed_keys}

Expand Down
14 changes: 9 additions & 5 deletions synapse/handlers/event_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from synapse.events.builder import EventBuilder
from synapse.events.snapshot import EventContext
from synapse.types import StateMap, get_domain_from_id
from synapse.util.join_rules import get_all_allow_lists, is_join_rule
from synapse.util.metrics import Measure

if TYPE_CHECKING:
Expand Down Expand Up @@ -198,7 +199,7 @@ async def check_restricted_join_rules(

# 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 = await self.get_rooms_that_allow_join(state_ids, room_version)
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
Expand Down Expand Up @@ -241,16 +242,19 @@ async def has_restricted_join_rules(

# If the join rule is not restricted, this doesn't apply.
join_rules_event = await self._store.get_event(join_rules_event_id)
return join_rules_event.content.get("join_rule") == JoinRules.RESTRICTED
return is_join_rule(room_version, join_rules_event, JoinRules.RESTRICTED)

async def get_rooms_that_allow_join(
self, state_ids: StateMap[str]
self,
state_ids: StateMap[str],
room_version: RoomVersion,
) -> Collection[str]:
"""
Generate a list of rooms in which membership allows access to a room.

Args:
state_ids: The current state of the room the user wishes to join
room_version: The version of the room

Returns:
A collection of room IDs. Membership in any of the rooms in the list grants the ability to join the target room.
Expand All @@ -264,8 +268,8 @@ async def get_rooms_that_allow_join(
join_rules_event = await self._store.get_event(join_rules_event_id)

# 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):
allow_list = get_all_allow_lists(room_version, join_rules_event)
if allow_list is None:
return ()

# Pull out the other room IDs, invalid data gets filtered.
Expand Down
9 changes: 6 additions & 3 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from synapse.spam_checker_api import RegistrationBehaviour
from synapse.storage.state import StateFilter
from synapse.types import RoomAlias, UserID, create_requester
from synapse.util.join_rules import is_join_rule

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand Down Expand Up @@ -538,9 +539,11 @@ async def _join_rooms(self, user_id: str) -> None:
event_id, allow_none=True
)
if join_rules_event:
join_rule = join_rules_event.content.get("join_rule", None)
requires_invite = (
join_rule and join_rule != JoinRules.PUBLIC
room_version = await self.store.get_room_version(r)
requires_invite = not is_join_rule(
room_version,
join_rules_event,
JoinRules.PUBLIC,
)

# Send the invite, if necessary.
Expand Down
10 changes: 8 additions & 2 deletions synapse/handlers/room_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from synapse.types import JsonDict, ThirdPartyInstanceID
from synapse.util.caches.descriptors import _CacheContext, cached
from synapse.util.caches.response_cache import ResponseCache
from synapse.util.join_rules import is_join_rule

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand Down Expand Up @@ -306,8 +307,13 @@ async def generate_room_entry(

join_rules_event = current_state.get((EventTypes.JoinRules, ""))
if join_rules_event:
join_rule = join_rules_event.content.get("join_rule", None)
if not allow_private and join_rule and join_rule != JoinRules.PUBLIC:
room_version = await self.store.get_room_version(room_id)
is_public = is_join_rule(
room_version,
join_rules_event,
JoinRules.PUBLIC,
)
if not allow_private and not is_public:
return None

# Return whether this room is open to federation users or not
Expand Down
3 changes: 3 additions & 0 deletions synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,9 @@ async def _make_and_store_3pid_invite(
if room_create_event:
room_type = room_create_event.content.get(EventContentFields.ROOM_TYPE)

# Note: for email invites we use the backwards compatible `join_rule`
# as it is meant to represent the "most semantically relevant" join
# rule for the room. See MSC2613 for details.
room_join_rules = ""
join_rules_event = room_state.get((EventTypes.JoinRules, ""))
if join_rules_event:
Expand Down
23 changes: 16 additions & 7 deletions synapse/handlers/room_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from synapse.events import EventBase
from synapse.types import JsonDict, Requester
from synapse.util.caches.response_cache import ResponseCache
from synapse.util.join_rules import is_join_rule

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand Down Expand Up @@ -858,10 +859,9 @@ async def _is_local_room_accessible(
join_rules_event_id = state_ids.get((EventTypes.JoinRules, ""))
if join_rules_event_id:
join_rules_event = await self._store.get_event(join_rules_event_id)
join_rule = join_rules_event.content.get("join_rule")
if join_rule == JoinRules.PUBLIC or (
room_version.msc2403_knocking and join_rule == JoinRules.KNOCK
):
is_public = is_join_rule(room_version, join_rules_event, JoinRules.PUBLIC)
is_knock = is_join_rule(room_version, join_rules_event, JoinRules.KNOCK)
if is_public or (room_version.msc2403_knocking and is_knock):
return True

# Include the room if it is peekable.
Expand Down Expand Up @@ -890,7 +890,9 @@ async def _is_local_room_accessible(
state_ids, room_version
):
allowed_rooms = (
await self._event_auth_handler.get_rooms_that_allow_join(state_ids)
await self._event_auth_handler.get_rooms_that_allow_join(
state_ids, room_version
)
)
if await self._event_auth_handler.is_user_in_rooms(
allowed_rooms, requester
Expand All @@ -912,7 +914,9 @@ async def _is_local_room_accessible(
state_ids, room_version
):
allowed_rooms = (
await self._event_auth_handler.get_rooms_that_allow_join(state_ids)
await self._event_auth_handler.get_rooms_that_allow_join(
state_ids, room_version
)
)
for space_id in allowed_rooms:
if await self._event_auth_handler.check_host_in_room(
Expand Down Expand Up @@ -952,7 +956,12 @@ async def _is_remote_room_accessible(
"""
# The API doesn't return the room version so assume that a
# join rule of knock is valid.
#
# Note: We're relying on the backwards compatible `join_rule`
# for the room as it is meant to reference the "most semantically
# relevant" join rule. See MSC3613 for details.
if (
# TODO: Use is_join_rule utility
room.get("join_rules") in (JoinRules.PUBLIC, JoinRules.KNOCK)
or room.get("world_readable") is True
):
Expand Down Expand Up @@ -1023,7 +1032,7 @@ async def _build_room_entry(self, room_id: str, for_federation: bool) -> JsonDic
):
allowed_rooms = (
await self._event_auth_handler.get_rooms_that_allow_join(
current_state_ids
current_state_ids, room_version
)
)
if allowed_rooms:
Expand Down
Loading