|
|
@@ -103,7 +103,7 @@ logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
@attr.s(slots=True) |
|
|
|
class _NewEventInfo: |
|
|
|
"""Holds information about a received event, ready for passing to _auth_and_persist_events |
|
|
|
"""Holds information about a received event, ready for passing to _handle_new_events |
|
|
|
|
|
|
|
Attributes: |
|
|
|
event: the received event |
|
|
@@ -146,7 +146,6 @@ class FederationHandler(BaseHandler): |
|
|
|
self.is_mine_id = hs.is_mine_id |
|
|
|
self.spam_checker = hs.get_spam_checker() |
|
|
|
self.event_creation_handler = hs.get_event_creation_handler() |
|
|
|
self.event_auth_handler = hs.get_event_auth_handler() |
|
|
|
self._message_handler = hs.get_message_handler() |
|
|
|
self._server_notices_mxid = hs.config.server_notices_mxid |
|
|
|
self.config = hs.config |
|
|
@@ -808,10 +807,7 @@ class FederationHandler(BaseHandler): |
|
|
|
logger.debug("Processing event: %s", event) |
|
|
|
|
|
|
|
try: |
|
|
|
context = await self.state_handler.compute_event_context( |
|
|
|
event, old_state=state |
|
|
|
) |
|
|
|
await self._auth_and_persist_event(origin, event, context, state=state) |
|
|
|
await self._handle_new_event(origin, event, state=state) |
|
|
|
except AuthError as e: |
|
|
|
raise FederationError("ERROR", e.code, e.msg, affected=event.event_id) |
|
|
|
|
|
|
@@ -1014,9 +1010,7 @@ class FederationHandler(BaseHandler): |
|
|
|
) |
|
|
|
|
|
|
|
if ev_infos: |
|
|
|
await self._auth_and_persist_events( |
|
|
|
dest, room_id, ev_infos, backfilled=True |
|
|
|
) |
|
|
|
await self._handle_new_events(dest, room_id, ev_infos, backfilled=True) |
|
|
|
|
|
|
|
# Step 2: Persist the rest of the events in the chunk one by one |
|
|
|
events.sort(key=lambda e: e.depth) |
|
|
@@ -1029,12 +1023,10 @@ class FederationHandler(BaseHandler): |
|
|
|
# non-outliers |
|
|
|
assert not event.internal_metadata.is_outlier() |
|
|
|
|
|
|
|
context = await self.state_handler.compute_event_context(event) |
|
|
|
|
|
|
|
# We store these one at a time since each event depends on the |
|
|
|
# previous to work out the state. |
|
|
|
# TODO: We can probably do something more clever here. |
|
|
|
await self._auth_and_persist_event(dest, event, context, backfilled=True) |
|
|
|
await self._handle_new_event(dest, event, backfilled=True) |
|
|
|
|
|
|
|
return events |
|
|
|
|
|
|
@@ -1368,7 +1360,7 @@ class FederationHandler(BaseHandler): |
|
|
|
|
|
|
|
event_infos.append(_NewEventInfo(event, None, auth)) |
|
|
|
|
|
|
|
await self._auth_and_persist_events( |
|
|
|
await self._handle_new_events( |
|
|
|
destination, |
|
|
|
room_id, |
|
|
|
event_infos, |
|
|
@@ -1674,47 +1666,16 @@ class FederationHandler(BaseHandler): |
|
|
|
# would introduce the danger of backwards-compatibility problems. |
|
|
|
event.internal_metadata.send_on_behalf_of = origin |
|
|
|
|
|
|
|
# Calculate the event context. |
|
|
|
context = await self.state_handler.compute_event_context(event) |
|
|
|
|
|
|
|
# Get the state before the new event. |
|
|
|
prev_state_ids = await context.get_prev_state_ids() |
|
|
|
|
|
|
|
# Check if the user is already in the room or invited to the room. |
|
|
|
user_id = event.state_key |
|
|
|
prev_member_event_id = prev_state_ids.get((EventTypes.Member, user_id), None) |
|
|
|
newly_joined = True |
|
|
|
is_invite = False |
|
|
|
if prev_member_event_id: |
|
|
|
prev_member_event = await self.store.get_event(prev_member_event_id) |
|
|
|
newly_joined = prev_member_event.membership != Membership.JOIN |
|
|
|
is_invite = prev_member_event.membership == Membership.INVITE |
|
|
|
|
|
|
|
# If the member is not already in the room, and not invited, check if |
|
|
|
# they should be allowed access via membership in a space. |
|
|
|
if ( |
|
|
|
newly_joined |
|
|
|
and not is_invite |
|
|
|
and not await self.event_auth_handler.can_join_without_invite( |
|
|
|
prev_state_ids, |
|
|
|
event.room_version, |
|
|
|
user_id, |
|
|
|
) |
|
|
|
): |
|
|
|
raise SynapseError( |
|
|
|
400, |
|
|
|
"You do not belong to any of the required spaces to join this room.", |
|
|
|
) |
|
|
|
|
|
|
|
# Persist the event. |
|
|
|
await self._auth_and_persist_event(origin, event, context) |
|
|
|
context = await self._handle_new_event(origin, event) |
|
|
|
|
|
|
|
logger.debug( |
|
|
|
"on_send_join_request: After _auth_and_persist_event: %s, sigs: %s", |
|
|
|
"on_send_join_request: After _handle_new_event: %s, sigs: %s", |
|
|
|
event.event_id, |
|
|
|
event.signatures, |
|
|
|
) |
|
|
|
|
|
|
|
prev_state_ids = await context.get_prev_state_ids() |
|
|
|
|
|
|
|
state_ids = list(prev_state_ids.values()) |
|
|
|
auth_chain = await self.store.get_auth_chain(event.room_id, state_ids) |
|
|
|
|
|
|
@@ -1917,11 +1878,10 @@ class FederationHandler(BaseHandler): |
|
|
|
|
|
|
|
event.internal_metadata.outlier = False |
|
|
|
|
|
|
|
context = await self.state_handler.compute_event_context(event) |
|
|
|
await self._auth_and_persist_event(origin, event, context) |
|
|
|
await self._handle_new_event(origin, event) |
|
|
|
|
|
|
|
logger.debug( |
|
|
|
"on_send_leave_request: After _auth_and_persist_event: %s, sigs: %s", |
|
|
|
"on_send_leave_request: After _handle_new_event: %s, sigs: %s", |
|
|
|
event.event_id, |
|
|
|
event.signatures, |
|
|
|
) |
|
|
@@ -2029,44 +1989,16 @@ class FederationHandler(BaseHandler): |
|
|
|
async def get_min_depth_for_context(self, context: str) -> int: |
|
|
|
return await self.store.get_min_depth(context) |
|
|
|
|
|
|
|
async def _auth_and_persist_event( |
|
|
|
async def _handle_new_event( |
|
|
|
self, |
|
|
|
origin: str, |
|
|
|
event: EventBase, |
|
|
|
context: EventContext, |
|
|
|
state: Optional[Iterable[EventBase]] = None, |
|
|
|
auth_events: Optional[MutableStateMap[EventBase]] = None, |
|
|
|
backfilled: bool = False, |
|
|
|
) -> None: |
|
|
|
""" |
|
|
|
Process an event by performing auth checks and then persisting to the database. |
|
|
|
|
|
|
|
Args: |
|
|
|
origin: The host the event originates from. |
|
|
|
event: The event itself. |
|
|
|
context: |
|
|
|
The event context. |
|
|
|
|
|
|
|
NB that this function potentially modifies it. |
|
|
|
state: |
|
|
|
The state events used to auth the event. If this is not provided |
|
|
|
the current state events will be used. |
|
|
|
auth_events: |
|
|
|
Map from (event_type, state_key) to event |
|
|
|
|
|
|
|
Normally, our calculated auth_events based on the state of the room |
|
|
|
at the event's position in the DAG, though occasionally (eg if the |
|
|
|
event is an outlier), may be the auth events claimed by the remote |
|
|
|
server. |
|
|
|
backfilled: True if the event was backfilled. |
|
|
|
""" |
|
|
|
context = await self._check_event_auth( |
|
|
|
origin, |
|
|
|
event, |
|
|
|
context, |
|
|
|
state=state, |
|
|
|
auth_events=auth_events, |
|
|
|
backfilled=backfilled, |
|
|
|
) -> EventContext: |
|
|
|
context = await self._prep_event( |
|
|
|
origin, event, state=state, auth_events=auth_events, backfilled=backfilled |
|
|
|
) |
|
|
|
|
|
|
|
try: |
|
|
@@ -2088,7 +2020,9 @@ class FederationHandler(BaseHandler): |
|
|
|
) |
|
|
|
raise |
|
|
|
|
|
|
|
async def _auth_and_persist_events( |
|
|
|
return context |
|
|
|
|
|
|
|
async def _handle_new_events( |
|
|
|
self, |
|
|
|
origin: str, |
|
|
|
room_id: str, |
|
|
@@ -2106,13 +2040,9 @@ class FederationHandler(BaseHandler): |
|
|
|
async def prep(ev_info: _NewEventInfo): |
|
|
|
event = ev_info.event |
|
|
|
with nested_logging_context(suffix=event.event_id): |
|
|
|
res = await self.state_handler.compute_event_context( |
|
|
|
event, old_state=ev_info.state |
|
|
|
) |
|
|
|
res = await self._check_event_auth( |
|
|
|
res = await self._prep_event( |
|
|
|
origin, |
|
|
|
event, |
|
|
|
res, |
|
|
|
state=ev_info.state, |
|
|
|
auth_events=ev_info.auth_events, |
|
|
|
backfilled=backfilled, |
|
|
@@ -2247,6 +2177,49 @@ class FederationHandler(BaseHandler): |
|
|
|
room_id, [(event, new_event_context)] |
|
|
|
) |
|
|
|
|
|
|
|
async def _prep_event( |
|
|
|
self, |
|
|
|
origin: str, |
|
|
|
event: EventBase, |
|
|
|
state: Optional[Iterable[EventBase]], |
|
|
|
auth_events: Optional[MutableStateMap[EventBase]], |
|
|
|
backfilled: bool, |
|
|
|
) -> EventContext: |
|
|
|
context = await self.state_handler.compute_event_context(event, old_state=state) |
|
|
|
|
|
|
|
if not auth_events: |
|
|
|
prev_state_ids = await context.get_prev_state_ids() |
|
|
|
auth_events_ids = self.auth.compute_auth_events( |
|
|
|
event, prev_state_ids, for_verification=True |
|
|
|
) |
|
|
|
auth_events_x = await self.store.get_events(auth_events_ids) |
|
|
|
auth_events = {(e.type, e.state_key): e for e in auth_events_x.values()} |
|
|
|
|
|
|
|
# This is a hack to fix some old rooms where the initial join event |
|
|
|
# didn't reference the create event in its auth events. |
|
|
|
if event.type == EventTypes.Member and not event.auth_event_ids(): |
|
|
|
if len(event.prev_event_ids()) == 1 and event.depth < 5: |
|
|
|
c = await self.store.get_event( |
|
|
|
event.prev_event_ids()[0], allow_none=True |
|
|
|
) |
|
|
|
if c and c.type == EventTypes.Create: |
|
|
|
auth_events[(c.type, c.state_key)] = c |
|
|
|
|
|
|
|
context = await self.do_auth(origin, event, context, auth_events=auth_events) |
|
|
|
|
|
|
|
if not context.rejected: |
|
|
|
await self._check_for_soft_fail(event, state, backfilled) |
|
|
|
|
|
|
|
if event.type == EventTypes.GuestAccess and not context.rejected: |
|
|
|
await self.maybe_kick_guest_users(event) |
|
|
|
|
|
|
|
# If we are going to send this event over federation we precaclculate |
|
|
|
# the joined hosts. |
|
|
|
if event.internal_metadata.get_send_on_behalf_of(): |
|
|
|
await self.event_creation_handler.cache_joined_hosts_for_event(event) |
|
|
|
|
|
|
|
return context |
|
|
|
|
|
|
|
async def _check_for_soft_fail( |
|
|
|
self, event: EventBase, state: Optional[Iterable[EventBase]], backfilled: bool |
|
|
|
) -> None: |
|
|
@@ -2357,28 +2330,19 @@ class FederationHandler(BaseHandler): |
|
|
|
|
|
|
|
return missing_events |
|
|
|
|
|
|
|
async def _check_event_auth( |
|
|
|
async def do_auth( |
|
|
|
self, |
|
|
|
origin: str, |
|
|
|
event: EventBase, |
|
|
|
context: EventContext, |
|
|
|
state: Optional[Iterable[EventBase]], |
|
|
|
auth_events: Optional[MutableStateMap[EventBase]], |
|
|
|
backfilled: bool, |
|
|
|
auth_events: MutableStateMap[EventBase], |
|
|
|
) -> EventContext: |
|
|
|
""" |
|
|
|
Checks whether an event should be rejected (for failing auth checks). |
|
|
|
|
|
|
|
Args: |
|
|
|
origin: The host the event originates from. |
|
|
|
event: The event itself. |
|
|
|
origin: |
|
|
|
event: |
|
|
|
context: |
|
|
|
The event context. |
|
|
|
|
|
|
|
NB that this function potentially modifies it. |
|
|
|
state: |
|
|
|
The state events used to auth the event. If this is not provided |
|
|
|
the current state events will be used. |
|
|
|
auth_events: |
|
|
|
Map from (event_type, state_key) to event |
|
|
|
|
|
|
@@ -2388,32 +2352,12 @@ class FederationHandler(BaseHandler): |
|
|
|
server. |
|
|
|
|
|
|
|
Also NB that this function adds entries to it. |
|
|
|
backfilled: True if the event was backfilled. |
|
|
|
|
|
|
|
Returns: |
|
|
|
The updated context object. |
|
|
|
updated context object |
|
|
|
""" |
|
|
|
room_version = await self.store.get_room_version_id(event.room_id) |
|
|
|
room_version_obj = KNOWN_ROOM_VERSIONS[room_version] |
|
|
|
|
|
|
|
if not auth_events: |
|
|
|
prev_state_ids = await context.get_prev_state_ids() |
|
|
|
auth_events_ids = self.auth.compute_auth_events( |
|
|
|
event, prev_state_ids, for_verification=True |
|
|
|
) |
|
|
|
auth_events_x = await self.store.get_events(auth_events_ids) |
|
|
|
auth_events = {(e.type, e.state_key): e for e in auth_events_x.values()} |
|
|
|
|
|
|
|
# This is a hack to fix some old rooms where the initial join event |
|
|
|
# didn't reference the create event in its auth events. |
|
|
|
if event.type == EventTypes.Member and not event.auth_event_ids(): |
|
|
|
if len(event.prev_event_ids()) == 1 and event.depth < 5: |
|
|
|
c = await self.store.get_event( |
|
|
|
event.prev_event_ids()[0], allow_none=True |
|
|
|
) |
|
|
|
if c and c.type == EventTypes.Create: |
|
|
|
auth_events[(c.type, c.state_key)] = c |
|
|
|
|
|
|
|
try: |
|
|
|
context = await self._update_auth_events_and_context_for_auth( |
|
|
|
origin, event, context, auth_events |
|
|
@@ -2435,17 +2379,6 @@ class FederationHandler(BaseHandler): |
|
|
|
logger.warning("Failed auth resolution for %r because %s", event, e) |
|
|
|
context.rejected = RejectedReason.AUTH_ERROR |
|
|
|
|
|
|
|
if not context.rejected: |
|
|
|
await self._check_for_soft_fail(event, state, backfilled) |
|
|
|
|
|
|
|
if event.type == EventTypes.GuestAccess and not context.rejected: |
|
|
|
await self.maybe_kick_guest_users(event) |
|
|
|
|
|
|
|
# If we are going to send this event over federation we precaclculate |
|
|
|
# the joined hosts. |
|
|
|
if event.internal_metadata.get_send_on_behalf_of(): |
|
|
|
await self.event_creation_handler.cache_joined_hosts_for_event(event) |
|
|
|
|
|
|
|
return context |
|
|
|
|
|
|
|
async def _update_auth_events_and_context_for_auth( |
|
|
@@ -2455,7 +2388,7 @@ class FederationHandler(BaseHandler): |
|
|
|
context: EventContext, |
|
|
|
auth_events: MutableStateMap[EventBase], |
|
|
|
) -> EventContext: |
|
|
|
"""Helper for _check_event_auth. See there for docs. |
|
|
|
"""Helper for do_auth. See there for docs. |
|
|
|
|
|
|
|
Checks whether a given event has the expected auth events. If it |
|
|
|
doesn't then we talk to the remote server to compare state to see if |
|
|
@@ -2535,14 +2468,9 @@ class FederationHandler(BaseHandler): |
|
|
|
e.internal_metadata.outlier = True |
|
|
|
|
|
|
|
logger.debug( |
|
|
|
"_check_event_auth %s missing_auth: %s", |
|
|
|
event.event_id, |
|
|
|
e.event_id, |
|
|
|
) |
|
|
|
context = await self.state_handler.compute_event_context(e) |
|
|
|
await self._auth_and_persist_event( |
|
|
|
origin, e, context, auth_events=auth |
|
|
|
"do_auth %s missing_auth: %s", event.event_id, e.event_id |
|
|
|
) |
|
|
|
await self._handle_new_event(origin, e, auth_events=auth) |
|
|
|
|
|
|
|
if e.event_id in event_auth_events: |
|
|
|
auth_events[(e.type, e.state_key)] = e |
|
|
|