From e3b59aa38c09a5fcd8b4a09d78c0752612ebb5f2 Mon Sep 17 00:00:00 2001 From: razvp Date: Mon, 27 Oct 2025 15:51:49 +0200 Subject: [PATCH] Add option to skip check_event_allowed callback for room creation events --- synapse/handlers/message.py | 40 ++++++++++++++++++++------------- synapse/handlers/room.py | 6 +++++ synapse/handlers/room_member.py | 18 ++++++++++++++- synapse/rest/client/room.py | 6 ++++- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 2ad1dbe73f3..394a9d88275 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -587,6 +587,7 @@ async def create_event( state_map: Optional[StateMap[str]] = None, for_batch: bool = False, current_state_group: Optional[int] = None, + skip_check_event_allowed: bool = False, ) -> tuple[EventBase, UnpersistedEventContextBase]: """ Given a dict from a client, create a new event. If bool for_batch is true, will @@ -740,6 +741,7 @@ async def create_event( state_map=state_map, for_batch=for_batch, current_state_group=current_state_group, + skip_check_event_allowed=skip_check_event_allowed, ) # In an ideal world we wouldn't need the second part of this condition. However, @@ -968,6 +970,7 @@ async def create_and_send_nonmember_event( ignore_shadow_ban: bool = False, outlier: bool = False, depth: Optional[int] = None, + skip_check_event_allowed: bool = False, ) -> tuple[EventBase, int]: """ Creates an event, then sends it. @@ -1092,6 +1095,7 @@ async def create_and_send_nonmember_event( ignore_shadow_ban=ignore_shadow_ban, outlier=outlier, depth=depth, + skip_check_event_allowed=skip_check_event_allowed, ) async def _create_and_send_nonmember_event_locked( @@ -1105,6 +1109,7 @@ async def _create_and_send_nonmember_event_locked( ignore_shadow_ban: bool = False, outlier: bool = False, depth: Optional[int] = None, + skip_check_event_allowed: bool = False, ) -> tuple[EventBase, int]: room_id = event_dict["room_id"] @@ -1133,6 +1138,7 @@ async def _create_and_send_nonmember_event_locked( state_event_ids=state_event_ids, outlier=outlier, depth=depth, + skip_check_event_allowed=skip_check_event_allowed, ) context = await unpersisted_context.persist(event) @@ -1227,6 +1233,7 @@ async def create_new_client_event( state_map: Optional[StateMap[str]] = None, for_batch: bool = False, current_state_group: Optional[int] = None, + skip_check_event_allowed: bool = False, ) -> tuple[EventBase, UnpersistedEventContextBase]: """Create a new event for a local client. If bool for_batch is true, will create an event using the prev_event_ids, and will create an event context for @@ -1374,23 +1381,24 @@ async def create_new_client_event( if requester: context.app_service = requester.app_service - res, new_content = await self._third_party_event_rules.check_event_allowed( - event, context - ) - if res is False: - logger.info( - "Event %s forbidden by third-party rules", - event, - ) - raise SynapseError( - 403, "This event is not allowed in this context", Codes.FORBIDDEN - ) - elif new_content is not None: - # the third-party rules want to replace the event. We'll need to build a new - # event. - event, context = await self._rebuild_event_after_third_party_rules( - new_content, event + if not skip_check_event_allowed: + res, new_content = await self._third_party_event_rules.check_event_allowed( + event, context ) + if res is False: + logger.info( + "Event %s forbidden by third-party rules", + event, + ) + raise SynapseError( + 403, "This event is not allowed in this context", Codes.FORBIDDEN + ) + elif new_content is not None: + # the third-party rules want to replace the event. We'll need to build a new + # event. + event, context = await self._rebuild_event_after_third_party_rules( + new_content, event + ) self.validator.validate_new(event, self.config) await self._validate_event_relation(event) diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index f242accef18..cc2cb736514 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -1041,6 +1041,7 @@ async def create_room( ratelimit: bool = True, creator_join_profile: Optional[JsonDict] = None, ignore_forced_encryption: bool = False, + skip_check_event_allowed: bool = False, ) -> tuple[str, Optional[RoomAlias], int]: """Creates a new room. @@ -1320,6 +1321,7 @@ async def create_room( creator_join_profile=creator_join_profile, ignore_forced_encryption=ignore_forced_encryption, creation_event_with_context=creation_event_with_context, + skip_check_event_allowed=skip_check_event_allowed, ) # we avoid dropping the lock between invites, as otherwise joins can @@ -1347,6 +1349,7 @@ async def create_room( new_room=True, prev_event_ids=[last_sent_event_id], depth=depth, + skip_check_event_allowed=skip_check_event_allowed, ) last_sent_event_id = member_event_id depth += 1 @@ -1372,6 +1375,7 @@ async def create_room( id_access_token=id_access_token, prev_event_ids=[last_sent_event_id], depth=depth, + skip_check_event_allowed=skip_check_event_allowed, ) last_sent_event_id = member_event_id depth += 1 @@ -1433,6 +1437,7 @@ async def _send_events_for_new_room( creation_event_with_context: Optional[ tuple[EventBase, synapse.events.snapshot.EventContext] ] = None, + skip_check_event_allowed: bool = False, ) -> tuple[int, str, int]: """Sends the initial events into a new room. Sends the room creation, membership, and power level events into the room sequentially, then creates and batches up the @@ -1527,6 +1532,7 @@ async def create_event( # state_map since it is modified below. state_map=dict(state_map), for_batch=for_batch, + skip_check_event_allowed=skip_check_event_allowed, ) depth += 1 diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index 03cfc992605..8f5cc2b03c8 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -405,6 +405,7 @@ async def _local_membership_update( require_consent: bool = True, outlier: bool = False, origin_server_ts: Optional[int] = None, + skip_check_event_allowed: bool = False, ) -> tuple[str, int]: """ Internal membership update function to get an existing event or create @@ -489,6 +490,7 @@ async def _local_membership_update( depth=depth, require_consent=require_consent, outlier=outlier, + skip_check_event_allowed=skip_check_event_allowed, ) context = await unpersisted_context.persist(event) prev_state_ids = await context.get_prev_state_ids( @@ -584,6 +586,7 @@ async def update_membership( state_event_ids: Optional[list[str]] = None, depth: Optional[int] = None, origin_server_ts: Optional[int] = None, + skip_check_event_allowed: bool = False, ) -> tuple[str, int]: """Update a user's membership in a room. @@ -676,6 +679,7 @@ async def update_membership( state_event_ids=state_event_ids, depth=depth, origin_server_ts=origin_server_ts, + skip_check_event_allowed=skip_check_event_allowed, ) return result @@ -698,6 +702,7 @@ async def update_membership_locked( state_event_ids: Optional[list[str]] = None, depth: Optional[int] = None, origin_server_ts: Optional[int] = None, + skip_check_event_allowed: bool = False, ) -> tuple[str, int]: """Helper for update_membership. @@ -940,6 +945,7 @@ async def update_membership_locked( require_consent=require_consent, outlier=outlier, origin_server_ts=origin_server_ts, + skip_check_event_allowed=skip_check_event_allowed, ) latest_event_ids = await self.store.get_prev_events_for_room(room_id) @@ -1198,6 +1204,7 @@ async def update_membership_locked( require_consent=require_consent, outlier=outlier, origin_server_ts=origin_server_ts, + skip_check_event_allowed=skip_check_event_allowed, ) async def check_for_any_membership_in_room( @@ -1614,6 +1621,7 @@ async def do_3pid_invite( id_access_token: str, prev_event_ids: Optional[list[str]] = None, depth: Optional[int] = None, + skip_check_event_allowed: bool = False, ) -> tuple[str, int]: """Invite a 3PID to a room. @@ -1680,7 +1688,12 @@ async def do_3pid_invite( # user_may_invite) because we'll do it further down the line anyway (in # update_membership_locked). event_id, stream_id = await self.update_membership( - requester, UserID.from_string(invitee), room_id, "invite", txn_id=txn_id + requester, + UserID.from_string(invitee), + room_id, + "invite", + txn_id=txn_id, + skip_check_event_allowed=skip_check_event_allowed, ) else: # Check if the spamchecker(s) allow this invite to go through. @@ -1711,6 +1724,7 @@ async def do_3pid_invite( id_access_token=id_access_token, prev_event_ids=prev_event_ids, depth=depth, + skip_check_event_allowed=skip_check_event_allowed, ) event_id = event.event_id @@ -1728,6 +1742,7 @@ async def _make_and_store_3pid_invite( id_access_token: str, prev_event_ids: Optional[list[str]] = None, depth: Optional[int] = None, + skip_check_event_allowed: bool = False, ) -> tuple[EventBase, int]: room_state = await self._storage_controllers.state.get_current_state( room_id, @@ -1823,6 +1838,7 @@ async def _make_and_store_3pid_invite( txn_id=txn_id, prev_event_ids=prev_event_ids, depth=depth, + skip_check_event_allowed=skip_check_event_allowed, ) return event, stream_id diff --git a/synapse/rest/client/room.py b/synapse/rest/client/room.py index 38e315d0e7c..1abb028290a 100644 --- a/synapse/rest/client/room.py +++ b/synapse/rest/client/room.py @@ -159,6 +159,8 @@ def __init__(self, hs: "HomeServer"): super().__init__(hs) self._room_creation_handler = hs.get_room_creation_handler() self.auth = hs.get_auth() + # TODO: Get this from config + self._skip_check_event_allowed_for_room_creation = True def register(self, http_server: HttpServer) -> None: PATTERNS = "/createRoom" @@ -181,7 +183,9 @@ async def _do( self, request: SynapseRequest, requester: Requester ) -> tuple[int, JsonDict]: room_id, _, _ = await self._room_creation_handler.create_room( - requester, self.get_room_config(request) + requester, + self.get_room_config(request), + skip_check_event_allowed=self._skip_check_event_allowed_for_room_creation, ) return 200, {"room_id": room_id}