From 85d032e705aabcf3448034181cfdb0d94abd7918 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 13 Jun 2022 00:39:27 +0100 Subject: [PATCH 1/5] Add auth events to events used in tests --- tests/test_event_auth.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/tests/test_event_auth.py b/tests/test_event_auth.py index 229ecd84a672..a45f26f7b7f0 100644 --- a/tests/test_event_auth.py +++ b/tests/test_event_auth.py @@ -13,7 +13,7 @@ # limitations under the License. import unittest -from typing import Optional +from typing import Iterable, List, Optional from parameterized import parameterized @@ -38,7 +38,7 @@ def test_rejected_auth_events(self): # creator should be able to send state event_auth.check_auth_rules_for_event( - _random_state_event(RoomVersions.V9, creator), + _random_state_event(RoomVersions.V9, creator, auth_events), auth_events, ) @@ -54,7 +54,7 @@ def test_rejected_auth_events(self): self.assertRaises( AuthError, event_auth.check_auth_rules_for_event, - _random_state_event(RoomVersions.V9, creator), + _random_state_event(RoomVersions.V9, creator, auth_events), auth_events, ) @@ -64,7 +64,7 @@ def test_rejected_auth_events(self): self.assertRaises( AuthError, event_auth.check_auth_rules_for_event, - _random_state_event(RoomVersions.V9, creator), + _random_state_event(RoomVersions.V9, creator, auth_events), auth_events, ) @@ -539,6 +539,7 @@ def _create_event( "state_key": "", "sender": user_id, "content": {"creator": user_id}, + "auth_events": [], }, room_version=room_version, ) @@ -559,6 +560,7 @@ def _member_event( "sender": sender or user_id, "state_key": user_id, "content": {"membership": membership, **(additional_content or {})}, + "auth_events": [], "prev_events": [], }, room_version=room_version, @@ -609,7 +611,22 @@ def _alias_event(room_version: RoomVersion, sender: str, **kwargs) -> EventBase: return make_event_from_dict(data, room_version=room_version) -def _random_state_event(room_version: RoomVersion, sender: str) -> EventBase: +def _build_auth_dict_for_room_version( + room_version: RoomVersion, auth_events: Iterable[EventBase] +) -> List: + if room_version.event_format == EventFormatVersions.V1: + return [(e.event_id, "not_used") for e in auth_events] + else: + return [e.event_id for e in auth_events] + + +def _random_state_event( + room_version: RoomVersion, + sender: str, + auth_events: Optional[Iterable[EventBase]] = None, +) -> EventBase: + if auth_events is None: + auth_events = [] return make_event_from_dict( { "room_id": TEST_ROOM_ID, @@ -618,6 +635,7 @@ def _random_state_event(room_version: RoomVersion, sender: str) -> EventBase: "sender": sender, "state_key": "", "content": {"membership": "join"}, + "auth_events": _build_auth_dict_for_room_version(room_version, auth_events), }, room_version=room_version, ) From 035c1258bc4b296b29a795f20e631198fc095db0 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 13 Jun 2022 00:19:49 +0100 Subject: [PATCH 2/5] Move some event auth checks out to a different method Some of the event auth checks apply to an event's auth_events, rather than the state at the event - which means they can play no part in state resolution. Move them out to a separate method. --- synapse/event_auth.py | 108 ++++++++++++++++++++------- synapse/handlers/event_auth.py | 6 +- synapse/handlers/federation_event.py | 17 +++-- tests/test_event_auth.py | 74 ++++++++++++++---- 4 files changed, 154 insertions(+), 51 deletions(-) diff --git a/synapse/event_auth.py b/synapse/event_auth.py index e23503c1e089..c165657fa6b2 100644 --- a/synapse/event_auth.py +++ b/synapse/event_auth.py @@ -15,11 +15,12 @@ import logging import typing -from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Union +from typing import Any, Collection, Dict, Iterable, List, Optional, Set, Tuple, Union from canonicaljson import encode_canonical_json from signedjson.key import decode_verify_key_bytes from signedjson.sign import SignatureVerifyException, verify_signed_json +from typing_extensions import Protocol from unpaddedbase64 import decode_base64 from synapse.api.constants import ( @@ -35,7 +36,8 @@ EventFormatVersions, RoomVersion, ) -from synapse.types import StateMap, UserID, get_domain_from_id +from synapse.storage.databases.main.events_worker import EventRedactBehaviour +from synapse.types import MutableStateMap, StateMap, UserID, get_domain_from_id if typing.TYPE_CHECKING: # conditional imports to avoid import cycle @@ -45,6 +47,17 @@ logger = logging.getLogger(__name__) +class _EventSourceStore(Protocol): + async def get_events( + self, + event_ids: Collection[str], + redact_behaviour: EventRedactBehaviour, + get_prev_content: bool = False, + allow_rejected: bool = False, + ) -> Dict[str, "EventBase"]: + ... + + def validate_event_for_room_version(event: "EventBase") -> None: """Ensure that the event complies with the limits, and has the right signatures @@ -112,47 +125,52 @@ def validate_event_for_room_version(event: "EventBase") -> None: raise AuthError(403, "Event not signed by authorising server") -def check_auth_rules_for_event( +async def check_state_independent_auth_rules( + store: _EventSourceStore, event: "EventBase", - auth_events: Iterable["EventBase"], ) -> None: - """Check that an event complies with the auth rules - - Checks whether an event passes the auth rules with a given set of state events - - Assumes that we have already checked that the event is the right shape (it has - enough signatures, has a room ID, etc). In other words: + """Check that an event complies with auth rules that are independent of room state - - it's fine for use in state resolution, when we have already decided whether to - accept the event or not, and are now trying to decide whether it should make it - into the room state - - - when we're doing the initial event auth, it is only suitable in combination with - a bunch of other tests. + Runs through the first few auth rules, which are independent of room state. (Which + means that we only need to them once for each received event) Args: + store: the datastore; used to fetch the auth events for validation event: the event being checked. - auth_events: the room state to check the events against. Raises: AuthError if the checks fail """ - # We need to ensure that the auth events are actually for the same room, to - # stop people from using powers they've been granted in other rooms for - # example. - # - # Arguably we don't need to do this when we're just doing state res, as presumably - # the state res algorithm isn't silly enough to give us events from different rooms. - # Still, it's easier to do it anyway. + # Check the auth events. + auth_events = await store.get_events( + event.auth_event_ids(), + redact_behaviour=EventRedactBehaviour.as_is, + allow_rejected=True, + ) room_id = event.room_id - for auth_event in auth_events: + auth_dict: MutableStateMap[str] = {} + for auth_event_id in event.auth_event_ids(): + auth_event = auth_events.get(auth_event_id) + + # we should have all the auth events by now, so if we do not, that suggests + # a synapse programming error + if auth_event is None: + raise RuntimeError( + f"Event {event.event_id} has unknown auth event {auth_event_id}" + ) + + # We need to ensure that the auth events are actually for the same room, to + # stop people from using powers they've been granted in other rooms for + # example. if auth_event.room_id != room_id: raise AuthError( 403, "During auth for event %s in room %s, found event %s in the state " "which is in room %s" - % (event.event_id, room_id, auth_event.event_id, auth_event.room_id), + % (event.event_id, room_id, auth_event_id, auth_event.room_id), ) + + # We also need to check that the auth event itself is not rejected. if auth_event.rejected_reason: raise AuthError( 403, @@ -160,6 +178,8 @@ def check_auth_rules_for_event( % (event.event_id, auth_event.event_id), ) + auth_dict[(auth_event.type, auth_event.state_key)] = auth_event_id + # Implementation of https://matrix.org/docs/spec/rooms/v1#authorization-rules # # 1. If type is m.room.create: @@ -181,16 +201,46 @@ def check_auth_rules_for_event( "room appears to have unsupported version %s" % (room_version_prop,), ) - logger.debug("Allowing! %s", event) return - auth_dict = {(e.type, e.state_key): e for e in auth_events} - # 3. If event does not have a m.room.create in its auth_events, reject. creation_event = auth_dict.get((EventTypes.Create, ""), None) if not creation_event: raise AuthError(403, "No create event in auth events") + +def check_auth_rules_for_event( + event: "EventBase", + auth_events: Iterable["EventBase"], +) -> None: + """Check that an event complies with auth rules that depend on room state + + Runs through the parts of the auth rules that check an event against bits of room + state. + + Note: + + - it's fine for use in state resolution, when we have already decided whether to + accept the event or not, and are now trying to decide whether it should make it + into the room state + + - when we're doing the initial event auth, it is only suitable in combination with + a bunch of other tests (including, but not limited to, check_state_independent_auth_rules). + + Args: + event: the event being checked. + auth_events: the room state to check the events against. + + Raises: + AuthError if the checks fail + """ + # there are no state-dependent auth rules for create events. + if event.type == EventTypes.Create: + logger.debug("Allowing! %s", event) + return + + auth_dict = {(e.type, e.state_key): e for e in auth_events} + # additional check for m.federate creating_domain = get_domain_from_id(event.room_id) originating_domain = get_domain_from_id(event.sender) diff --git a/synapse/handlers/event_auth.py b/synapse/handlers/event_auth.py index ed4149bd589c..511bbc38a1c5 100644 --- a/synapse/handlers/event_auth.py +++ b/synapse/handlers/event_auth.py @@ -23,7 +23,10 @@ ) from synapse.api.errors import AuthError, Codes, SynapseError from synapse.api.room_versions import RoomVersion -from synapse.event_auth import check_auth_rules_for_event +from synapse.event_auth import ( + check_auth_rules_for_event, + check_state_independent_auth_rules, +) from synapse.events import EventBase from synapse.events.builder import EventBuilder from synapse.events.snapshot import EventContext @@ -52,6 +55,7 @@ async def check_auth_rules_from_context( context: EventContext, ) -> None: """Check an event passes the auth rules at its own auth events""" + await check_state_independent_auth_rules(self._store, event) auth_event_ids = event.auth_event_ids() auth_events_by_id = await self._store.get_events(auth_event_ids) check_auth_rules_for_event(event, auth_events_by_id.values()) diff --git a/synapse/handlers/federation_event.py b/synapse/handlers/federation_event.py index 6c9e6a00b599..e7fc7d20308f 100644 --- a/synapse/handlers/federation_event.py +++ b/synapse/handlers/federation_event.py @@ -51,6 +51,7 @@ from synapse.event_auth import ( auth_types_for_event, check_auth_rules_for_event, + check_state_independent_auth_rules, validate_event_for_room_version, ) from synapse.events import EventBase @@ -1430,7 +1431,9 @@ async def _auth_and_persist_outliers_inner( allow_rejected=True, ) - def prep(event: EventBase) -> Optional[Tuple[EventBase, EventContext]]: + events_and_contexts_to_persist: List[Tuple[EventBase, EventContext]] = [] + + async def prep(event: EventBase) -> None: with nested_logging_context(suffix=event.event_id): auth = [] for auth_event_id in event.auth_event_ids(): @@ -1444,7 +1447,7 @@ def prep(event: EventBase) -> Optional[Tuple[EventBase, EventContext]]: event, auth_event_id, ) - return None + return auth.append(ae) # we're not bothering about room state, so flag the event as an outlier. @@ -1453,17 +1456,20 @@ def prep(event: EventBase) -> Optional[Tuple[EventBase, EventContext]]: context = EventContext.for_outlier(self._storage_controllers) try: validate_event_for_room_version(event) + await check_state_independent_auth_rules(self._store, event) check_auth_rules_for_event(event, auth) except AuthError as e: logger.warning("Rejecting %r because %s", event, e) context.rejected = RejectedReason.AUTH_ERROR - return event, context + events_and_contexts_to_persist.append((event, context)) + + for event in fetched_events: + await prep(event) - events_to_persist = (x for x in (prep(event) for event in fetched_events) if x) await self.persist_events_and_notify( room_id, - tuple(events_to_persist), + events_and_contexts_to_persist, # Mark these events backfilled as they're historic events that will # eventually be backfilled. For example, missing events we fetch # during backfill should be marked as backfilled as well. @@ -1515,6 +1521,7 @@ async def _check_event_auth( # ... and check that the event passes auth at those auth events. try: + await check_state_independent_auth_rules(self._store, event) check_auth_rules_for_event(event, claimed_auth_events) except AuthError as e: logger.warning( diff --git a/tests/test_event_auth.py b/tests/test_event_auth.py index a45f26f7b7f0..7e11e1982160 100644 --- a/tests/test_event_auth.py +++ b/tests/test_event_auth.py @@ -1,4 +1,4 @@ -# Copyright 2018 New Vector Ltd +# Copyright 2018-2022 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import typing import unittest from typing import Iterable, List, Optional @@ -22,8 +23,41 @@ from synapse.api.errors import AuthError from synapse.api.room_versions import EventFormatVersions, RoomVersion, RoomVersions from synapse.events import EventBase, make_event_from_dict +from synapse.storage.databases.main.events_worker import EventRedactBehaviour from synapse.types import JsonDict, get_domain_from_id +from tests.test_utils import get_awaitable_result + + +class _StubEventSourceStore: + """A stub implementation of the EventSourceStore""" + + def __init__(self): + self._store: typing.Dict[str, EventBase] = {} + + def add_event(self, event: EventBase): + self._store[event.event_id] = event + + def add_events(self, events: typing.Iterable[EventBase]): + for event in events: + self._store[event.event_id] = event + + async def get_events( + self, + event_ids: typing.Collection[str], + redact_behaviour: EventRedactBehaviour, + get_prev_content: bool = False, + allow_rejected: bool = False, + ) -> typing.Dict[str, EventBase]: + assert allow_rejected + assert not get_prev_content + assert redact_behaviour == EventRedactBehaviour.as_is + results = {} + for e in event_ids: + if e in self._store: + results[e] = self._store[e] + return results + class EventAuthTestCase(unittest.TestCase): def test_rejected_auth_events(self): @@ -36,11 +70,15 @@ def test_rejected_auth_events(self): _join_event(RoomVersions.V9, creator), ] + event_store = _StubEventSourceStore() + event_store.add_events(auth_events) + # creator should be able to send state - event_auth.check_auth_rules_for_event( - _random_state_event(RoomVersions.V9, creator, auth_events), - auth_events, + event = _random_state_event(RoomVersions.V9, creator, auth_events) + get_awaitable_result( + event_auth.check_state_independent_auth_rules(event_store, event) ) + event_auth.check_auth_rules_for_event(event, auth_events) # ... but a rejected join_rules event should cause it to be rejected rejected_join_rules = _join_rules_event( @@ -50,23 +88,27 @@ def test_rejected_auth_events(self): ) rejected_join_rules.rejected_reason = "stinky" auth_events.append(rejected_join_rules) + event_store.add_event(rejected_join_rules) - self.assertRaises( - AuthError, - event_auth.check_auth_rules_for_event, - _random_state_event(RoomVersions.V9, creator, auth_events), - auth_events, - ) + with self.assertRaises(AuthError): + get_awaitable_result( + event_auth.check_state_independent_auth_rules( + event_store, + _random_state_event(RoomVersions.V9, creator), + ) + ) # ... even if there is *also* a good join rules auth_events.append(_join_rules_event(RoomVersions.V9, creator, "public")) + event_store.add_event(rejected_join_rules) - self.assertRaises( - AuthError, - event_auth.check_auth_rules_for_event, - _random_state_event(RoomVersions.V9, creator, auth_events), - auth_events, - ) + with self.assertRaises(AuthError): + get_awaitable_result( + event_auth.check_state_independent_auth_rules( + event_store, + _random_state_event(RoomVersions.V9, creator), + ) + ) def test_random_users_cannot_send_state_before_first_pl(self): """ From d731b2220581c9bbd0cbca47802d189a547506cc Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 15 Jun 2022 14:00:38 +0100 Subject: [PATCH 3/5] Rename check_auth_rules_for_event Now it only checks the state-dependent auth rules, it needs a better name. --- synapse/event_auth.py | 2 +- synapse/handlers/event_auth.py | 4 +- synapse/handlers/federation_event.py | 10 ++-- synapse/state/v1.py | 4 +- synapse/state/v2.py | 2 +- tests/test_event_auth.py | 74 ++++++++++++++-------------- 6 files changed, 48 insertions(+), 48 deletions(-) diff --git a/synapse/event_auth.py b/synapse/event_auth.py index c165657fa6b2..360a50cc719c 100644 --- a/synapse/event_auth.py +++ b/synapse/event_auth.py @@ -209,7 +209,7 @@ async def check_state_independent_auth_rules( raise AuthError(403, "No create event in auth events") -def check_auth_rules_for_event( +def check_state_dependent_auth_rules( event: "EventBase", auth_events: Iterable["EventBase"], ) -> None: diff --git a/synapse/handlers/event_auth.py b/synapse/handlers/event_auth.py index 511bbc38a1c5..a2dd9c7efabf 100644 --- a/synapse/handlers/event_auth.py +++ b/synapse/handlers/event_auth.py @@ -24,7 +24,7 @@ from synapse.api.errors import AuthError, Codes, SynapseError from synapse.api.room_versions import RoomVersion from synapse.event_auth import ( - check_auth_rules_for_event, + check_state_dependent_auth_rules, check_state_independent_auth_rules, ) from synapse.events import EventBase @@ -58,7 +58,7 @@ async def check_auth_rules_from_context( await check_state_independent_auth_rules(self._store, event) auth_event_ids = event.auth_event_ids() auth_events_by_id = await self._store.get_events(auth_event_ids) - check_auth_rules_for_event(event, auth_events_by_id.values()) + check_state_dependent_auth_rules(event, auth_events_by_id.values()) def compute_auth_events( self, diff --git a/synapse/handlers/federation_event.py b/synapse/handlers/federation_event.py index e7fc7d20308f..565ffd7cfdb8 100644 --- a/synapse/handlers/federation_event.py +++ b/synapse/handlers/federation_event.py @@ -50,7 +50,7 @@ from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion, RoomVersions from synapse.event_auth import ( auth_types_for_event, - check_auth_rules_for_event, + check_state_dependent_auth_rules, check_state_independent_auth_rules, validate_event_for_room_version, ) @@ -1457,7 +1457,7 @@ async def prep(event: EventBase) -> None: try: validate_event_for_room_version(event) await check_state_independent_auth_rules(self._store, event) - check_auth_rules_for_event(event, auth) + check_state_dependent_auth_rules(event, auth) except AuthError as e: logger.warning("Rejecting %r because %s", event, e) context.rejected = RejectedReason.AUTH_ERROR @@ -1522,7 +1522,7 @@ async def _check_event_auth( # ... and check that the event passes auth at those auth events. try: await check_state_independent_auth_rules(self._store, event) - check_auth_rules_for_event(event, claimed_auth_events) + check_state_dependent_auth_rules(event, claimed_auth_events) except AuthError as e: logger.warning( "While checking auth of %r against auth_events: %s", event, e @@ -1570,7 +1570,7 @@ async def _check_event_auth( auth_events_for_auth = calculated_auth_event_map try: - check_auth_rules_for_event(event, auth_events_for_auth.values()) + check_state_dependent_auth_rules(event, auth_events_for_auth.values()) except AuthError as e: logger.warning("Failed auth resolution for %r because %s", event, e) context.rejected = RejectedReason.AUTH_ERROR @@ -1670,7 +1670,7 @@ async def _check_for_soft_fail( ) try: - check_auth_rules_for_event(event, current_auth_events) + check_state_dependent_auth_rules(event, current_auth_events) except AuthError as e: logger.warning( "Soft-failing %r (from %s) because %s", diff --git a/synapse/state/v1.py b/synapse/state/v1.py index 8bbb4ce41ccf..500e38469537 100644 --- a/synapse/state/v1.py +++ b/synapse/state/v1.py @@ -330,7 +330,7 @@ def _resolve_auth_events( auth_events[(prev_event.type, prev_event.state_key)] = prev_event try: # The signatures have already been checked at this point - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( event, auth_events.values(), ) @@ -347,7 +347,7 @@ def _resolve_normal_events( for event in _ordered_events(events): try: # The signatures have already been checked at this point - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( event, auth_events.values(), ) diff --git a/synapse/state/v2.py b/synapse/state/v2.py index 6a16f38a15c9..7db032203b5e 100644 --- a/synapse/state/v2.py +++ b/synapse/state/v2.py @@ -573,7 +573,7 @@ async def _iterative_auth_checks( auth_events[key] = event_map[ev_id] try: - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( event, auth_events.values(), ) diff --git a/tests/test_event_auth.py b/tests/test_event_auth.py index 7e11e1982160..2b60235e855b 100644 --- a/tests/test_event_auth.py +++ b/tests/test_event_auth.py @@ -78,7 +78,7 @@ def test_rejected_auth_events(self): get_awaitable_result( event_auth.check_state_independent_auth_rules(event_store, event) ) - event_auth.check_auth_rules_for_event(event, auth_events) + event_auth.check_state_dependent_auth_rules(event, auth_events) # ... but a rejected join_rules event should cause it to be rejected rejected_join_rules = _join_rules_event( @@ -124,7 +124,7 @@ def test_random_users_cannot_send_state_before_first_pl(self): ] # creator should be able to send state - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _random_state_event(RoomVersions.V1, creator), auth_events, ) @@ -132,7 +132,7 @@ def test_random_users_cannot_send_state_before_first_pl(self): # joiner should not be able to send state self.assertRaises( AuthError, - event_auth.check_auth_rules_for_event, + event_auth.check_state_dependent_auth_rules, _random_state_event(RoomVersions.V1, joiner), auth_events, ) @@ -161,13 +161,13 @@ def test_state_default_level(self): # pleb should not be able to send state self.assertRaises( AuthError, - event_auth.check_auth_rules_for_event, + event_auth.check_state_dependent_auth_rules, _random_state_event(RoomVersions.V1, pleb), auth_events, ), # king should be able to send state - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _random_state_event(RoomVersions.V1, king), auth_events, ) @@ -182,27 +182,27 @@ def test_alias_event(self): ] # creator should be able to send aliases - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _alias_event(RoomVersions.V1, creator), auth_events, ) # Reject an event with no state key. with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _alias_event(RoomVersions.V1, creator, state_key=""), auth_events, ) # If the domain of the sender does not match the state key, reject. with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _alias_event(RoomVersions.V1, creator, state_key="test.com"), auth_events, ) # Note that the member does *not* need to be in the room. - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _alias_event(RoomVersions.V1, other), auth_events, ) @@ -217,24 +217,24 @@ def test_msc2432_alias_event(self): ] # creator should be able to send aliases - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _alias_event(RoomVersions.V6, creator), auth_events, ) # No particular checks are done on the state key. - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _alias_event(RoomVersions.V6, creator, state_key=""), auth_events, ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _alias_event(RoomVersions.V6, creator, state_key="test.com"), auth_events, ) # Per standard auth rules, the member must be in the room. with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _alias_event(RoomVersions.V6, other), auth_events, ) @@ -262,12 +262,12 @@ def test_notifications(self, room_version: RoomVersion, allow_modification: bool # on room V1, pleb should be able to modify the notifications power level. if allow_modification: - event_auth.check_auth_rules_for_event(pl_event, auth_events) + event_auth.check_state_dependent_auth_rules(pl_event, auth_events) else: # But an MSC2209 room rejects this change. with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event(pl_event, auth_events) + event_auth.check_state_dependent_auth_rules(pl_event, auth_events) def test_join_rules_public(self): """ @@ -285,14 +285,14 @@ def test_join_rules_public(self): } # Check join. - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) # A user cannot be force-joined to a room. with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _member_event(RoomVersions.V6, pleb, "join", sender=creator), auth_events.values(), ) @@ -302,7 +302,7 @@ def test_join_rules_public(self): RoomVersions.V6, pleb, "ban" ) with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) @@ -311,7 +311,7 @@ def test_join_rules_public(self): auth_events[("m.room.member", pleb)] = _member_event( RoomVersions.V6, pleb, "leave" ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) @@ -320,7 +320,7 @@ def test_join_rules_public(self): auth_events[("m.room.member", pleb)] = _member_event( RoomVersions.V6, pleb, "join" ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) @@ -329,7 +329,7 @@ def test_join_rules_public(self): auth_events[("m.room.member", pleb)] = _member_event( RoomVersions.V6, pleb, "invite", sender=creator ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) @@ -351,14 +351,14 @@ def test_join_rules_invite(self): # A join without an invite is rejected. with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) # A user cannot be force-joined to a room. with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _member_event(RoomVersions.V6, pleb, "join", sender=creator), auth_events.values(), ) @@ -368,7 +368,7 @@ def test_join_rules_invite(self): RoomVersions.V6, pleb, "ban" ) with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) @@ -378,7 +378,7 @@ def test_join_rules_invite(self): RoomVersions.V6, pleb, "leave" ) with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) @@ -387,7 +387,7 @@ def test_join_rules_invite(self): auth_events[("m.room.member", pleb)] = _member_event( RoomVersions.V6, pleb, "join" ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) @@ -396,7 +396,7 @@ def test_join_rules_invite(self): auth_events[("m.room.member", pleb)] = _member_event( RoomVersions.V6, pleb, "invite", sender=creator ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) @@ -418,7 +418,7 @@ def test_join_rules_restricted_old_room(self) -> None: } with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V6, pleb), auth_events.values(), ) @@ -455,7 +455,7 @@ def test_join_rules_msc3083_restricted(self) -> None: EventContentFields.AUTHORISING_USER: "@creator:example.com" }, ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( authorised_join_event, auth_events.values(), ) @@ -471,7 +471,7 @@ def test_join_rules_msc3083_restricted(self) -> None: pl_auth_events[("m.room.member", "@inviter:foo.test")] = _join_event( RoomVersions.V8, "@inviter:foo.test" ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event( RoomVersions.V8, pleb, @@ -484,7 +484,7 @@ def test_join_rules_msc3083_restricted(self) -> None: # A join which is missing an authorised server is rejected. with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V8, pleb), auth_events.values(), ) @@ -497,7 +497,7 @@ def test_join_rules_msc3083_restricted(self) -> None: {"invite": 100, "users": {"@other:example.com": 150}}, ) with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event( RoomVersions.V8, pleb, @@ -511,7 +511,7 @@ def test_join_rules_msc3083_restricted(self) -> None: # A user cannot be force-joined to a room. (This uses an event which # *would* be valid, but is sent be a different user.) with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _member_event( RoomVersions.V8, pleb, @@ -529,7 +529,7 @@ def test_join_rules_msc3083_restricted(self) -> None: RoomVersions.V8, pleb, "ban" ) with self.assertRaises(AuthError): - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( authorised_join_event, auth_events.values(), ) @@ -538,7 +538,7 @@ def test_join_rules_msc3083_restricted(self) -> None: auth_events[("m.room.member", pleb)] = _member_event( RoomVersions.V8, pleb, "leave" ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( authorised_join_event, auth_events.values(), ) @@ -548,7 +548,7 @@ def test_join_rules_msc3083_restricted(self) -> None: auth_events[("m.room.member", pleb)] = _member_event( RoomVersions.V8, pleb, "join" ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V8, pleb), auth_events.values(), ) @@ -558,7 +558,7 @@ def test_join_rules_msc3083_restricted(self) -> None: auth_events[("m.room.member", pleb)] = _member_event( RoomVersions.V8, pleb, "invite", sender=creator ) - event_auth.check_auth_rules_for_event( + event_auth.check_state_dependent_auth_rules( _join_event(RoomVersions.V8, pleb), auth_events.values(), ) From 04b217bfc286d13b4040931bc3582e6c905c3c2b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 15 Jun 2022 13:58:11 +0100 Subject: [PATCH 4/5] changelog --- changelog.d/13065.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/13065.misc diff --git a/changelog.d/13065.misc b/changelog.d/13065.misc new file mode 100644 index 000000000000..e9e8a7659a04 --- /dev/null +++ b/changelog.d/13065.misc @@ -0,0 +1 @@ +Avoid rechecking event auth rules which are independent of room state. From 543938aa2b3cccc1ff6f2da55f7bfa46c2aa9302 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 15 Jun 2022 18:57:41 +0100 Subject: [PATCH 5/5] clean up typing import --- tests/test_event_auth.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/test_event_auth.py b/tests/test_event_auth.py index 2b60235e855b..e8e458cfd3ce 100644 --- a/tests/test_event_auth.py +++ b/tests/test_event_auth.py @@ -12,9 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import typing import unittest -from typing import Iterable, List, Optional +from typing import Collection, Dict, Iterable, List, Optional from parameterized import parameterized @@ -33,22 +32,22 @@ class _StubEventSourceStore: """A stub implementation of the EventSourceStore""" def __init__(self): - self._store: typing.Dict[str, EventBase] = {} + self._store: Dict[str, EventBase] = {} def add_event(self, event: EventBase): self._store[event.event_id] = event - def add_events(self, events: typing.Iterable[EventBase]): + def add_events(self, events: Iterable[EventBase]): for event in events: self._store[event.event_id] = event async def get_events( self, - event_ids: typing.Collection[str], + event_ids: Collection[str], redact_behaviour: EventRedactBehaviour, get_prev_content: bool = False, allow_rejected: bool = False, - ) -> typing.Dict[str, EventBase]: + ) -> Dict[str, EventBase]: assert allow_rejected assert not get_prev_content assert redact_behaviour == EventRedactBehaviour.as_is