summary refs log tree commit diff
path: root/synapse/module_api/callbacks
diff options
context:
space:
mode:
authorHugh Nimmo-Smith <hughns@users.noreply.github.com>2025-06-04 12:26:04 +0100
committerGitHub <noreply@github.com>2025-06-04 11:26:04 +0000
commit28f21b403683619047668d5974219fdff8a33dfd (patch)
treed9fba49b3e984dd2116cb1ba12a5088a2fda668b /synapse/module_api/callbacks
parentAdd media repository callbacks to module API to control media upload size (#1... (diff)
downloadsynapse-28f21b403683619047668d5974219fdff8a33dfd.tar.xz
Add user_may_send_state_event callback to spam checker module API (#18455)
Diffstat (limited to 'synapse/module_api/callbacks')
-rw-r--r--synapse/module_api/callbacks/spamchecker_callbacks.py57
1 files changed, 57 insertions, 0 deletions
diff --git a/synapse/module_api/callbacks/spamchecker_callbacks.py b/synapse/module_api/callbacks/spamchecker_callbacks.py

index 9b373ff67c..bea5c5badf 100644 --- a/synapse/module_api/callbacks/spamchecker_callbacks.py +++ b/synapse/module_api/callbacks/spamchecker_callbacks.py
@@ -173,6 +173,20 @@ USER_MAY_PUBLISH_ROOM_CALLBACK = Callable[ ] ], ] +USER_MAY_SEND_STATE_EVENT_CALLBACK = Callable[ + [str, str, str, str, JsonDict], + Awaitable[ + Union[ + Literal["NOT_SPAM"], + Codes, + # Highly experimental, not officially part of the spamchecker API, may + # disappear without warning depending on the results of ongoing + # experiments. + # Use this to return additional information as part of an error. + Tuple[Codes, JsonDict], + ] + ], +] CHECK_USERNAME_FOR_SPAM_CALLBACK = Union[ Callable[[UserProfile], Awaitable[bool]], Callable[[UserProfile, str], Awaitable[bool]], @@ -337,6 +351,9 @@ class SpamCheckerModuleApiCallbacks: USER_MAY_SEND_3PID_INVITE_CALLBACK ] = [] self._user_may_create_room_callbacks: List[USER_MAY_CREATE_ROOM_CALLBACK] = [] + self._user_may_send_state_event_callbacks: List[ + USER_MAY_SEND_STATE_EVENT_CALLBACK + ] = [] self._user_may_create_room_alias_callbacks: List[ USER_MAY_CREATE_ROOM_ALIAS_CALLBACK ] = [] @@ -372,6 +389,7 @@ class SpamCheckerModuleApiCallbacks: ] = None, check_media_file_for_spam: Optional[CHECK_MEDIA_FILE_FOR_SPAM_CALLBACK] = None, check_login_for_spam: Optional[CHECK_LOGIN_FOR_SPAM_CALLBACK] = None, + user_may_send_state_event: Optional[USER_MAY_SEND_STATE_EVENT_CALLBACK] = None, ) -> None: """Register callbacks from module for each hook.""" if check_event_for_spam is not None: @@ -396,6 +414,11 @@ class SpamCheckerModuleApiCallbacks: if user_may_create_room is not None: self._user_may_create_room_callbacks.append(user_may_create_room) + if user_may_send_state_event is not None: + self._user_may_send_state_event_callbacks.append( + user_may_send_state_event, + ) + if user_may_create_room_alias is not None: self._user_may_create_room_alias_callbacks.append( user_may_create_room_alias, @@ -683,6 +706,40 @@ class SpamCheckerModuleApiCallbacks: return self.NOT_SPAM + async def user_may_send_state_event( + self, + user_id: str, + room_id: str, + event_type: str, + state_key: str, + content: JsonDict, + ) -> Union[Tuple[Codes, dict], Literal["NOT_SPAM"]]: + """Checks if a given user may create a room with a given visibility + Args: + user_id: The ID of the user attempting to create a room + room_id: The ID of the room that the event will be sent to + event_type: The type of the state event + state_key: The state key of the state event + content: The content of the state event + """ + for callback in self._user_may_send_state_event_callbacks: + with Measure(self.clock, f"{callback.__module__}.{callback.__qualname__}"): + # We make a copy of the content to ensure that the spam checker cannot modify it. + res = await delay_cancellation( + callback(user_id, room_id, event_type, state_key, deepcopy(content)) + ) + if res is self.NOT_SPAM: + continue + elif isinstance(res, synapse.api.errors.Codes): + return res, {} + else: + logger.warning( + "Module returned invalid value, rejecting room creation as spam" + ) + return synapse.api.errors.Codes.FORBIDDEN, {} + + return self.NOT_SPAM + async def user_may_create_room_alias( self, userid: str, room_alias: RoomAlias ) -> Union[Tuple[Codes, dict], Literal["NOT_SPAM"]]: