mirror of
https://github.com/element-hq/synapse.git
synced 2026-11-04 06:57:20 +00:00
Persist the join first when the initial state has m.room.aliases
With `require_membership_for_aliases` (the default), `create_event` checks that the sender of an `m.room.aliases` event has joined, by loading their membership event from the database. Since the initial events of a room are persisted in one batch, the creator's join is not there yet, so a `/createRoom` with `m.room.aliases` in its initial state failed with 403 "You must be in the room to create an alias for it". Persist the create event and the join on their own first in that case, as for v1 rooms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVLsNh6akt4ifEizY4D1ta
This commit is contained in:
1 parent
b7b4e04de3
commit
a486744648
2 files changed
+35
-10
No files matched your search
+18
-10
@@ -1320,6 +1320,12 @@ class RoomCreationHandler:
|
||||
if reason and isinstance(reason, str):
|
||||
invite_content["reason"] = reason
|
||||
|
||||
raw_initial_state = config.get("initial_state", [])
|
||||
|
||||
initial_state = OrderedDict()
|
||||
for val in raw_initial_state:
|
||||
initial_state[(val["type"], val.get("state_key", ""))] = val["content"]
|
||||
|
||||
# Local invites go into the batch with the room's other initial events,
|
||||
# in order, up to the first one that has to be sent on its own: an
|
||||
# invite to a remote user, which needs a federation request first, or
|
||||
@@ -1331,7 +1337,7 @@ class RoomCreationHandler:
|
||||
# The invites to send on their own after the batch.
|
||||
remaining_invites = invite_list
|
||||
if not (
|
||||
self._persist_create_and_join_alone(room_version)
|
||||
self._persist_create_and_join_alone(room_version, initial_state)
|
||||
or self._spam_checker_module_callbacks.has_user_may_invite_callbacks()
|
||||
):
|
||||
for index, invitee in enumerate(invite_list):
|
||||
@@ -1352,12 +1358,6 @@ class RoomCreationHandler:
|
||||
else:
|
||||
remaining_invites = []
|
||||
|
||||
raw_initial_state = config.get("initial_state", [])
|
||||
|
||||
initial_state = OrderedDict()
|
||||
for val in raw_initial_state:
|
||||
initial_state[(val["type"], val.get("state_key", ""))] = val["content"]
|
||||
|
||||
(
|
||||
last_stream_id,
|
||||
last_sent_event_id,
|
||||
@@ -1698,7 +1698,7 @@ class RoomCreationHandler:
|
||||
current_state_group = events_and_context[-1][1].state_group
|
||||
|
||||
persist_create_and_join_alone = self._persist_create_and_join_alone(
|
||||
room_version
|
||||
room_version, initial_state
|
||||
)
|
||||
assert not (persist_create_and_join_alone and batched_invites)
|
||||
|
||||
@@ -1927,18 +1927,26 @@ class RoomCreationHandler:
|
||||
len(invited_users),
|
||||
)
|
||||
|
||||
def _persist_create_and_join_alone(self, room_version: RoomVersion) -> bool:
|
||||
def _persist_create_and_join_alone(
|
||||
self, room_version: RoomVersion, initial_state: StateMap[Any]
|
||||
) -> bool:
|
||||
"""Whether a new room's create event and the creator's join must each
|
||||
be persisted on their own, before the next event is built.
|
||||
|
||||
Events in v1 and v2 rooms refer to their prev and auth events by hash,
|
||||
which the builder reads from the database. `check_event_allowed`
|
||||
callbacks are given the room state from the database, and rebuilding an
|
||||
event they change reads its prev events from the database too.
|
||||
event they change reads its prev events from the database too. An
|
||||
`m.room.aliases` event in the initial state needs the sender's join in
|
||||
the database when `require_membership_for_aliases` is set.
|
||||
"""
|
||||
return (
|
||||
room_version.event_format == EventFormatVersions.ROOM_V1_V2
|
||||
or self._third_party_event_rules.has_check_event_allowed_callbacks()
|
||||
or (
|
||||
self.event_creation_handler.require_membership_for_aliases
|
||||
and any(etype == EventTypes.Aliases for etype, _ in initial_state)
|
||||
)
|
||||
)
|
||||
|
||||
def _validate_room_config(
|
||||
|
||||
@@ -427,6 +427,23 @@ class CreateRoomPersistBatchesTestCase(unittest.HomeserverTestCase):
|
||||
)
|
||||
self._check_room(room_id)
|
||||
|
||||
def test_aliases_in_initial_state(self) -> None:
|
||||
"""An `m.room.aliases` event in the initial state needs the creator's
|
||||
join in the database, so the create event and the join go first."""
|
||||
self.batches.clear()
|
||||
self.helper.create_room_as(
|
||||
self.user_id,
|
||||
tok=self.tok,
|
||||
room_version="10",
|
||||
extra_content={
|
||||
"initial_state": [
|
||||
{"type": EventTypes.Aliases, "state_key": "test", "content": {}}
|
||||
]
|
||||
},
|
||||
)
|
||||
self.assertEqual(self.batches[:2], [[EventTypes.Create], [EventTypes.Member]])
|
||||
self.assertIn(EventTypes.Aliases, self.batches[2])
|
||||
|
||||
def test_check_event_allowed_callback(self) -> None:
|
||||
"""With a `check_event_allowed` callback, the create event and the join
|
||||
are persisted on their own. A callback that rewrites them leaves no
|
||||
|
||||
Reference in new issue
Block a user