summary refs log tree commit diff
path: root/synapse/module_api
diff options
context:
space:
mode:
authorHugh Nimmo-Smith <hughns@users.noreply.github.com>2025-06-04 11:30:45 +0100
committerGitHub <noreply@github.com>2025-06-04 11:30:45 +0100
commitfbe7a898f0380aa194c26b71eccf029d0ac47b5c (patch)
tree479a0222b3192614d2a863d102c533e5c4ef5c23 /synapse/module_api
parentMerge branch 'master' into develop (diff)
downloadsynapse-fbe7a898f0380aa194c26b71eccf029d0ac47b5c.tar.xz
Pass room_config argument to user_may_create_room spam checker module callback (#18486)
This PR adds an additional `room_config` argument to the
`user_may_create_room` spam checker module API callback.

It will continue to work with implementations of `user_may_create_room`
that do not expect the additional parameter.

A side affect is that on a room upgrade the spam checker callback is
called *after* doing some work to calculate the state rather than
before. However, I hope that this is acceptable given the relative
infrequency of room upgrades.
Diffstat (limited to 'synapse/module_api')
-rw-r--r--synapse/module_api/callbacks/spamchecker_callbacks.py62
1 files changed, 46 insertions, 16 deletions
diff --git a/synapse/module_api/callbacks/spamchecker_callbacks.py b/synapse/module_api/callbacks/spamchecker_callbacks.py

index a86b46ba54..9b373ff67c 100644 --- a/synapse/module_api/callbacks/spamchecker_callbacks.py +++ b/synapse/module_api/callbacks/spamchecker_callbacks.py
@@ -22,6 +22,7 @@ import functools import inspect import logging +from copy import deepcopy from typing import ( TYPE_CHECKING, Any, @@ -120,20 +121,24 @@ USER_MAY_SEND_3PID_INVITE_CALLBACK = Callable[ ] ], ] -USER_MAY_CREATE_ROOM_CALLBACK = Callable[ - [str], - 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], - # Deprecated - bool, - ] +USER_MAY_CREATE_ROOM_CALLBACK_RETURN_VALUE = 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], + # Deprecated + bool, +] +USER_MAY_CREATE_ROOM_CALLBACK = Union[ + Callable[ + [str, JsonDict], + Awaitable[USER_MAY_CREATE_ROOM_CALLBACK_RETURN_VALUE], + ], + Callable[ # Single argument variant for backwards compatibility + [str], Awaitable[USER_MAY_CREATE_ROOM_CALLBACK_RETURN_VALUE] ], ] USER_MAY_CREATE_ROOM_ALIAS_CALLBACK = Callable[ @@ -622,16 +627,41 @@ class SpamCheckerModuleApiCallbacks: return self.NOT_SPAM async def user_may_create_room( - self, userid: str + self, userid: str, room_config: JsonDict ) -> Union[Tuple[Codes, dict], Literal["NOT_SPAM"]]: """Checks if a given user may create a room Args: userid: The ID of the user attempting to create a room + room_config: The room creation configuration which is the body of the /createRoom request """ for callback in self._user_may_create_room_callbacks: with Measure(self.clock, f"{callback.__module__}.{callback.__qualname__}"): - res = await delay_cancellation(callback(userid)) + checker_args = inspect.signature(callback) + # Also ensure backwards compatibility with spam checker callbacks + # that don't expect the room_config argument. + if len(checker_args.parameters) == 2: + callback_with_requester_id = cast( + Callable[ + [str, JsonDict], + Awaitable[USER_MAY_CREATE_ROOM_CALLBACK_RETURN_VALUE], + ], + callback, + ) + # We make a copy of the config to ensure the spam checker cannot modify it. + res = await delay_cancellation( + callback_with_requester_id(userid, deepcopy(room_config)) + ) + else: + callback_without_requester_id = cast( + Callable[ + [str], Awaitable[USER_MAY_CREATE_ROOM_CALLBACK_RETURN_VALUE] + ], + callback, + ) + res = await delay_cancellation( + callback_without_requester_id(userid) + ) if res is True or res is self.NOT_SPAM: continue elif res is False: