diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index e817e4f020..fe3f22fc6d 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -1663,6 +1663,22 @@ class FederationHandler: async def do_remotely_reject_invite( self, target_hosts: Iterable[str], room_id: str, user_id: str, content: JsonDict ) -> tuple[EventBase, int]: + """Leave a room we are not resident in, via the make_leave/send_leave dance. + + Used both to reject an out-of-band invite and, under MSC4233, to + rescind an out-of-band knock through the server the knock was + fulfilled through: the two are the same federation dance and differ + only in which hosts we are given. + + Args: + target_hosts: The servers to try, in order. + room_id: The room to leave. + user_id: The local user leaving. + content: The content for the leave event. + + Returns: + The persisted leave event and its stream ID. + """ origin, event, room_version = await self._make_and_verify_event( target_hosts, room_id, user_id, "leave", content=content ) diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index 43eb29989e..3f5ab689b5 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -2098,13 +2098,37 @@ class RoomMemberMasterHandler(RoomMemberHandler): Implements RoomMemberHandler.remote_rescind_knock """ - # TODO: We don't yet support rescinding knocks over federation - # as we don't know which homeserver to send it to. An obvious - # candidate is the remote homeserver we originally knocked through, - # however we don't currently store that information. - - # Just rescind the knock locally knock_event = await self.store.get_event(knock_event_id) + + if self.hs.config.experimental.msc4233_enabled: + # MSC4233: route the rescission through the server the knock was + # fulfilled through, which we remembered at knock time. + via_server = await self.store.get_local_knock_via_server( + knock_event.state_key, knock_event.room_id, knock_event_id + ) + if via_server is not None: + try: + ( + event, + stream_id, + ) = await self.federation_handler.do_remotely_reject_invite( + [via_server], + knock_event.room_id, + knock_event.state_key, + content, + ) + return event.event_id, stream_id + except Exception as e: + # MSC4233 makes the local rescission below the fallback + # for any failure of the dance, from DNS errors upwards. + logger.warning( + "Failed to rescind knock on %s via %s: %s", + knock_event.room_id, + via_server, + e, + ) + + # Rescind the knock locally return await self._generate_local_out_of_band_leave( knock_event, txn_id, requester, content )