12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
+ import collections
15
16
import itertools
16
17
import logging
17
18
from http import HTTPStatus
@@ -347,7 +348,7 @@ async def on_send_membership_event(
347
348
event .internal_metadata .send_on_behalf_of = origin
348
349
349
350
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 )
351
352
if context .rejected :
352
353
raise SynapseError (
353
354
403 , f"{ event .membership } event was rejected" , Codes .FORBIDDEN
@@ -485,7 +486,7 @@ async def process_remote_join(
485
486
partial_state = partial_state ,
486
487
)
487
488
488
- context = await self ._check_event_auth (origin , event , context )
489
+ await self ._check_event_auth (origin , event , context )
489
490
if context .rejected :
490
491
raise SynapseError (400 , "Join event was rejected" )
491
492
@@ -1116,11 +1117,7 @@ async def _process_received_pdu(
1116
1117
state_ids_before_event = state_ids ,
1117
1118
)
1118
1119
try :
1119
- context = await self ._check_event_auth (
1120
- origin ,
1121
- event ,
1122
- context ,
1123
- )
1120
+ await self ._check_event_auth (origin , event , context )
1124
1121
except AuthError as e :
1125
1122
# This happens only if we couldn't find the auth events. We'll already have
1126
1123
# logged a warning, so now we just convert to a FederationError.
@@ -1495,11 +1492,8 @@ async def prep(event: EventBase) -> None:
1495
1492
)
1496
1493
1497
1494
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 :
1503
1497
"""
1504
1498
Checks whether an event should be rejected (for failing auth checks).
1505
1499
@@ -1509,9 +1503,6 @@ async def _check_event_auth(
1509
1503
context:
1510
1504
The event context.
1511
1505
1512
- Returns:
1513
- The updated context object.
1514
-
1515
1506
Raises:
1516
1507
AuthError if we were unable to find copies of the event's auth events.
1517
1508
(Most other failures just cause us to set `context.rejected`.)
@@ -1526,7 +1517,7 @@ async def _check_event_auth(
1526
1517
logger .warning ("While validating received event %r: %s" , event , e )
1527
1518
# TODO: use a different rejected reason here?
1528
1519
context .rejected = RejectedReason .AUTH_ERROR
1529
- return context
1520
+ return
1530
1521
1531
1522
# next, check that we have all of the event's auth events.
1532
1523
#
@@ -1538,6 +1529,9 @@ async def _check_event_auth(
1538
1529
)
1539
1530
1540
1531
# ... 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.
1541
1535
try :
1542
1536
await check_state_independent_auth_rules (self ._store , event )
1543
1537
check_state_dependent_auth_rules (event , claimed_auth_events )
@@ -1546,55 +1540,90 @@ async def _check_event_auth(
1546
1540
"While checking auth of %r against auth_events: %s" , event , e
1547
1541
)
1548
1542
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
+ )
1550
1578
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
1555
1581
)
1556
1582
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
1559
1592
)
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 }
1561
1597
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
1563
1599
}
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
+ )
1564
1616
1565
1617
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" ,
1567
1622
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 ,
1585
1624
)
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 )
1594
1625
context .rejected = RejectedReason .AUTH_ERROR
1595
1626
1596
- return context
1597
-
1598
1627
async def _maybe_kick_guest_users (self , event : EventBase ) -> None :
1599
1628
if event .type != EventTypes .GuestAccess :
1600
1629
return
@@ -1704,93 +1733,6 @@ async def _check_for_soft_fail(
1704
1733
soft_failed_event_counter .inc ()
1705
1734
event .internal_metadata .soft_failed = True
1706
1735
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
-
1794
1736
async def _load_or_fetch_auth_events_for_event (
1795
1737
self , destination : str , event : EventBase
1796
1738
) -> Collection [EventBase ]:
@@ -1888,61 +1830,6 @@ async def _get_remote_auth_chain_for_event(
1888
1830
1889
1831
await self ._auth_and_persist_outliers (room_id , remote_auth_events )
1890
1832
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
-
1946
1833
async def _run_push_actions_and_persist_event (
1947
1834
self , event : EventBase , context : EventContext , backfilled : bool = False
1948
1835
) -> None :
0 commit comments