mirror of
https://mau.dev/maunium/synapse.git
synced 2024-10-01 01:36:05 -04:00
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)
This commit is contained in:
parent
ac7e5683d6
commit
c06b2b7142
1
changelog.d/13823.misc
Normal file
1
changelog.d/13823.misc
Normal file
@ -0,0 +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.
|
@ -100,6 +100,12 @@ class Codes(str, Enum):
|
|||||||
|
|
||||||
UNREDACTED_CONTENT_DELETED = "FI.MAU.MSC2815_UNREDACTED_CONTENT_DELETED"
|
UNREDACTED_CONTENT_DELETED = "FI.MAU.MSC2815_UNREDACTED_CONTENT_DELETED"
|
||||||
|
|
||||||
|
# Returned for federation requests where we can't process a request as we
|
||||||
|
# can't ensure the sending server is in a room which is partial-stated on
|
||||||
|
# our side.
|
||||||
|
# Part of MSC3895.
|
||||||
|
UNABLE_DUE_TO_PARTIAL_STATE = "ORG.MATRIX.MSC3895_UNABLE_DUE_TO_PARTIAL_STATE"
|
||||||
|
|
||||||
|
|
||||||
class CodeMessageException(RuntimeError):
|
class CodeMessageException(RuntimeError):
|
||||||
"""An exception with integer code and message string attributes.
|
"""An exception with integer code and message string attributes.
|
||||||
|
@ -63,7 +63,8 @@ class ExperimentalConfig(Config):
|
|||||||
# MSC3706 (server-side support for partial state in /send_join responses)
|
# MSC3706 (server-side support for partial state in /send_join responses)
|
||||||
self.msc3706_enabled: bool = experimental.get("msc3706_enabled", False)
|
self.msc3706_enabled: bool = experimental.get("msc3706_enabled", False)
|
||||||
|
|
||||||
# experimental support for faster joins over federation (msc2775, msc3706)
|
# experimental support for faster joins over federation
|
||||||
|
# (MSC2775, MSC3706, MSC3895)
|
||||||
# requires a target server with msc3706_enabled enabled.
|
# requires a target server with msc3706_enabled enabled.
|
||||||
self.faster_joins_enabled: bool = experimental.get("faster_joins", False)
|
self.faster_joins_enabled: bool = experimental.get("faster_joins", False)
|
||||||
|
|
||||||
|
@ -530,13 +530,10 @@ class FederationServer(FederationBase):
|
|||||||
async def on_room_state_request(
|
async def on_room_state_request(
|
||||||
self, origin: str, room_id: str, event_id: str
|
self, origin: str, room_id: str, event_id: str
|
||||||
) -> Tuple[int, JsonDict]:
|
) -> Tuple[int, JsonDict]:
|
||||||
|
await self._event_auth_handler.assert_host_in_room(room_id, origin)
|
||||||
origin_host, _ = parse_server_name(origin)
|
origin_host, _ = parse_server_name(origin)
|
||||||
await self.check_server_matches_acl(origin_host, room_id)
|
await self.check_server_matches_acl(origin_host, room_id)
|
||||||
|
|
||||||
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
|
|
||||||
if not in_room:
|
|
||||||
raise AuthError(403, "Host not in room.")
|
|
||||||
|
|
||||||
# we grab the linearizer to protect ourselves from servers which hammer
|
# we grab the linearizer to protect ourselves from servers which hammer
|
||||||
# us. In theory we might already have the response to this query
|
# us. In theory we might already have the response to this query
|
||||||
# in the cache so we could return it without waiting for the linearizer
|
# in the cache so we could return it without waiting for the linearizer
|
||||||
@ -560,13 +557,10 @@ class FederationServer(FederationBase):
|
|||||||
if not event_id:
|
if not event_id:
|
||||||
raise NotImplementedError("Specify an event")
|
raise NotImplementedError("Specify an event")
|
||||||
|
|
||||||
|
await self._event_auth_handler.assert_host_in_room(room_id, origin)
|
||||||
origin_host, _ = parse_server_name(origin)
|
origin_host, _ = parse_server_name(origin)
|
||||||
await self.check_server_matches_acl(origin_host, room_id)
|
await self.check_server_matches_acl(origin_host, room_id)
|
||||||
|
|
||||||
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
|
|
||||||
if not in_room:
|
|
||||||
raise AuthError(403, "Host not in room.")
|
|
||||||
|
|
||||||
resp = await self._state_ids_resp_cache.wrap(
|
resp = await self._state_ids_resp_cache.wrap(
|
||||||
(room_id, event_id),
|
(room_id, event_id),
|
||||||
self._on_state_ids_request_compute,
|
self._on_state_ids_request_compute,
|
||||||
@ -955,6 +949,7 @@ class FederationServer(FederationBase):
|
|||||||
self, origin: str, room_id: str, event_id: str
|
self, origin: str, room_id: str, event_id: str
|
||||||
) -> Tuple[int, Dict[str, Any]]:
|
) -> Tuple[int, Dict[str, Any]]:
|
||||||
async with self._server_linearizer.queue((origin, room_id)):
|
async with self._server_linearizer.queue((origin, room_id)):
|
||||||
|
await self._event_auth_handler.assert_host_in_room(room_id, origin)
|
||||||
origin_host, _ = parse_server_name(origin)
|
origin_host, _ = parse_server_name(origin)
|
||||||
await self.check_server_matches_acl(origin_host, room_id)
|
await self.check_server_matches_acl(origin_host, room_id)
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ from synapse.events import EventBase
|
|||||||
from synapse.events.builder import EventBuilder
|
from synapse.events.builder import EventBuilder
|
||||||
from synapse.events.snapshot import EventContext
|
from synapse.events.snapshot import EventContext
|
||||||
from synapse.types import StateMap, get_domain_from_id
|
from synapse.types import StateMap, get_domain_from_id
|
||||||
from synapse.util.metrics import Measure
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
@ -156,10 +155,34 @@ class EventAuthHandler:
|
|||||||
Codes.UNABLE_TO_GRANT_JOIN,
|
Codes.UNABLE_TO_GRANT_JOIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def check_host_in_room(self, room_id: str, host: str) -> bool:
|
async def is_host_in_room(self, room_id: str, host: str) -> bool:
|
||||||
with Measure(self._clock, "check_host_in_room"):
|
|
||||||
return await self._store.is_host_joined(room_id, host)
|
return await self._store.is_host_joined(room_id, host)
|
||||||
|
|
||||||
|
async def assert_host_in_room(
|
||||||
|
self, room_id: str, host: str, allow_partial_state_rooms: bool = False
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Asserts that the host is in the room, or raises an AuthError.
|
||||||
|
|
||||||
|
If the room is partial-stated, we raise an AuthError with the
|
||||||
|
UNABLE_DUE_TO_PARTIAL_STATE error code, unless `allow_partial_state_rooms` is true.
|
||||||
|
|
||||||
|
If allow_partial_state_rooms is True and the room is partial-stated,
|
||||||
|
this function may return an incorrect result as we are not able to fully
|
||||||
|
track server membership in a room without full state.
|
||||||
|
"""
|
||||||
|
if not allow_partial_state_rooms and await self._store.is_partial_state_room(
|
||||||
|
room_id
|
||||||
|
):
|
||||||
|
raise AuthError(
|
||||||
|
403,
|
||||||
|
"Unable to authorise you right now; room is partial-stated here.",
|
||||||
|
errcode=Codes.UNABLE_DUE_TO_PARTIAL_STATE,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not await self.is_host_in_room(room_id, host):
|
||||||
|
raise AuthError(403, "Host not in room.")
|
||||||
|
|
||||||
async def check_restricted_join_rules(
|
async def check_restricted_join_rules(
|
||||||
self,
|
self,
|
||||||
state_ids: StateMap[str],
|
state_ids: StateMap[str],
|
||||||
|
@ -804,7 +804,7 @@ class FederationHandler:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# now check that we are *still* in the room
|
# now check that we are *still* in the room
|
||||||
is_in_room = await self._event_auth_handler.check_host_in_room(
|
is_in_room = await self._event_auth_handler.is_host_in_room(
|
||||||
room_id, self.server_name
|
room_id, self.server_name
|
||||||
)
|
)
|
||||||
if not is_in_room:
|
if not is_in_room:
|
||||||
@ -1150,9 +1150,7 @@ class FederationHandler:
|
|||||||
async def on_backfill_request(
|
async def on_backfill_request(
|
||||||
self, origin: str, room_id: str, pdu_list: List[str], limit: int
|
self, origin: str, room_id: str, pdu_list: List[str], limit: int
|
||||||
) -> List[EventBase]:
|
) -> List[EventBase]:
|
||||||
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
|
await self._event_auth_handler.assert_host_in_room(room_id, origin)
|
||||||
if not in_room:
|
|
||||||
raise AuthError(403, "Host not in room.")
|
|
||||||
|
|
||||||
# Synapse asks for 100 events per backfill request. Do not allow more.
|
# Synapse asks for 100 events per backfill request. Do not allow more.
|
||||||
limit = min(limit, 100)
|
limit = min(limit, 100)
|
||||||
@ -1198,20 +1196,16 @@ class FederationHandler:
|
|||||||
event_id, allow_none=True, allow_rejected=True
|
event_id, allow_none=True, allow_rejected=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if event:
|
if not event:
|
||||||
in_room = await self._event_auth_handler.check_host_in_room(
|
return None
|
||||||
event.room_id, origin
|
|
||||||
)
|
await self._event_auth_handler.assert_host_in_room(event.room_id, origin)
|
||||||
if not in_room:
|
|
||||||
raise AuthError(403, "Host not in room.")
|
|
||||||
|
|
||||||
events = await filter_events_for_server(
|
events = await filter_events_for_server(
|
||||||
self._storage_controllers, origin, [event]
|
self._storage_controllers, origin, [event]
|
||||||
)
|
)
|
||||||
event = events[0]
|
event = events[0]
|
||||||
return event
|
return event
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def on_get_missing_events(
|
async def on_get_missing_events(
|
||||||
self,
|
self,
|
||||||
@ -1221,9 +1215,7 @@ class FederationHandler:
|
|||||||
latest_events: List[str],
|
latest_events: List[str],
|
||||||
limit: int,
|
limit: int,
|
||||||
) -> List[EventBase]:
|
) -> List[EventBase]:
|
||||||
in_room = await self._event_auth_handler.check_host_in_room(room_id, origin)
|
await self._event_auth_handler.assert_host_in_room(room_id, origin)
|
||||||
if not in_room:
|
|
||||||
raise AuthError(403, "Host not in room.")
|
|
||||||
|
|
||||||
# Only allow up to 20 events to be retrieved per request.
|
# Only allow up to 20 events to be retrieved per request.
|
||||||
limit = min(limit, 20)
|
limit = min(limit, 20)
|
||||||
@ -1257,7 +1249,7 @@ class FederationHandler:
|
|||||||
"state_key": target_user_id,
|
"state_key": target_user_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
if await self._event_auth_handler.check_host_in_room(room_id, self.hs.hostname):
|
if await self._event_auth_handler.is_host_in_room(room_id, self.hs.hostname):
|
||||||
room_version_obj = await self.store.get_room_version(room_id)
|
room_version_obj = await self.store.get_room_version(room_id)
|
||||||
builder = self.event_builder_factory.for_room_version(
|
builder = self.event_builder_factory.for_room_version(
|
||||||
room_version_obj, event_dict
|
room_version_obj, event_dict
|
||||||
|
@ -238,7 +238,7 @@ class FederationEventHandler:
|
|||||||
#
|
#
|
||||||
# Note that if we were never in the room then we would have already
|
# Note that if we were never in the room then we would have already
|
||||||
# dropped the event, since we wouldn't know the room version.
|
# dropped the event, since we wouldn't know the room version.
|
||||||
is_in_room = await self._event_auth_handler.check_host_in_room(
|
is_in_room = await self._event_auth_handler.is_host_in_room(
|
||||||
room_id, self._server_name
|
room_id, self._server_name
|
||||||
)
|
)
|
||||||
if not is_in_room:
|
if not is_in_room:
|
||||||
|
@ -70,7 +70,7 @@ class ReceiptsHandler:
|
|||||||
# If we're not in the room just ditch the event entirely. This is
|
# If we're not in the room just ditch the event entirely. This is
|
||||||
# probably an old server that has come back and thinks we're still in
|
# probably an old server that has come back and thinks we're still in
|
||||||
# the room (or we've been rejoined to the room by a state reset).
|
# the room (or we've been rejoined to the room by a state reset).
|
||||||
is_in_room = await self.event_auth_handler.check_host_in_room(
|
is_in_room = await self.event_auth_handler.is_host_in_room(
|
||||||
room_id, self.server_name
|
room_id, self.server_name
|
||||||
)
|
)
|
||||||
if not is_in_room:
|
if not is_in_room:
|
||||||
|
@ -609,7 +609,7 @@ class RoomSummaryHandler:
|
|||||||
# If this is a request over federation, check if the host is in the room or
|
# If this is a request over federation, check if the host is in the room or
|
||||||
# has a user who could join the room.
|
# has a user who could join the room.
|
||||||
elif origin:
|
elif origin:
|
||||||
if await self._event_auth_handler.check_host_in_room(
|
if await self._event_auth_handler.is_host_in_room(
|
||||||
room_id, origin
|
room_id, origin
|
||||||
) or await self._store.is_host_invited(room_id, origin):
|
) or await self._store.is_host_invited(room_id, origin):
|
||||||
return True
|
return True
|
||||||
@ -624,9 +624,7 @@ class RoomSummaryHandler:
|
|||||||
await self._event_auth_handler.get_rooms_that_allow_join(state_ids)
|
await self._event_auth_handler.get_rooms_that_allow_join(state_ids)
|
||||||
)
|
)
|
||||||
for space_id in allowed_rooms:
|
for space_id in allowed_rooms:
|
||||||
if await self._event_auth_handler.check_host_in_room(
|
if await self._event_auth_handler.is_host_in_room(space_id, origin):
|
||||||
space_id, origin
|
|
||||||
):
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
|
@ -340,7 +340,7 @@ class TypingWriterHandler(FollowerTypingHandler):
|
|||||||
# If we're not in the room just ditch the event entirely. This is
|
# If we're not in the room just ditch the event entirely. This is
|
||||||
# probably an old server that has come back and thinks we're still in
|
# probably an old server that has come back and thinks we're still in
|
||||||
# the room (or we've been rejoined to the room by a state reset).
|
# the room (or we've been rejoined to the room by a state reset).
|
||||||
is_in_room = await self.event_auth_handler.check_host_in_room(
|
is_in_room = await self.event_auth_handler.is_host_in_room(
|
||||||
room_id, self.server_name
|
room_id, self.server_name
|
||||||
)
|
)
|
||||||
if not is_in_room:
|
if not is_in_room:
|
||||||
|
@ -129,7 +129,7 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
|
|||||||
async def check_host_in_room(room_id: str, server_name: str) -> bool:
|
async def check_host_in_room(room_id: str, server_name: str) -> bool:
|
||||||
return room_id == ROOM_ID
|
return room_id == ROOM_ID
|
||||||
|
|
||||||
hs.get_event_auth_handler().check_host_in_room = check_host_in_room
|
hs.get_event_auth_handler().is_host_in_room = check_host_in_room
|
||||||
|
|
||||||
async def get_current_hosts_in_room(room_id: str):
|
async def get_current_hosts_in_room(room_id: str):
|
||||||
return {member.domain for member in self.room_members}
|
return {member.domain for member in self.room_members}
|
||||||
|
Loading…
Reference in New Issue
Block a user