From 08e1b63b30d889ba16f94d286b453aa79b0fead4 Mon Sep 17 00:00:00 2001 From: Devon Hudson Date: Mon, 1 Dec 2025 18:26:43 +0000 Subject: [PATCH] Fix v12 rooms when using frozen dicts (#19235) Fix #19233 Synapse fails to handle events in v12 rooms when the server is run with the `{use_frozen_dicts: True}` config. This PR fixes the issue, and adds tests which cover room creation, joining, and joining over federation, with both frozen and not frozen config settings, by extending the existing `test_send_join` federation tests. This approach to testing was chosen as it is a simple way to get high level integration style test coverage, without going through all our existing tests and trying to retroactively add in coverage when using frozen dicts. This should provide an easy place for future room versions to extend the suite of tests and reduce the chance of introducing subtle bugs like this in the future. ### Pull Request Checklist * [x] Pull request is based on the develop branch * [x] Pull request includes a [changelog file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog). The entry should: - Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from `EventStore` to `EventWorkerStore`.". - Use markdown where necessary, mostly for `code blocks`. - End with either a period (.) or an exclamation mark (!). - Start with a capital letter. - Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry. * [x] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct (run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters)) --- changelog.d/19235.bugfix | 1 + synapse/events/__init__.py | 2 +- tests/federation/test_federation_server.py | 58 ++++++++++++++++++---- 3 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 changelog.d/19235.bugfix diff --git a/changelog.d/19235.bugfix b/changelog.d/19235.bugfix new file mode 100644 index 0000000000..1c312351a4 --- /dev/null +++ b/changelog.d/19235.bugfix @@ -0,0 +1 @@ +Fix v12 rooms when running with `use_frozen_dicts: True`. diff --git a/synapse/events/__init__.py b/synapse/events/__init__.py index 5f78603782..c7eaf7eda2 100644 --- a/synapse/events/__init__.py +++ b/synapse/events/__init__.py @@ -548,7 +548,7 @@ class FrozenEventV4(FrozenEventV3): assert create_event_id not in self._dict["auth_events"] if self.type == EventTypes.Create and self.get_state_key() == "": return self._dict["auth_events"] # should be [] - return self._dict["auth_events"] + [create_event_id] + return [*self._dict["auth_events"], create_event_id] def _event_type_from_format_version( diff --git a/tests/federation/test_federation_server.py b/tests/federation/test_federation_server.py index 0d74791290..c4491d5b3c 100644 --- a/tests/federation/test_federation_server.py +++ b/tests/federation/test_federation_server.py @@ -30,6 +30,7 @@ from synapse.api.constants import EventTypes, Membership from synapse.api.errors import FederationError from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersions from synapse.config.server import DEFAULT_ROOM_VERSION +from synapse.crypto.event_signing import add_hashes_and_signatures from synapse.events import EventBase, make_event_from_dict from synapse.federation.federation_base import event_from_pdu_json from synapse.http.types import QueryParams @@ -356,19 +357,44 @@ class SendJoinFederationTests(unittest.FederatingHomeserverTestCase): self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body) return channel.json_body - def test_send_join(self) -> None: + def _test_send_join_common(self, room_version: str) -> None: """happy-path test of send_join""" - joining_user = "@misspiggy:" + self.OTHER_SERVER_NAME - join_result = self._make_join(joining_user) + creator_user_id = self.register_user(f"kermit_v{room_version}", "test") + tok = self.login(f"kermit_v{room_version}", "test") + room_id = self.helper.create_room_as( + room_creator=creator_user_id, tok=tok, room_version=room_version + ) + # Second member joins + second_member_user_id = self.register_user(f"fozzie_v{room_version}", "bear") + tok2 = self.login(f"fozzie_v{room_version}", "bear") + self.helper.join(room_id, second_member_user_id, tok=tok2) + + # Make join for remote user + joining_user = "@misspiggy:" + self.OTHER_SERVER_NAME + channel = self.make_signed_federation_request( + "GET", + f"/_matrix/federation/v1/make_join/{room_id}/{joining_user}?ver={room_version}", + ) + self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body) + join_result = channel.json_body + + # Sign and send the join join_event_dict = join_result["event"] self.add_hashes_and_signatures_from_other_server( join_event_dict, - KNOWN_ROOM_VERSIONS[DEFAULT_ROOM_VERSION], + KNOWN_ROOM_VERSIONS[room_version], ) + if room_version in ["1", "2"]: + add_hashes_and_signatures( + KNOWN_ROOM_VERSIONS[room_version], + join_event_dict, + signature_name=self.hs.hostname, + signing_key=self.hs.signing_key, + ) channel = self.make_signed_federation_request( "PUT", - f"/_matrix/federation/v2/send_join/{self._room_id}/x", + f"/_matrix/federation/v2/send_join/{room_id}/x", content=join_event_dict, ) self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body) @@ -384,8 +410,8 @@ class SendJoinFederationTests(unittest.FederatingHomeserverTestCase): ("m.room.power_levels", ""), ("m.room.join_rules", ""), ("m.room.history_visibility", ""), - ("m.room.member", "@kermit:test"), - ("m.room.member", "@fozzie:test"), + ("m.room.member", f"@kermit_v{room_version}:test"), + ("m.room.member", f"@fozzie_v{room_version}:test"), # nb: *not* the joining user ], ) @@ -398,18 +424,28 @@ class SendJoinFederationTests(unittest.FederatingHomeserverTestCase): returned_auth_chain_events, [ ("m.room.create", ""), - ("m.room.member", "@kermit:test"), + ("m.room.member", f"@kermit_v{room_version}:test"), ("m.room.power_levels", ""), ("m.room.join_rules", ""), ], ) # the room should show that the new user is a member - r = self.get_success( - self._storage_controllers.state.get_current_state(self._room_id) - ) + r = self.get_success(self._storage_controllers.state.get_current_state(room_id)) self.assertEqual(r[("m.room.member", joining_user)].membership, "join") + @parameterized.expand([(k,) for k in KNOWN_ROOM_VERSIONS.keys()]) + @override_config({"use_frozen_dicts": True}) + def test_send_join_with_frozen_dicts(self, room_version: str) -> None: + """Test send_join with USE_FROZEN_DICTS=True""" + self._test_send_join_common(room_version) + + @parameterized.expand([(k,) for k in KNOWN_ROOM_VERSIONS.keys()]) + @override_config({"use_frozen_dicts": False}) + def test_send_join_without_frozen_dicts(self, room_version: str) -> None: + """Test send_join with USE_FROZEN_DICTS=False""" + self._test_send_join_common(room_version) + def test_send_join_partial_state(self) -> None: """/send_join should return partial state, if requested""" joining_user = "@misspiggy:" + self.OTHER_SERVER_NAME