1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15+ import collections
1516import itertools
1617import logging
1718from http import HTTPStatus
@@ -347,7 +348,7 @@ async def on_send_membership_event(
347348 event .internal_metadata .send_on_behalf_of = origin
348349
349350 context = await self ._state_handler .compute_event_context (event )
350- context = await self ._check_event_auth (origin , event , context )
351+ await self ._check_event_auth (origin , event , context )
351352 if context .rejected :
352353 raise SynapseError (
353354 403 , f"{ event .membership } event was rejected" , Codes .FORBIDDEN
@@ -485,7 +486,7 @@ async def process_remote_join(
485486 partial_state = partial_state ,
486487 )
487488
488- context = await self ._check_event_auth (origin , event , context )
489+ await self ._check_event_auth (origin , event , context )
489490 if context .rejected :
490491 raise SynapseError (400 , "Join event was rejected" )
491492
@@ -1116,11 +1117,7 @@ async def _process_received_pdu(
11161117 state_ids_before_event = state_ids ,
11171118 )
11181119 try :
1119- context = await self ._check_event_auth (
1120- origin ,
1121- event ,
1122- context ,
1123- )
1120+ await self ._check_event_auth (origin , event , context )
11241121 except AuthError as e :
11251122 # This happens only if we couldn't find the auth events. We'll already have
11261123 # logged a warning, so now we just convert to a FederationError.
@@ -1495,11 +1492,8 @@ async def prep(event: EventBase) -> None:
14951492 )
14961493
14971494 async def _check_event_auth (
1498- self ,
1499- origin : str ,
1500- event : EventBase ,
1501- context : EventContext ,
1502- ) -> EventContext :
1495+ self , origin : str , event : EventBase , context : EventContext
1496+ ) -> None :
15031497 """
15041498 Checks whether an event should be rejected (for failing auth checks).
15051499
@@ -1509,9 +1503,6 @@ async def _check_event_auth(
15091503 context:
15101504 The event context.
15111505
1512- Returns:
1513- The updated context object.
1514-
15151506 Raises:
15161507 AuthError if we were unable to find copies of the event's auth events.
15171508 (Most other failures just cause us to set `context.rejected`.)
@@ -1526,7 +1517,7 @@ async def _check_event_auth(
15261517 logger .warning ("While validating received event %r: %s" , event , e )
15271518 # TODO: use a different rejected reason here?
15281519 context .rejected = RejectedReason .AUTH_ERROR
1529- return context
1520+ return
15301521
15311522 # next, check that we have all of the event's auth events.
15321523 #
@@ -1538,6 +1529,9 @@ async def _check_event_auth(
15381529 )
15391530
15401531 # ... and check that the event passes auth at those auth events.
1532+ # https://spec.matrix.org/v1.3/server-server-api/#checks-performed-on-receipt-of-a-pdu:
1533+ # 4. Passes authorization rules based on the event’s auth events,
1534+ # otherwise it is rejected.
15411535 try :
15421536 await check_state_independent_auth_rules (self ._store , event )
15431537 check_state_dependent_auth_rules (event , claimed_auth_events )
@@ -1546,55 +1540,90 @@ async def _check_event_auth(
15461540 "While checking auth of %r against auth_events: %s" , event , e
15471541 )
15481542 context .rejected = RejectedReason .AUTH_ERROR
1549- return context
1543+ return
1544+
1545+ # now check the auth rules pass against the room state before the event
1546+ # https://spec.matrix.org/v1.3/server-server-api/#checks-performed-on-receipt-of-a-pdu:
1547+ # 5. Passes authorization rules based on the state before the event,
1548+ # otherwise it is rejected.
1549+ #
1550+ # ... however, if we only have partial state for the room, then there is a good
1551+ # chance that we'll be missing some of the state needed to auth the new event.
1552+ # So, we state-resolve the auth events that we are given against the state that
1553+ # we know about, which ensures things like bans are applied. (Note that we'll
1554+ # already have checked we have all the auth events, in
1555+ # _load_or_fetch_auth_events_for_event above)
1556+ if context .partial_state :
1557+ room_version = await self ._store .get_room_version_id (event .room_id )
1558+
1559+ local_state_id_map = await context .get_prev_state_ids ()
1560+ claimed_auth_events_id_map = {
1561+ (ev .type , ev .state_key ): ev .event_id for ev in claimed_auth_events
1562+ }
1563+
1564+ state_for_auth_id_map = (
1565+ await self ._state_resolution_handler .resolve_events_with_store (
1566+ event .room_id ,
1567+ room_version ,
1568+ [local_state_id_map , claimed_auth_events_id_map ],
1569+ event_map = None ,
1570+ state_res_store = StateResolutionStore (self ._store ),
1571+ )
1572+ )
1573+ else :
1574+ event_types = event_auth .auth_types_for_event (event .room_version , event )
1575+ state_for_auth_id_map = await context .get_prev_state_ids (
1576+ StateFilter .from_types (event_types )
1577+ )
15501578
1551- # now check auth against what we think the auth events *should* be.
1552- event_types = event_auth .auth_types_for_event (event .room_version , event )
1553- prev_state_ids = await context .get_prev_state_ids (
1554- StateFilter .from_types (event_types )
1579+ calculated_auth_event_ids = self ._event_auth_handler .compute_auth_events (
1580+ event , state_for_auth_id_map , for_verification = True
15551581 )
15561582
1557- auth_events_ids = self ._event_auth_handler .compute_auth_events (
1558- event , prev_state_ids , for_verification = True
1583+ # if those are the same, we're done here.
1584+ if collections .Counter (event .auth_event_ids ()) == collections .Counter (
1585+ calculated_auth_event_ids
1586+ ):
1587+ return
1588+
1589+ # otherwise, re-run the auth checks based on what we calculated.
1590+ calculated_auth_events = await self ._store .get_events_as_list (
1591+ calculated_auth_event_ids
15591592 )
1560- auth_events_x = await self ._store .get_events (auth_events_ids )
1593+
1594+ # log the differences
1595+
1596+ claimed_auth_event_map = {(e .type , e .state_key ): e for e in claimed_auth_events }
15611597 calculated_auth_event_map = {
1562- (e .type , e .state_key ): e for e in auth_events_x . values ()
1598+ (e .type , e .state_key ): e for e in calculated_auth_events
15631599 }
1600+ logger .info (
1601+ "event's auth_events are different to our calculated auth_events. "
1602+ "Claimed but not calculated: %s. Calculated but not claimed: %s" ,
1603+ [
1604+ ev
1605+ for k , ev in claimed_auth_event_map .items ()
1606+ if k not in calculated_auth_event_map
1607+ or calculated_auth_event_map [k ].event_id != ev .event_id
1608+ ],
1609+ [
1610+ ev
1611+ for k , ev in calculated_auth_event_map .items ()
1612+ if k not in claimed_auth_event_map
1613+ or claimed_auth_event_map [k ].event_id != ev .event_id
1614+ ],
1615+ )
15641616
15651617 try :
1566- updated_auth_events = await self ._update_auth_events_for_auth (
1618+ check_state_dependent_auth_rules (event , calculated_auth_events )
1619+ except AuthError as e :
1620+ logger .warning (
1621+ "While checking auth of %r against room state before the event: %s" ,
15671622 event ,
1568- calculated_auth_event_map = calculated_auth_event_map ,
1569- )
1570- except Exception :
1571- # We don't really mind if the above fails, so lets not fail
1572- # processing if it does. However, it really shouldn't fail so
1573- # let's still log as an exception since we'll still want to fix
1574- # any bugs.
1575- logger .exception (
1576- "Failed to double check auth events for %s with remote. "
1577- "Ignoring failure and continuing processing of event." ,
1578- event .event_id ,
1579- )
1580- updated_auth_events = None
1581-
1582- if updated_auth_events :
1583- context = await self ._update_context_for_auth_events (
1584- event , context , updated_auth_events
1623+ e ,
15851624 )
1586- auth_events_for_auth = updated_auth_events
1587- else :
1588- auth_events_for_auth = calculated_auth_event_map
1589-
1590- try :
1591- check_state_dependent_auth_rules (event , auth_events_for_auth .values ())
1592- except AuthError as e :
1593- logger .warning ("Failed auth resolution for %r because %s" , event , e )
15941625 context .rejected = RejectedReason .AUTH_ERROR
15951626
1596- return context
1597-
15981627 async def _maybe_kick_guest_users (self , event : EventBase ) -> None :
15991628 if event .type != EventTypes .GuestAccess :
16001629 return
@@ -1704,93 +1733,6 @@ async def _check_for_soft_fail(
17041733 soft_failed_event_counter .inc ()
17051734 event .internal_metadata .soft_failed = True
17061735
1707- async def _update_auth_events_for_auth (
1708- self ,
1709- event : EventBase ,
1710- calculated_auth_event_map : StateMap [EventBase ],
1711- ) -> Optional [StateMap [EventBase ]]:
1712- """Helper for _check_event_auth. See there for docs.
1713-
1714- Checks whether a given event has the expected auth events. If it
1715- doesn't then we talk to the remote server to compare state to see if
1716- we can come to a consensus (e.g. if one server missed some valid
1717- state).
1718-
1719- This attempts to resolve any potential divergence of state between
1720- servers, but is not essential and so failures should not block further
1721- processing of the event.
1722-
1723- Args:
1724- event:
1725-
1726- calculated_auth_event_map:
1727- Our calculated auth_events based on the state of the room
1728- at the event's position in the DAG.
1729-
1730- Returns:
1731- updated auth event map, or None if no changes are needed.
1732-
1733- """
1734- assert not event .internal_metadata .outlier
1735-
1736- # check for events which are in the event's claimed auth_events, but not
1737- # in our calculated event map.
1738- event_auth_events = set (event .auth_event_ids ())
1739- different_auth = event_auth_events .difference (
1740- e .event_id for e in calculated_auth_event_map .values ()
1741- )
1742-
1743- if not different_auth :
1744- return None
1745-
1746- logger .info (
1747- "auth_events refers to events which are not in our calculated auth "
1748- "chain: %s" ,
1749- different_auth ,
1750- )
1751-
1752- # XXX: currently this checks for redactions but I'm not convinced that is
1753- # necessary?
1754- different_events = await self ._store .get_events_as_list (different_auth )
1755-
1756- # double-check they're all in the same room - we should already have checked
1757- # this but it doesn't hurt to check again.
1758- for d in different_events :
1759- assert (
1760- d .room_id == event .room_id
1761- ), f"Event { event .event_id } refers to auth_event { d .event_id } which is in a different room"
1762-
1763- # now we state-resolve between our own idea of the auth events, and the remote's
1764- # idea of them.
1765-
1766- local_state = calculated_auth_event_map .values ()
1767- remote_auth_events = dict (calculated_auth_event_map )
1768- remote_auth_events .update ({(d .type , d .state_key ): d for d in different_events })
1769- remote_state = remote_auth_events .values ()
1770-
1771- room_version = await self ._store .get_room_version_id (event .room_id )
1772- new_state = await self ._state_handler .resolve_events (
1773- room_version , (local_state , remote_state ), event
1774- )
1775- different_state = {
1776- (d .type , d .state_key ): d
1777- for d in new_state .values ()
1778- if calculated_auth_event_map .get ((d .type , d .state_key )) != d
1779- }
1780- if not different_state :
1781- logger .info ("State res returned no new state" )
1782- return None
1783-
1784- logger .info (
1785- "After state res: updating auth_events with new state %s" ,
1786- different_state .values (),
1787- )
1788-
1789- # take a copy of calculated_auth_event_map before we modify it.
1790- auth_events = dict (calculated_auth_event_map )
1791- auth_events .update (different_state )
1792- return auth_events
1793-
17941736 async def _load_or_fetch_auth_events_for_event (
17951737 self , destination : str , event : EventBase
17961738 ) -> Collection [EventBase ]:
@@ -1888,61 +1830,6 @@ async def _get_remote_auth_chain_for_event(
18881830
18891831 await self ._auth_and_persist_outliers (room_id , remote_auth_events )
18901832
1891- async def _update_context_for_auth_events (
1892- self , event : EventBase , context : EventContext , auth_events : StateMap [EventBase ]
1893- ) -> EventContext :
1894- """Update the state_ids in an event context after auth event resolution,
1895- storing the changes as a new state group.
1896-
1897- Args:
1898- event: The event we're handling the context for
1899-
1900- context: initial event context
1901-
1902- auth_events: Events to update in the event context.
1903-
1904- Returns:
1905- new event context
1906- """
1907- # exclude the state key of the new event from the current_state in the context.
1908- if event .is_state ():
1909- event_key : Optional [Tuple [str , str ]] = (event .type , event .state_key )
1910- else :
1911- event_key = None
1912- state_updates = {
1913- k : a .event_id for k , a in auth_events .items () if k != event_key
1914- }
1915-
1916- current_state_ids = await context .get_current_state_ids ()
1917- current_state_ids = dict (current_state_ids ) # type: ignore
1918-
1919- current_state_ids .update (state_updates )
1920-
1921- prev_state_ids = await context .get_prev_state_ids ()
1922- prev_state_ids = dict (prev_state_ids )
1923-
1924- prev_state_ids .update ({k : a .event_id for k , a in auth_events .items ()})
1925-
1926- # create a new state group as a delta from the existing one.
1927- prev_group = context .state_group
1928- state_group = await self ._state_storage_controller .store_state_group (
1929- event .event_id ,
1930- event .room_id ,
1931- prev_group = prev_group ,
1932- delta_ids = state_updates ,
1933- current_state_ids = current_state_ids ,
1934- )
1935-
1936- return EventContext .with_state (
1937- storage = self ._storage_controllers ,
1938- state_group = state_group ,
1939- state_group_before_event = context .state_group_before_event ,
1940- state_delta_due_to_event = state_updates ,
1941- prev_group = prev_group ,
1942- delta_ids = state_updates ,
1943- partial_state = context .partial_state ,
1944- )
1945-
19461833 async def _run_push_actions_and_persist_event (
19471834 self , event : EventBase , context : EventContext , backfilled : bool = False
19481835 ) -> None :
0 commit comments