Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit c06b2b7

Browse files
authored
Faster Remote Room Joins: tell remote homeservers that we are unable to authorise them if they query a room which has partial state on our server. (#13823)
1 parent ac7e568 commit c06b2b7

File tree

11 files changed

+58
-42
lines changed

11 files changed

+58
-42
lines changed

changelog.d/13823.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Faster Remote Room Joins: tell remote homeservers that we are unable to authorise them if they query a room which has partial state on our server.

synapse/api/errors.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ class Codes(str, Enum):
100100

101101
UNREDACTED_CONTENT_DELETED = "FI.MAU.MSC2815_UNREDACTED_CONTENT_DELETED"
102102

103+
# Returned for federation requests where we can't process a request as we
104+
# can't ensure the sending server is in a room which is partial-stated on
105+
# our side.
106+
# Part of MSC3895.
107+
UNABLE_DUE_TO_PARTIAL_STATE = "ORG.MATRIX.MSC3895_UNABLE_DUE_TO_PARTIAL_STATE"
108+
103109

104110
class CodeMessageException(RuntimeError):
105111
"""An exception with integer code and message string attributes.

synapse/config/experimental.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
6363
# MSC3706 (server-side support for partial state in /send_join responses)
6464
self.msc3706_enabled: bool = experimental.get("msc3706_enabled", False)
6565

66-
# experimental support for faster joins over federation (msc2775, msc3706)
66+
# experimental support for faster joins over federation
67+
# (MSC2775, MSC3706, MSC3895)
6768
# requires a target server with msc3706_enabled enabled.
6869
self.faster_joins_enabled: bool = experimental.get("faster_joins", False)
6970

synapse/federation/federation_server.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -530,13 +530,10 @@ async def _process_edu(edu_dict: JsonDict) -> None:
530530
async def on_room_state_request(
531531
self, origin: str, room_id: str, event_id: str
532532
) -> Tuple[int, JsonDict]:
533+
await self._event_auth_handler.assert_host_in_room(room_id, origin)
533534
origin_host, _ = parse_server_name(origin)
534535
await self.check_server_matches_acl(origin_host, room_id)
535536

536-
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
537-
if not in_room:
538-
raise AuthError(403, "Host not in room.")
539-
540537
# we grab the linearizer to protect ourselves from servers which hammer
541538
# us. In theory we might already have the response to this query
542539
# in the cache so we could return it without waiting for the linearizer
@@ -560,13 +557,10 @@ async def on_state_ids_request(
560557
if not event_id:
561558
raise NotImplementedError("Specify an event")
562559

560+
await self._event_auth_handler.assert_host_in_room(room_id, origin)
563561
origin_host, _ = parse_server_name(origin)
564562
await self.check_server_matches_acl(origin_host, room_id)
565563

566-
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
567-
if not in_room:
568-
raise AuthError(403, "Host not in room.")
569-
570564
resp = await self._state_ids_resp_cache.wrap(
571565
(room_id, event_id),
572566
self._on_state_ids_request_compute,
@@ -955,6 +949,7 @@ async def on_event_auth(
955949
self, origin: str, room_id: str, event_id: str
956950
) -> Tuple[int, Dict[str, Any]]:
957951
async with self._server_linearizer.queue((origin, room_id)):
952+
await self._event_auth_handler.assert_host_in_room(room_id, origin)
958953
origin_host, _ = parse_server_name(origin)
959954
await self.check_server_matches_acl(origin_host, room_id)
960955

synapse/handlers/event_auth.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
from synapse.events.builder import EventBuilder
3232
from synapse.events.snapshot import EventContext
3333
from synapse.types import StateMap, get_domain_from_id
34-
from synapse.util.metrics import Measure
3534

3635
if TYPE_CHECKING:
3736
from synapse.server import HomeServer
@@ -156,9 +155,33 @@ async def get_user_which_could_invite(
156155
Codes.UNABLE_TO_GRANT_JOIN,
157156
)
158157

159-
async def check_host_in_room(self, room_id: str, host: str) -> bool:
160-
with Measure(self._clock, "check_host_in_room"):
161-
return await self._store.is_host_joined(room_id, host)
158+
async def is_host_in_room(self, room_id: str, host: str) -> bool:
159+
return await self._store.is_host_joined(room_id, host)
160+
161+
async def assert_host_in_room(
162+
self, room_id: str, host: str, allow_partial_state_rooms: bool = False
163+
) -> None:
164+
"""
165+
Asserts that the host is in the room, or raises an AuthError.
166+
167+
If the room is partial-stated, we raise an AuthError with the
168+
UNABLE_DUE_TO_PARTIAL_STATE error code, unless `allow_partial_state_rooms` is true.
169+
170+
If allow_partial_state_rooms is True and the room is partial-stated,
171+
this function may return an incorrect result as we are not able to fully
172+
track server membership in a room without full state.
173+
"""
174+
if not allow_partial_state_rooms and await self._store.is_partial_state_room(
175+
room_id
176+
):
177+
raise AuthError(
178+
403,
179+
"Unable to authorise you right now; room is partial-stated here.",
180+
errcode=Codes.UNABLE_DUE_TO_PARTIAL_STATE,
181+
)
182+
183+
if not await self.is_host_in_room(room_id, host):
184+
raise AuthError(403, "Host not in room.")
162185

163186
async def check_restricted_join_rules(
164187
self,

synapse/handlers/federation.py

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ async def on_make_join_request(
804804
)
805805

806806
# now check that we are *still* in the room
807-
is_in_room = await self._event_auth_handler.check_host_in_room(
807+
is_in_room = await self._event_auth_handler.is_host_in_room(
808808
room_id, self.server_name
809809
)
810810
if not is_in_room:
@@ -1150,9 +1150,7 @@ async def get_state_ids_for_pdu(self, room_id: str, event_id: str) -> List[str]:
11501150
async def on_backfill_request(
11511151
self, origin: str, room_id: str, pdu_list: List[str], limit: int
11521152
) -> List[EventBase]:
1153-
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
1154-
if not in_room:
1155-
raise AuthError(403, "Host not in room.")
1153+
await self._event_auth_handler.assert_host_in_room(room_id, origin)
11561154

11571155
# Synapse asks for 100 events per backfill request. Do not allow more.
11581156
limit = min(limit, 100)
@@ -1198,21 +1196,17 @@ async def get_persisted_pdu(
11981196
event_id, allow_none=True, allow_rejected=True
11991197
)
12001198

1201-
if event:
1202-
in_room = await self._event_auth_handler.check_host_in_room(
1203-
event.room_id, origin
1204-
)
1205-
if not in_room:
1206-
raise AuthError(403, "Host not in room.")
1207-
1208-
events = await filter_events_for_server(
1209-
self._storage_controllers, origin, [event]
1210-
)
1211-
event = events[0]
1212-
return event
1213-
else:
1199+
if not event:
12141200
return None
12151201

1202+
await self._event_auth_handler.assert_host_in_room(event.room_id, origin)
1203+
1204+
events = await filter_events_for_server(
1205+
self._storage_controllers, origin, [event]
1206+
)
1207+
event = events[0]
1208+
return event
1209+
12161210
async def on_get_missing_events(
12171211
self,
12181212
origin: str,
@@ -1221,9 +1215,7 @@ async def on_get_missing_events(
12211215
latest_events: List[str],
12221216
limit: int,
12231217
) -> List[EventBase]:
1224-
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
1225-
if not in_room:
1226-
raise AuthError(403, "Host not in room.")
1218+
await self._event_auth_handler.assert_host_in_room(room_id, origin)
12271219

12281220
# Only allow up to 20 events to be retrieved per request.
12291221
limit = min(limit, 20)
@@ -1257,7 +1249,7 @@ async def exchange_third_party_invite(
12571249
"state_key": target_user_id,
12581250
}
12591251

1260-
if await self._event_auth_handler.check_host_in_room(room_id, self.hs.hostname):
1252+
if await self._event_auth_handler.is_host_in_room(room_id, self.hs.hostname):
12611253
room_version_obj = await self.store.get_room_version(room_id)
12621254
builder = self.event_builder_factory.for_room_version(
12631255
room_version_obj, event_dict

synapse/handlers/federation_event.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ async def on_receive_pdu(self, origin: str, pdu: EventBase) -> None:
238238
#
239239
# Note that if we were never in the room then we would have already
240240
# dropped the event, since we wouldn't know the room version.
241-
is_in_room = await self._event_auth_handler.check_host_in_room(
241+
is_in_room = await self._event_auth_handler.is_host_in_room(
242242
room_id, self._server_name
243243
)
244244
if not is_in_room:

synapse/handlers/receipts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async def _received_remote_receipt(self, origin: str, content: JsonDict) -> None
7070
# If we're not in the room just ditch the event entirely. This is
7171
# probably an old server that has come back and thinks we're still in
7272
# the room (or we've been rejoined to the room by a state reset).
73-
is_in_room = await self.event_auth_handler.check_host_in_room(
73+
is_in_room = await self.event_auth_handler.is_host_in_room(
7474
room_id, self.server_name
7575
)
7676
if not is_in_room:

synapse/handlers/room_summary.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ async def _is_local_room_accessible(
609609
# If this is a request over federation, check if the host is in the room or
610610
# has a user who could join the room.
611611
elif origin:
612-
if await self._event_auth_handler.check_host_in_room(
612+
if await self._event_auth_handler.is_host_in_room(
613613
room_id, origin
614614
) or await self._store.is_host_invited(room_id, origin):
615615
return True
@@ -624,9 +624,7 @@ async def _is_local_room_accessible(
624624
await self._event_auth_handler.get_rooms_that_allow_join(state_ids)
625625
)
626626
for space_id in allowed_rooms:
627-
if await self._event_auth_handler.check_host_in_room(
628-
space_id, origin
629-
):
627+
if await self._event_auth_handler.is_host_in_room(space_id, origin):
630628
return True
631629

632630
logger.info(

synapse/handlers/typing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ async def _recv_edu(self, origin: str, content: JsonDict) -> None:
340340
# If we're not in the room just ditch the event entirely. This is
341341
# probably an old server that has come back and thinks we're still in
342342
# the room (or we've been rejoined to the room by a state reset).
343-
is_in_room = await self.event_auth_handler.check_host_in_room(
343+
is_in_room = await self.event_auth_handler.is_host_in_room(
344344
room_id, self.server_name
345345
)
346346
if not is_in_room:

tests/handlers/test_typing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ async def check_user_in_room(room_id: str, requester: Requester) -> None:
129129
async def check_host_in_room(room_id: str, server_name: str) -> bool:
130130
return room_id == ROOM_ID
131131

132-
hs.get_event_auth_handler().check_host_in_room = check_host_in_room
132+
hs.get_event_auth_handler().is_host_in_room = check_host_in_room
133133

134134
async def get_current_hosts_in_room(room_id: str):
135135
return {member.domain for member in self.room_members}

0 commit comments

Comments
 (0)