@@ -1256,6 +1256,10 @@ async def _check_event_auth(
1256
1256
1257
1257
Returns:
1258
1258
The updated context object.
1259
+
1260
+ Raises:
1261
+ AuthError if we were unable to find copies of the event's auth events.
1262
+ (Most other failures just cause us to set `context.rejected`.)
1259
1263
"""
1260
1264
# This method should only be used for non-outliers
1261
1265
assert not event .internal_metadata .outlier
@@ -1272,7 +1276,26 @@ async def _check_event_auth(
1272
1276
context .rejected = RejectedReason .AUTH_ERROR
1273
1277
return context
1274
1278
1275
- # calculate what the auth events *should* be, to use as a basis for auth.
1279
+ # next, check that we have all of the event's auth events.
1280
+ #
1281
+ # Note that this can raise AuthError, which we want to propagate to the
1282
+ # caller rather than swallow with `context.rejected` (since we cannot be
1283
+ # certain that there is a permanent problem with the event).
1284
+ claimed_auth_events = await self ._load_or_fetch_auth_events_for_event (
1285
+ origin , event
1286
+ )
1287
+
1288
+ # ... and check that the event passes auth at those auth events.
1289
+ try :
1290
+ check_auth_rules_for_event (room_version_obj , event , claimed_auth_events )
1291
+ except AuthError as e :
1292
+ logger .warning (
1293
+ "While checking auth of %r against auth_events: %s" , event , e
1294
+ )
1295
+ context .rejected = RejectedReason .AUTH_ERROR
1296
+ return context
1297
+
1298
+ # now check auth against what we think the auth events *should* be.
1276
1299
prev_state_ids = await context .get_prev_state_ids ()
1277
1300
auth_events_ids = self ._event_auth_handler .compute_auth_events (
1278
1301
event , prev_state_ids , for_verification = True
@@ -1472,6 +1495,9 @@ async def _update_auth_events_and_context_for_auth(
1472
1495
# if we have missing events, we need to fetch those events from somewhere.
1473
1496
#
1474
1497
# we start by checking if they are in the store, and then try calling /event_auth/.
1498
+ #
1499
+ # TODO: this code is now redundant, since it should be impossible for us to
1500
+ # get here without already having the auth events.
1475
1501
if missing_auth :
1476
1502
have_events = await self ._store .have_seen_events (
1477
1503
event .room_id , missing_auth
@@ -1575,7 +1601,7 @@ async def _update_auth_events_and_context_for_auth(
1575
1601
logger .info (
1576
1602
"After state res: updating auth_events with new state %s" ,
1577
1603
{
1578
- ( d . type , d . state_key ): d . event_id
1604
+ d
1579
1605
for d in new_state .values ()
1580
1606
if auth_events .get ((d .type , d .state_key )) != d
1581
1607
},
@@ -1589,6 +1615,75 @@ async def _update_auth_events_and_context_for_auth(
1589
1615
1590
1616
return context , auth_events
1591
1617
1618
+ async def _load_or_fetch_auth_events_for_event (
1619
+ self , destination : str , event : EventBase
1620
+ ) -> Collection [EventBase ]:
1621
+ """Fetch this event's auth_events, from database or remote
1622
+
1623
+ Loads any of the auth_events that we already have from the database/cache. If
1624
+ there are any that are missing, calls /event_auth to get the complete auth
1625
+ chain for the event (and then attempts to load the auth_events again).
1626
+
1627
+ If any of the auth_events cannot be found, raises an AuthError. This can happen
1628
+ for a number of reasons; eg: the events don't exist, or we were unable to talk
1629
+ to `destination`, or we couldn't validate the signature on the event (which
1630
+ in turn has multiple potential causes).
1631
+
1632
+ Args:
1633
+ destination: where to send the /event_auth request. Typically the server
1634
+ that sent us `event` in the first place.
1635
+ event: the event whose auth_events we want
1636
+
1637
+ Returns:
1638
+ all of the events in `event.auth_events`, after deduplication
1639
+
1640
+ Raises:
1641
+ AuthError if we were unable to fetch the auth_events for any reason.
1642
+ """
1643
+ event_auth_event_ids = set (event .auth_event_ids ())
1644
+ event_auth_events = await self ._store .get_events (
1645
+ event_auth_event_ids , allow_rejected = True
1646
+ )
1647
+ missing_auth_event_ids = event_auth_event_ids .difference (
1648
+ event_auth_events .keys ()
1649
+ )
1650
+ if not missing_auth_event_ids :
1651
+ return event_auth_events .values ()
1652
+
1653
+ logger .info (
1654
+ "Event %s refers to unknown auth events %s: fetching auth chain" ,
1655
+ event ,
1656
+ missing_auth_event_ids ,
1657
+ )
1658
+ try :
1659
+ await self ._get_remote_auth_chain_for_event (
1660
+ destination , event .room_id , event .event_id
1661
+ )
1662
+ except Exception as e :
1663
+ logger .warning ("Failed to get auth chain for %s: %s" , event , e )
1664
+ # in this case, it's very likely we still won't have all the auth
1665
+ # events - but we pick that up below.
1666
+
1667
+ # try to fetch the auth events we missed list time.
1668
+ extra_auth_events = await self ._store .get_events (
1669
+ missing_auth_event_ids , allow_rejected = True
1670
+ )
1671
+ missing_auth_event_ids .difference_update (extra_auth_events .keys ())
1672
+ event_auth_events .update (extra_auth_events )
1673
+ if not missing_auth_event_ids :
1674
+ return event_auth_events .values ()
1675
+
1676
+ # we still don't have all the auth events.
1677
+ logger .warning (
1678
+ "Missing auth events for %s: %s" ,
1679
+ event ,
1680
+ shortstr (missing_auth_event_ids ),
1681
+ )
1682
+ # the fact we can't find the auth event doesn't mean it doesn't
1683
+ # exist, which means it is premature to store `event` as rejected.
1684
+ # instead we raise an AuthError, which will make the caller ignore it.
1685
+ raise AuthError (code = HTTPStatus .FORBIDDEN , msg = "Auth events could not be found" )
1686
+
1592
1687
async def _get_remote_auth_chain_for_event (
1593
1688
self , destination : str , room_id : str , event_id : str
1594
1689
) -> None :
0 commit comments