Skip to content

Commit 4b43e6f

Browse files
authored
Handle rescinding invites over federation (#18823)
We should send events that rescind invites over federation. Similarly, we should handle receiving such events. Unfortunately, the protocol doesn't make it possible to fully auth such events, and so we can only handle the case where the original inviter rescinded the invite (rather than a room admin). Complement test: matrix-org/complement#797
1 parent b2997a8 commit 4b43e6f

File tree

3 files changed

+68
-3
lines changed

3 files changed

+68
-3
lines changed

changelog.d/18823.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix bug where we did not send invite revocations over federation.

synapse/federation/sender/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150
from twisted.internet import defer
151151

152152
import synapse.metrics
153+
from synapse.api.constants import EventTypes, Membership
153154
from synapse.api.presence import UserPresenceState
154155
from synapse.events import EventBase
155156
from synapse.federation.sender.per_destination_queue import (
@@ -655,6 +656,31 @@ async def handle_event(event: EventBase) -> None:
655656
)
656657
return
657658

659+
# If we've rescinded an invite then we want to tell the
660+
# other server.
661+
if (
662+
event.type == EventTypes.Member
663+
and event.membership == Membership.LEAVE
664+
and event.sender != event.state_key
665+
):
666+
# We check if this leave event is rescinding an invite
667+
# by looking if there is an invite event for the user in
668+
# the auth events. It could otherwise be a kick or
669+
# unban, which we don't want to send (if the user wasn't
670+
# already in the room).
671+
auth_events = await self.store.get_events_as_list(
672+
event.auth_event_ids()
673+
)
674+
for auth_event in auth_events:
675+
if (
676+
auth_event.type == EventTypes.Member
677+
and auth_event.state_key == event.state_key
678+
and auth_event.membership == Membership.INVITE
679+
):
680+
destinations = set(destinations)
681+
destinations.add(get_domain_from_id(event.state_key))
682+
break
683+
658684
sharded_destinations = {
659685
d
660686
for d in destinations

synapse/handlers/federation_event.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,16 +248,54 @@ async def on_receive_pdu(self, origin: str, pdu: EventBase) -> None:
248248
self.room_queues[room_id].append((pdu, origin))
249249
return
250250

251-
# If we're not in the room just ditch the event entirely. This is
252-
# probably an old server that has come back and thinks we're still in
253-
# the room (or we've been rejoined to the room by a state reset).
251+
# If we're not in the room just ditch the event entirely (and not
252+
# invited). This is probably an old server that has come back and thinks
253+
# we're still in the room (or we've been rejoined to the room by a state
254+
# reset).
254255
#
255256
# Note that if we were never in the room then we would have already
256257
# dropped the event, since we wouldn't know the room version.
257258
is_in_room = await self._event_auth_handler.is_host_in_room(
258259
room_id, self.server_name
259260
)
260261
if not is_in_room:
262+
# Check if this is a leave event rescinding an invite
263+
if (
264+
pdu.type == EventTypes.Member
265+
and pdu.membership == Membership.LEAVE
266+
and pdu.state_key != pdu.sender
267+
and self._is_mine_id(pdu.state_key)
268+
):
269+
(
270+
membership,
271+
membership_event_id,
272+
) = await self._store.get_local_current_membership_for_user_in_room(
273+
pdu.state_key, pdu.room_id
274+
)
275+
if (
276+
membership == Membership.INVITE
277+
and membership_event_id
278+
and membership_event_id
279+
in pdu.auth_event_ids() # The invite should be in the auth events of the rescission.
280+
):
281+
invite_event = await self._store.get_event(
282+
membership_event_id, allow_none=True
283+
)
284+
285+
# We cannot fully auth the rescission event, but we can
286+
# check if the sender of the leave event is the same as the
287+
# invite.
288+
#
289+
# Technically, a room admin could rescind the invite, but we
290+
# have no way of knowing who is and isn't a room admin.
291+
if invite_event and pdu.sender == invite_event.sender:
292+
# Handle the rescission event
293+
pdu.internal_metadata.outlier = True
294+
pdu.internal_metadata.out_of_band_membership = True
295+
context = EventContext.for_outlier(self._storage_controllers)
296+
await self.persist_events_and_notify(room_id, [(pdu, context)])
297+
return
298+
261299
logger.info(
262300
"Ignoring PDU from %s as we're not in the room",
263301
origin,

0 commit comments

Comments
 (0)