diff --git a/changelog.d/17332.feature b/changelog.d/17332.feature new file mode 100644 index 0000000000..0b863f1a0d --- /dev/null +++ b/changelog.d/17332.feature @@ -0,0 +1 @@ +Add implementation of owned state events as proposed by [MSC3779](https://github.com/matrix-org/matrix-spec-proposals/pull/3779). diff --git a/synapse/api/constants.py b/synapse/api/constants.py index 9265a271d2..cb032d4c37 100644 --- a/synapse/api/constants.py +++ b/synapse/api/constants.py @@ -132,6 +132,23 @@ class EventTypes: CallInvite: Final = "m.call.invite" +ZERO_LENGTH_STATE_KEY_EVENT_TYPES = { + EventTypes.CanonicalAlias, + EventTypes.Create, + EventTypes.JoinRules, + EventTypes.PowerLevels, + EventTypes.Name, + EventTypes.Topic, + EventTypes.RoomAvatar, + EventTypes.Pinned, + EventTypes.RoomEncryption, + EventTypes.RoomHistoryVisibility, + EventTypes.GuestAccess, + EventTypes.ServerACL, + EventTypes.Tombstone, +} + + class ToDeviceEventTypes: RoomKeyRequest: Final = "m.room_key_request" diff --git a/synapse/api/room_versions.py b/synapse/api/room_versions.py index fbc1d58ecb..f83fb8fc48 100644 --- a/synapse/api/room_versions.py +++ b/synapse/api/room_versions.py @@ -320,6 +320,26 @@ class RoomVersions: enforce_int_power_levels=True, msc3931_push_features=(PushRuleRoomFlag.EXTENSIBLE_EVENTS,), ) + MSC3779v10 = RoomVersion( + # MSC3779 ("Owned" state events) based on room version "10" + "org.matrix.msc3779.10", + RoomDisposition.UNSTABLE, + EventFormatVersions.ROOM_V4_PLUS, + StateResolutionVersions.V2, + enforce_key_validity=True, + special_case_aliases_auth=False, + strict_canonicaljson=True, + limit_notifications_power_levels=True, + implicit_room_creator=False, + updated_redaction_rules=False, + restricted_join_rule=True, + restricted_join_rule_fix=True, + knock_join_rule=True, + msc3389_relation_redactions=False, + knock_restricted_join_rule=True, + enforce_int_power_levels=True, + msc3931_push_features=(), + ) V11 = RoomVersion( "11", RoomDisposition.STABLE, @@ -355,6 +375,7 @@ KNOWN_ROOM_VERSIONS: Dict[str, RoomVersion] = { RoomVersions.V9, RoomVersions.V10, RoomVersions.V11, + RoomVersions.MSC3779v10, ) } diff --git a/synapse/event_auth.py b/synapse/event_auth.py index f5abcde2db..0b2c741398 100644 --- a/synapse/event_auth.py +++ b/synapse/event_auth.py @@ -46,6 +46,7 @@ from unpaddedbase64 import decode_base64 from synapse.api.constants import ( MAX_PDU_SIZE, + ZERO_LENGTH_STATE_KEY_EVENT_TYPES, EventContentFields, EventTypes, JoinRules, @@ -388,6 +389,7 @@ LENIENT_EVENT_BYTE_LIMITS_ROOM_VERSIONS = { RoomVersions.V9, RoomVersions.V10, RoomVersions.MSC1767v10, + RoomVersions.MSC3779v10, } @@ -753,7 +755,10 @@ def _check_joined_room( def get_send_level( - etype: str, state_key: Optional[str], power_levels_event: Optional["EventBase"] + etype: str, + state_key: Optional[str], + power_levels_event: Optional["EventBase"], + msc_3779_sender: Optional[str] = None, ) -> int: """Get the power level required to send an event of a given type @@ -767,6 +772,8 @@ def get_send_level( a state event. power_levels_event: power levels event in force at this point in the room + msc_3779_sender: MXID of the user who sent the event. + Only for rooms with MSC3779 ("owned" state events) applied. Returns: power level required to send this event. """ @@ -781,7 +788,16 @@ def get_send_level( # otherwise, fall back to the state_default/events_default. if send_level is None: - if state_key is not None: + is_owned_state_event = ( + msc_3779_sender is not None + and state_key is not None + and ( + state_key == msc_3779_sender + or state_key.startswith(msc_3779_sender + "_") + ) + and etype not in ZERO_LENGTH_STATE_KEY_EVENT_TYPES + ) + if state_key is not None and not is_owned_state_event: send_level = power_levels_content.get("state_default", 50) else: send_level = power_levels_content.get("events_default", 0) @@ -792,7 +808,13 @@ def get_send_level( def _can_send_event(event: "EventBase", auth_events: StateMap["EventBase"]) -> bool: power_levels_event = get_power_level_event(auth_events) - send_level = get_send_level(event.type, event.get("state_key"), power_levels_event) + use_msc3779 = event.room_version is RoomVersions.MSC3779v10 + send_level = get_send_level( + event.type, + event.get("state_key"), + power_levels_event, + event.user_id if use_msc3779 else None, + ) user_level = get_user_power_level(event.user_id, auth_events) if user_level < send_level: @@ -806,7 +828,9 @@ def _can_send_event(event: "EventBase", auth_events: StateMap["EventBase"]) -> b # Check state_key if hasattr(event, "state_key"): if event.state_key.startswith("@"): - if event.state_key != event.user_id: + if event.state_key != event.user_id and not ( + use_msc3779 and event.state_key.startswith(event.user_id + "_") + ): raise AuthError(403, "You are not allowed to set others state") return True