summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Cloke <patrickc@matrix.org>2023-07-13 08:09:13 -0400
committerPatrick Cloke <patrickc@matrix.org>2023-07-17 11:05:44 -0400
commitdcd3d5cdc677f46eed004179859381284e6a4cf1 (patch)
tree3bf57a6103d80dc59cc19e4725e51007b0eb436e
parentAccept LPDUs in transactions and fan them back out. (diff)
downloadsynapse-dcd3d5cdc677f46eed004179859381284e6a4cf1.tar.xz
Provide templated LPDUs for make_{join,leave,knock} requests.
-rw-r--r--synapse/events/__init__.py18
-rw-r--r--synapse/federation/federation_server.py23
-rw-r--r--synapse/handlers/federation.py26
-rw-r--r--tests/handlers/test_federation.py20
4 files changed, 60 insertions, 27 deletions
diff --git a/synapse/events/__init__.py b/synapse/events/__init__.py
index 99b9d61847..f2f8e1a935 100644
--- a/synapse/events/__init__.py
+++ b/synapse/events/__init__.py
@@ -629,6 +629,24 @@ class FrozenLinearizedEvent(FrozenEventV3):
         pdu.pop("prev_events")
         return pdu
 
+    def get_templated_pdu_json(self) -> JsonDict:
+        """
+        Return a JSON object suitable for a templated event, as used in the
+        make_{join,leave,knock} workflow.
+        """
+        # By using _dict directly we don't pull in signatures/unsigned.
+        template_json = dict(self._dict)
+        # The hashes (similar to the signature) need to be recalculated by the
+        # joining/leaving/knocking server after (potentially) modifying the
+        # event.
+        template_json.pop("hashes")
+
+        # Linearized Matrix servers don't know about auth/prev events.
+        template_json.pop("auth_events")
+        template_json.pop("prev_events")
+
+        return template_json
+
 
 def _event_type_from_format_version(
     format_version: int,
diff --git a/synapse/federation/federation_server.py b/synapse/federation/federation_server.py
index 61ed41c903..6e5fa82a10 100644
--- a/synapse/federation/federation_server.py
+++ b/synapse/federation/federation_server.py
@@ -675,12 +675,14 @@ class FederationServer(FederationBase):
         origin_host, _ = parse_server_name(origin)
         await self.check_server_matches_acl(origin_host, room_id)
 
-        room_version = await self.store.get_room_version_id(room_id)
-        if room_version not in supported_versions:
+        # checking the room version will check that we've actually heard of the room
+        # (and return a 404 otherwise)
+        room_version_id = await self.store.get_room_version_id(room_id)
+        if room_version_id not in supported_versions:
             logger.warning(
-                "Room version %s not in %s", room_version, supported_versions
+                "Room version %s not in %s", room_version_id, supported_versions
             )
-            raise IncompatibleRoomVersionError(room_version=room_version)
+            raise IncompatibleRoomVersionError(room_version=room_version_id)
 
         # Refuse the request if that room has seen too many joins recently.
         # This is in addition to the HS-level rate limiting applied by
@@ -691,8 +693,10 @@ class FederationServer(FederationBase):
             key=room_id,
             update=False,
         )
-        pdu = await self.handler.on_make_join_request(origin, room_id, user_id)
-        return {"event": pdu.get_templated_pdu_json(), "room_version": room_version}
+        pdu = await self.handler.on_make_join_request(
+            origin, room_id, KNOWN_ROOM_VERSIONS[room_version_id], user_id
+        )
+        return {"event": pdu.get_templated_pdu_json(), "room_version": room_version_id}
 
     async def on_invite_request(
         self, origin: str, content: JsonDict, room_version_id: str
@@ -960,7 +964,12 @@ class FederationServer(FederationBase):
                 errcode=Codes.FORBIDDEN,
             )
 
-        event = event_from_pdu_json(content, room_version)
+        # Linearized Matrix requires building the event (i.e. adding auth/prev
+        # events). The input content is an LPDU.
+        if room_version.linearized_matrix:
+            event = await self._on_lpdu_event(content, room_version)
+        else:
+            event = event_from_pdu_json(content, room_version)
 
         if event.type != EventTypes.Member or not event.is_state():
             raise SynapseError(400, "Not an m.room.member event", Codes.BAD_JSON)
diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py
index d90a14788d..84ad276ebd 100644
--- a/synapse/handlers/federation.py
+++ b/synapse/handlers/federation.py
@@ -899,7 +899,7 @@ class FederationHandler:
                 )
 
     async def on_make_join_request(
-        self, origin: str, room_id: str, user_id: str
+        self, origin: str, room_id: str, room_version: RoomVersion, user_id: str
     ) -> EventBase:
         """We've received a /make_join/ request, so we create a partial
         join event for the room and return that. We do *not* persist or
@@ -908,6 +908,7 @@ class FederationHandler:
         Args:
             origin: The (verified) server name of the requesting server.
             room_id: Room to create join event in
+            room_version: The room's room version.
             user_id: The user to create the join for
         """
         if get_domain_from_id(user_id) != origin:
@@ -918,10 +919,6 @@ class FederationHandler:
             )
             raise SynapseError(403, "User not from origin", Codes.FORBIDDEN)
 
-        # checking the room version will check that we've actually heard of the room
-        # (and return a 404 otherwise)
-        room_version = await self.store.get_room_version(room_id)
-
         if await self.store.is_partial_state_room(room_id):
             # If our server is still only partially joined, we can't give a complete
             # response to /make_join, so return a 404 as we would if we weren't in the
@@ -997,16 +994,15 @@ class FederationHandler:
                         state_ids,
                     )
 
-        builder = self.event_builder_factory.for_room_version(
-            room_version,
-            {
-                "type": EventTypes.Member,
-                "content": event_content,
-                "room_id": room_id,
-                "sender": user_id,
-                "state_key": user_id,
-            },
-        )
+        event_dict = {
+            "type": EventTypes.Member,
+            "content": event_content,
+            "room_id": room_id,
+            "sender": user_id,
+            "state_key": user_id,
+        }
+
+        builder = self.event_builder_factory.for_room_version(room_version, event_dict)
 
         try:
             (
diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py
index bf0862ed54..e5f67693b8 100644
--- a/tests/handlers/test_federation.py
+++ b/tests/handlers/test_federation.py
@@ -27,7 +27,7 @@ from synapse.api.errors import (
     NotFoundError,
     SynapseError,
 )
-from synapse.api.room_versions import RoomVersions
+from synapse.api.room_versions import RoomVersion, RoomVersions
 from synapse.events import EventBase, make_event_from_dict
 from synapse.federation.federation_base import event_from_pdu_json
 from synapse.federation.federation_client import SendJoinResult
@@ -124,7 +124,9 @@ class FederationTestCase(unittest.FederatingHomeserverTestCase):
         room_version = self.get_success(self.store.get_room_version(room_id))
 
         # pretend that another server has joined
-        join_event = self._build_and_send_join_event(OTHER_SERVER, OTHER_USER, room_id)
+        join_event = self._build_and_send_join_event(
+            OTHER_SERVER, OTHER_USER, room_id, room_version
+        )
 
         # check the state group
         sg = self.get_success(
@@ -177,7 +179,9 @@ class FederationTestCase(unittest.FederatingHomeserverTestCase):
         room_version = self.get_success(self.store.get_room_version(room_id))
 
         # pretend that another server has joined
-        join_event = self._build_and_send_join_event(OTHER_SERVER, OTHER_USER, room_id)
+        join_event = self._build_and_send_join_event(
+            OTHER_SERVER, OTHER_USER, room_id, room_version
+        )
 
         # check the state group
         sg = self.get_success(
@@ -479,10 +483,16 @@ class FederationTestCase(unittest.FederatingHomeserverTestCase):
         )
 
     def _build_and_send_join_event(
-        self, other_server: str, other_user: str, room_id: str
+        self,
+        other_server: str,
+        other_user: str,
+        room_id: str,
+        room_version: RoomVersion,
     ) -> EventBase:
         join_event = self.get_success(
-            self.handler.on_make_join_request(other_server, room_id, other_user)
+            self.handler.on_make_join_request(
+                other_server, room_id, room_version, other_user
+            )
         )
         # the auth code requires that a signature exists, but doesn't check that
         # signature... go figure.