diff --git a/synapse/module_api/callbacks/__init__.py b/synapse/module_api/callbacks/__init__.py
index c20d9543fb..16ef7a4b47 100644
--- a/synapse/module_api/callbacks/__init__.py
+++ b/synapse/module_api/callbacks/__init__.py
@@ -27,6 +27,12 @@ if TYPE_CHECKING:
from synapse.module_api.callbacks.account_validity_callbacks import (
AccountValidityModuleApiCallbacks,
)
+from synapse.module_api.callbacks.media_repository_callbacks import (
+ MediaRepositoryModuleApiCallbacks,
+)
+from synapse.module_api.callbacks.ratelimit_callbacks import (
+ RatelimitModuleApiCallbacks,
+)
from synapse.module_api.callbacks.spamchecker_callbacks import (
SpamCheckerModuleApiCallbacks,
)
@@ -38,5 +44,7 @@ from synapse.module_api.callbacks.third_party_event_rules_callbacks import (
class ModuleApiCallbacks:
def __init__(self, hs: "HomeServer") -> None:
self.account_validity = AccountValidityModuleApiCallbacks()
+ self.media_repository = MediaRepositoryModuleApiCallbacks(hs)
+ self.ratelimit = RatelimitModuleApiCallbacks(hs)
self.spam_checker = SpamCheckerModuleApiCallbacks(hs)
self.third_party_event_rules = ThirdPartyEventRulesModuleApiCallbacks(hs)
diff --git a/synapse/module_api/callbacks/media_repository_callbacks.py b/synapse/module_api/callbacks/media_repository_callbacks.py
new file mode 100644
index 0000000000..6fa80a8eab
--- /dev/null
+++ b/synapse/module_api/callbacks/media_repository_callbacks.py
@@ -0,0 +1,76 @@
+#
+# This file is licensed under the Affero General Public License (AGPL) version 3.
+#
+# Copyright (C) 2025 New Vector, Ltd
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# See the GNU Affero General Public License for more details:
+# <https://www.gnu.org/licenses/agpl-3.0.html>.
+#
+
+import logging
+from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional
+
+from synapse.types import JsonDict
+from synapse.util.async_helpers import delay_cancellation
+from synapse.util.metrics import Measure
+
+if TYPE_CHECKING:
+ from synapse.server import HomeServer
+
+logger = logging.getLogger(__name__)
+
+GET_MEDIA_CONFIG_FOR_USER_CALLBACK = Callable[[str], Awaitable[Optional[JsonDict]]]
+
+IS_USER_ALLOWED_TO_UPLOAD_MEDIA_OF_SIZE_CALLBACK = Callable[[str, int], Awaitable[bool]]
+
+
+class MediaRepositoryModuleApiCallbacks:
+ def __init__(self, hs: "HomeServer") -> None:
+ self.clock = hs.get_clock()
+ self._get_media_config_for_user_callbacks: List[
+ GET_MEDIA_CONFIG_FOR_USER_CALLBACK
+ ] = []
+ self._is_user_allowed_to_upload_media_of_size_callbacks: List[
+ IS_USER_ALLOWED_TO_UPLOAD_MEDIA_OF_SIZE_CALLBACK
+ ] = []
+
+ def register_callbacks(
+ self,
+ get_media_config_for_user: Optional[GET_MEDIA_CONFIG_FOR_USER_CALLBACK] = None,
+ is_user_allowed_to_upload_media_of_size: Optional[
+ IS_USER_ALLOWED_TO_UPLOAD_MEDIA_OF_SIZE_CALLBACK
+ ] = None,
+ ) -> None:
+ """Register callbacks from module for each hook."""
+ if get_media_config_for_user is not None:
+ self._get_media_config_for_user_callbacks.append(get_media_config_for_user)
+
+ if is_user_allowed_to_upload_media_of_size is not None:
+ self._is_user_allowed_to_upload_media_of_size_callbacks.append(
+ is_user_allowed_to_upload_media_of_size
+ )
+
+ async def get_media_config_for_user(self, user_id: str) -> Optional[JsonDict]:
+ for callback in self._get_media_config_for_user_callbacks:
+ with Measure(self.clock, f"{callback.__module__}.{callback.__qualname__}"):
+ res: Optional[JsonDict] = await delay_cancellation(callback(user_id))
+ if res:
+ return res
+
+ return None
+
+ async def is_user_allowed_to_upload_media_of_size(
+ self, user_id: str, size: int
+ ) -> bool:
+ for callback in self._is_user_allowed_to_upload_media_of_size_callbacks:
+ with Measure(self.clock, f"{callback.__module__}.{callback.__qualname__}"):
+ res: bool = await delay_cancellation(callback(user_id, size))
+ if not res:
+ return res
+
+ return True
diff --git a/synapse/module_api/callbacks/ratelimit_callbacks.py b/synapse/module_api/callbacks/ratelimit_callbacks.py
new file mode 100644
index 0000000000..64f9cc81e8
--- /dev/null
+++ b/synapse/module_api/callbacks/ratelimit_callbacks.py
@@ -0,0 +1,74 @@
+#
+# This file is licensed under the Affero General Public License (AGPL) version 3.
+#
+# Copyright (C) 2025 New Vector, Ltd
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# See the GNU Affero General Public License for more details:
+# <https://www.gnu.org/licenses/agpl-3.0.html>.
+#
+
+import logging
+from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional
+
+import attr
+
+from synapse.util.async_helpers import delay_cancellation
+from synapse.util.metrics import Measure
+
+if TYPE_CHECKING:
+ from synapse.server import HomeServer
+
+logger = logging.getLogger(__name__)
+
+
+@attr.s(auto_attribs=True)
+class RatelimitOverride:
+ """Represents a ratelimit being overridden."""
+
+ per_second: float
+ """The number of actions that can be performed in a second. `0.0` means that ratelimiting is disabled."""
+ burst_count: int
+ """How many actions that can be performed before being limited."""
+
+
+GET_RATELIMIT_OVERRIDE_FOR_USER_CALLBACK = Callable[
+ [str, str], Awaitable[Optional[RatelimitOverride]]
+]
+
+
+class RatelimitModuleApiCallbacks:
+ def __init__(self, hs: "HomeServer") -> None:
+ self.clock = hs.get_clock()
+ self._get_ratelimit_override_for_user_callbacks: List[
+ GET_RATELIMIT_OVERRIDE_FOR_USER_CALLBACK
+ ] = []
+
+ def register_callbacks(
+ self,
+ get_ratelimit_override_for_user: Optional[
+ GET_RATELIMIT_OVERRIDE_FOR_USER_CALLBACK
+ ] = None,
+ ) -> None:
+ """Register callbacks from module for each hook."""
+ if get_ratelimit_override_for_user is not None:
+ self._get_ratelimit_override_for_user_callbacks.append(
+ get_ratelimit_override_for_user
+ )
+
+ async def get_ratelimit_override_for_user(
+ self, user_id: str, limiter_name: str
+ ) -> Optional[RatelimitOverride]:
+ for callback in self._get_ratelimit_override_for_user_callbacks:
+ with Measure(self.clock, f"{callback.__module__}.{callback.__qualname__}"):
+ res: Optional[RatelimitOverride] = await delay_cancellation(
+ callback(user_id, limiter_name)
+ )
+ if res:
+ return res
+
+ return None
diff --git a/synapse/module_api/callbacks/spamchecker_callbacks.py b/synapse/module_api/callbacks/spamchecker_callbacks.py
index 17079ff781..30cab9eb7e 100644
--- a/synapse/module_api/callbacks/spamchecker_callbacks.py
+++ b/synapse/module_api/callbacks/spamchecker_callbacks.py
@@ -19,8 +19,10 @@
#
#
+import functools
import inspect
import logging
+from copy import deepcopy
from typing import (
TYPE_CHECKING,
Any,
@@ -28,14 +30,13 @@ from typing import (
Callable,
Collection,
List,
+ Literal,
Optional,
Tuple,
Union,
+ cast,
)
-# `Literal` appears with Python 3.8.
-from typing_extensions import Literal
-
import synapse
from synapse.api.errors import Codes
from synapse.logging.opentracing import trace
@@ -104,24 +105,28 @@ USER_MAY_INVITE_CALLBACK = Callable[
]
],
]
-USER_MAY_SEND_3PID_INVITE_CALLBACK = Callable[
- [str, str, str, 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_CALLBACK = Callable[
- [str],
+USER_MAY_CREATE_ROOM_ALIAS_CALLBACK = Callable[
+ [str, RoomAlias],
Awaitable[
Union[
Literal["NOT_SPAM"],
@@ -136,8 +141,8 @@ USER_MAY_CREATE_ROOM_CALLBACK = Callable[
]
],
]
-USER_MAY_CREATE_ROOM_ALIAS_CALLBACK = Callable[
- [str, RoomAlias],
+USER_MAY_PUBLISH_ROOM_CALLBACK = Callable[
+ [str, str],
Awaitable[
Union[
Literal["NOT_SPAM"],
@@ -152,8 +157,8 @@ USER_MAY_CREATE_ROOM_ALIAS_CALLBACK = Callable[
]
],
]
-USER_MAY_PUBLISH_ROOM_CALLBACK = Callable[
- [str, str],
+USER_MAY_SEND_STATE_EVENT_CALLBACK = Callable[
+ [str, str, str, str, JsonDict],
Awaitable[
Union[
Literal["NOT_SPAM"],
@@ -163,12 +168,13 @@ USER_MAY_PUBLISH_ROOM_CALLBACK = Callable[
# experiments.
# Use this to return additional information as part of an error.
Tuple[Codes, JsonDict],
- # Deprecated
- bool,
]
],
]
-CHECK_USERNAME_FOR_SPAM_CALLBACK = Callable[[UserProfile], Awaitable[bool]]
+CHECK_USERNAME_FOR_SPAM_CALLBACK = Union[
+ Callable[[UserProfile], Awaitable[bool]],
+ Callable[[UserProfile, str], Awaitable[bool]],
+]
LEGACY_CHECK_REGISTRATION_FOR_SPAM_CALLBACK = Callable[
[
Optional[dict],
@@ -293,6 +299,7 @@ def load_legacy_spam_checkers(hs: "synapse.server.HomeServer") -> None:
"Bad signature for callback check_registration_for_spam",
)
+ @functools.wraps(wrapped_func)
def run(*args: Any, **kwargs: Any) -> Awaitable:
# Assertion required because mypy can't prove we won't change `f`
# back to `None`. See
@@ -324,10 +331,10 @@ class SpamCheckerModuleApiCallbacks:
] = []
self._user_may_join_room_callbacks: List[USER_MAY_JOIN_ROOM_CALLBACK] = []
self._user_may_invite_callbacks: List[USER_MAY_INVITE_CALLBACK] = []
- self._user_may_send_3pid_invite_callbacks: List[
- 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
] = []
@@ -351,7 +358,6 @@ class SpamCheckerModuleApiCallbacks:
] = None,
user_may_join_room: Optional[USER_MAY_JOIN_ROOM_CALLBACK] = None,
user_may_invite: Optional[USER_MAY_INVITE_CALLBACK] = None,
- user_may_send_3pid_invite: Optional[USER_MAY_SEND_3PID_INVITE_CALLBACK] = None,
user_may_create_room: Optional[USER_MAY_CREATE_ROOM_CALLBACK] = None,
user_may_create_room_alias: Optional[
USER_MAY_CREATE_ROOM_ALIAS_CALLBACK
@@ -363,6 +369,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:
@@ -379,14 +386,14 @@ class SpamCheckerModuleApiCallbacks:
if user_may_invite is not None:
self._user_may_invite_callbacks.append(user_may_invite)
- if user_may_send_3pid_invite is not None:
- self._user_may_send_3pid_invite_callbacks.append(
- user_may_send_3pid_invite,
- )
-
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,
@@ -573,29 +580,42 @@ class SpamCheckerModuleApiCallbacks:
# No spam-checker has rejected the request, let it pass.
return self.NOT_SPAM
- async def user_may_send_3pid_invite(
- self, inviter_userid: str, medium: str, address: str, room_id: str
+ async def user_may_create_room(
+ self, userid: str, room_config: JsonDict
) -> Union[Tuple[Codes, dict], Literal["NOT_SPAM"]]:
- """Checks if a given user may invite a given threepid into the room
-
- Note that if the threepid is already associated with a Matrix user ID, Synapse
- will call user_may_invite with said user ID instead.
+ """Checks if a given user may create a room
Args:
- inviter_userid: The user ID of the sender of the invitation
- medium: The 3PID's medium (e.g. "email")
- address: The 3PID's address (e.g. "alice@example.com")
- room_id: The room ID
-
- Returns:
- NOT_SPAM if the operation is permitted, Codes otherwise.
+ 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_send_3pid_invite_callbacks:
+ for callback in self._user_may_create_room_callbacks:
with Measure(self.clock, f"{callback.__module__}.{callback.__qualname__}"):
- res = await delay_cancellation(
- callback(inviter_userid, medium, address, room_id)
- )
- # Normalize return values to `Codes` or `"NOT_SPAM"`.
+ 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:
@@ -611,36 +631,38 @@ class SpamCheckerModuleApiCallbacks:
return res
else:
logger.warning(
- "Module returned invalid value, rejecting 3pid invite as spam"
+ "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(
- self, userid: str
+ 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
-
+ """Checks if a given user may create a room with a given visibility
Args:
- userid: The ID of the user attempting to create a room
+ 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_create_room_callbacks:
+ for callback in self._user_may_send_state_event_callbacks:
with Measure(self.clock, f"{callback.__module__}.{callback.__qualname__}"):
- res = await delay_cancellation(callback(userid))
- if res is True or res is self.NOT_SPAM:
+ # 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 res is False:
- return synapse.api.errors.Codes.FORBIDDEN, {}
elif isinstance(res, synapse.api.errors.Codes):
return res, {}
- elif (
- isinstance(res, tuple)
- and len(res) == 2
- and isinstance(res[0], synapse.api.errors.Codes)
- and isinstance(res[1], dict)
- ):
- return res
else:
logger.warning(
"Module returned invalid value, rejecting room creation as spam"
@@ -716,7 +738,9 @@ class SpamCheckerModuleApiCallbacks:
return self.NOT_SPAM
- async def check_username_for_spam(self, user_profile: UserProfile) -> bool:
+ async def check_username_for_spam(
+ self, user_profile: UserProfile, requester_id: str
+ ) -> bool:
"""Checks if a user ID or display name are considered "spammy" by this server.
If the server considers a username spammy, then it will not be included in
@@ -727,15 +751,33 @@ class SpamCheckerModuleApiCallbacks:
* user_id
* display_name
* avatar_url
+ requester_id: The user ID of the user making the user directory search request.
Returns:
True if the user is spammy.
"""
for callback in self._check_username_for_spam_callbacks:
with Measure(self.clock, f"{callback.__module__}.{callback.__qualname__}"):
+ checker_args = inspect.signature(callback)
# Make a copy of the user profile object to ensure the spam checker cannot
# modify it.
- res = await delay_cancellation(callback(user_profile.copy()))
+ # Also ensure backwards compatibility with spam checker callbacks
+ # that don't expect the requester_id argument.
+ if len(checker_args.parameters) == 2:
+ callback_with_requester_id = cast(
+ Callable[[UserProfile, str], Awaitable[bool]], callback
+ )
+ res = await delay_cancellation(
+ callback_with_requester_id(user_profile.copy(), requester_id)
+ )
+ else:
+ callback_without_requester_id = cast(
+ Callable[[UserProfile], Awaitable[bool]], callback
+ )
+ res = await delay_cancellation(
+ callback_without_requester_id(user_profile.copy())
+ )
+
if res:
return True
@@ -755,8 +797,8 @@ class SpamCheckerModuleApiCallbacks:
username: The request user name, if any
request_info: List of tuples of user agent and IP that
were used during the registration process.
- auth_provider_id: The SSO IdP the user used, e.g "oidc", "saml",
- "cas". If any. Note this does not include users registered
+ auth_provider_id: The SSO IdP the user used, e.g "oidc".
+ If any. Note this does not include users registered
via a password provider.
Returns:
@@ -844,8 +886,8 @@ class SpamCheckerModuleApiCallbacks:
user_id: The request user ID
request_info: List of tuples of user agent and IP that
were used during the registration process.
- auth_provider_id: The SSO IdP the user used, e.g "oidc", "saml",
- "cas". If any. Note this does not include users registered
+ auth_provider_id: The SSO IdP the user used, e.g "oidc".
+ If any. Note this does not include users registered
via a password provider.
Returns:
diff --git a/synapse/module_api/callbacks/third_party_event_rules_callbacks.py b/synapse/module_api/callbacks/third_party_event_rules_callbacks.py
index 9f7a04372d..13508cc582 100644
--- a/synapse/module_api/callbacks/third_party_event_rules_callbacks.py
+++ b/synapse/module_api/callbacks/third_party_event_rules_callbacks.py
@@ -40,9 +40,6 @@ CHECK_EVENT_ALLOWED_CALLBACK = Callable[
[EventBase, StateMap[EventBase]], Awaitable[Tuple[bool, Optional[dict]]]
]
ON_CREATE_ROOM_CALLBACK = Callable[[Requester, dict, bool], Awaitable]
-CHECK_THREEPID_CAN_BE_INVITED_CALLBACK = Callable[
- [str, str, StateMap[EventBase]], Awaitable[bool]
-]
CHECK_VISIBILITY_CAN_BE_MODIFIED_CALLBACK = Callable[
[str, StateMap[EventBase], str], Awaitable[bool]
]
@@ -51,9 +48,6 @@ CHECK_CAN_SHUTDOWN_ROOM_CALLBACK = Callable[[Optional[str], str], Awaitable[bool
CHECK_CAN_DEACTIVATE_USER_CALLBACK = Callable[[str, bool], Awaitable[bool]]
ON_PROFILE_UPDATE_CALLBACK = Callable[[str, ProfileInfo, bool, bool], Awaitable]
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK = Callable[[str, bool, bool], Awaitable]
-ON_THREEPID_BIND_CALLBACK = Callable[[str, str, str], Awaitable]
-ON_ADD_USER_THIRD_PARTY_IDENTIFIER_CALLBACK = Callable[[str, str, str], Awaitable]
-ON_REMOVE_USER_THIRD_PARTY_IDENTIFIER_CALLBACK = Callable[[str, str, str], Awaitable]
def load_legacy_third_party_event_rules(hs: "HomeServer") -> None:
@@ -73,7 +67,6 @@ def load_legacy_third_party_event_rules(hs: "HomeServer") -> None:
third_party_event_rules_methods = {
"check_event_allowed",
"on_create_room",
- "check_threepid_can_be_invited",
"check_visibility_can_be_modified",
}
@@ -161,9 +154,6 @@ class ThirdPartyEventRulesModuleApiCallbacks:
self._check_event_allowed_callbacks: List[CHECK_EVENT_ALLOWED_CALLBACK] = []
self._on_create_room_callbacks: List[ON_CREATE_ROOM_CALLBACK] = []
- self._check_threepid_can_be_invited_callbacks: List[
- CHECK_THREEPID_CAN_BE_INVITED_CALLBACK
- ] = []
self._check_visibility_can_be_modified_callbacks: List[
CHECK_VISIBILITY_CAN_BE_MODIFIED_CALLBACK
] = []
@@ -178,21 +168,11 @@ class ThirdPartyEventRulesModuleApiCallbacks:
self._on_user_deactivation_status_changed_callbacks: List[
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK
] = []
- self._on_threepid_bind_callbacks: List[ON_THREEPID_BIND_CALLBACK] = []
- self._on_add_user_third_party_identifier_callbacks: List[
- ON_ADD_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
- ] = []
- self._on_remove_user_third_party_identifier_callbacks: List[
- ON_REMOVE_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
- ] = []
def register_third_party_rules_callbacks(
self,
check_event_allowed: Optional[CHECK_EVENT_ALLOWED_CALLBACK] = None,
on_create_room: Optional[ON_CREATE_ROOM_CALLBACK] = None,
- check_threepid_can_be_invited: Optional[
- CHECK_THREEPID_CAN_BE_INVITED_CALLBACK
- ] = None,
check_visibility_can_be_modified: Optional[
CHECK_VISIBILITY_CAN_BE_MODIFIED_CALLBACK
] = None,
@@ -202,14 +182,7 @@ class ThirdPartyEventRulesModuleApiCallbacks:
on_profile_update: Optional[ON_PROFILE_UPDATE_CALLBACK] = None,
on_user_deactivation_status_changed: Optional[
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK
- ] = None,
- on_threepid_bind: Optional[ON_THREEPID_BIND_CALLBACK] = None,
- on_add_user_third_party_identifier: Optional[
- ON_ADD_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
- ] = None,
- on_remove_user_third_party_identifier: Optional[
- ON_REMOVE_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
- ] = None,
+ ] = None
) -> None:
"""Register callbacks from modules for each hook."""
if check_event_allowed is not None:
@@ -218,11 +191,6 @@ class ThirdPartyEventRulesModuleApiCallbacks:
if on_create_room is not None:
self._on_create_room_callbacks.append(on_create_room)
- if check_threepid_can_be_invited is not None:
- self._check_threepid_can_be_invited_callbacks.append(
- check_threepid_can_be_invited,
- )
-
if check_visibility_can_be_modified is not None:
self._check_visibility_can_be_modified_callbacks.append(
check_visibility_can_be_modified,
@@ -236,6 +204,7 @@ class ThirdPartyEventRulesModuleApiCallbacks:
if check_can_deactivate_user is not None:
self._check_can_deactivate_user_callbacks.append(check_can_deactivate_user)
+
if on_profile_update is not None:
self._on_profile_update_callbacks.append(on_profile_update)
@@ -244,19 +213,6 @@ class ThirdPartyEventRulesModuleApiCallbacks:
on_user_deactivation_status_changed,
)
- if on_threepid_bind is not None:
- self._on_threepid_bind_callbacks.append(on_threepid_bind)
-
- if on_add_user_third_party_identifier is not None:
- self._on_add_user_third_party_identifier_callbacks.append(
- on_add_user_third_party_identifier
- )
-
- if on_remove_user_third_party_identifier is not None:
- self._on_remove_user_third_party_identifier_callbacks.append(
- on_remove_user_third_party_identifier
- )
-
async def check_event_allowed(
self,
event: EventBase,
@@ -349,39 +305,6 @@ class ThirdPartyEventRulesModuleApiCallbacks:
raise e
- async def check_threepid_can_be_invited(
- self, medium: str, address: str, room_id: str
- ) -> bool:
- """Check if a provided 3PID can be invited in the given room.
-
- Args:
- medium: The 3PID's medium.
- address: The 3PID's address.
- room_id: The room we want to invite the threepid to.
-
- Returns:
- True if the 3PID can be invited, False if not.
- """
- # Bail out early without hitting the store if we don't have any callbacks to run.
- if len(self._check_threepid_can_be_invited_callbacks) == 0:
- return True
-
- state_events = await self._storage_controllers.state.get_current_state(room_id)
-
- for callback in self._check_threepid_can_be_invited_callbacks:
- try:
- threepid_can_be_invited = await delay_cancellation(
- callback(medium, address, state_events)
- )
- if threepid_can_be_invited is False:
- return False
- except CancelledError:
- raise
- except Exception as e:
- logger.warning("Failed to run module API callback %s: %s", callback, e)
-
- return True
-
async def check_visibility_can_be_modified(
self, room_id: str, new_visibility: str
) -> bool:
@@ -533,67 +456,3 @@ class ThirdPartyEventRulesModuleApiCallbacks:
logger.exception(
"Failed to run module API callback %s: %s", callback, e
)
-
- async def on_threepid_bind(self, user_id: str, medium: str, address: str) -> None:
- """Called after a threepid association has been verified and stored.
-
- Note that this callback is called when an association is created on the
- local homeserver, not when it's created on an identity server (and then kept track
- of so that it can be unbound on the same IS later on).
-
- THIS MODULE CALLBACK METHOD HAS BEEN DEPRECATED. Please use the
- `on_add_user_third_party_identifier` callback method instead.
-
- Args:
- user_id: the user being associated with the threepid.
- medium: the threepid's medium.
- address: the threepid's address.
- """
- for callback in self._on_threepid_bind_callbacks:
- try:
- await callback(user_id, medium, address)
- except Exception as e:
- logger.exception(
- "Failed to run module API callback %s: %s", callback, e
- )
-
- async def on_add_user_third_party_identifier(
- self, user_id: str, medium: str, address: str
- ) -> None:
- """Called when an association between a user's Matrix ID and a third-party ID
- (email, phone number) has successfully been registered on the homeserver.
-
- Args:
- user_id: The User ID included in the association.
- medium: The medium of the third-party ID (email, msisdn).
- address: The address of the third-party ID (i.e. an email address).
- """
- for callback in self._on_add_user_third_party_identifier_callbacks:
- try:
- await callback(user_id, medium, address)
- except Exception as e:
- logger.exception(
- "Failed to run module API callback %s: %s", callback, e
- )
-
- async def on_remove_user_third_party_identifier(
- self, user_id: str, medium: str, address: str
- ) -> None:
- """Called when an association between a user's Matrix ID and a third-party ID
- (email, phone number) has been successfully removed on the homeserver.
-
- This is called *after* any known bindings on identity servers for this
- association have been removed.
-
- Args:
- user_id: The User ID included in the removed association.
- medium: The medium of the third-party ID (email, msisdn).
- address: The address of the third-party ID (i.e. an email address).
- """
- for callback in self._on_remove_user_third_party_identifier_callbacks:
- try:
- await callback(user_id, medium, address)
- except Exception as e:
- logger.exception(
- "Failed to run module API callback %s: %s", callback, e
- )
|