From ca25be76d1e40ca03f81a561b4c25fd2a43ce23a Mon Sep 17 00:00:00 2001 From: reivilibre Date: Wed, 24 Apr 2024 13:43:33 +0000 Subject: deploy: 4cd6b75d0a95aa373068fae8b3a431fd453c9728 --- v1.106/modules/account_data_callbacks.html | 295 +++++++++++ v1.106/modules/account_validity_callbacks.html | 240 +++++++++ ...add_extra_fields_to_client_events_unsigned.html | 225 ++++++++ .../background_update_controller_callbacks.html | 251 +++++++++ v1.106/modules/index.html | 246 +++++++++ .../modules/password_auth_provider_callbacks.html | 443 ++++++++++++++++ v1.106/modules/porting_legacy_module.html | 221 ++++++++ v1.106/modules/presence_router_callbacks.html | 292 +++++++++++ v1.106/modules/spam_checker_callbacks.html | 583 +++++++++++++++++++++ v1.106/modules/third_party_rules_callbacks.html | 455 ++++++++++++++++ v1.106/modules/writing_a_module.html | 313 +++++++++++ 11 files changed, 3564 insertions(+) create mode 100644 v1.106/modules/account_data_callbacks.html create mode 100644 v1.106/modules/account_validity_callbacks.html create mode 100644 v1.106/modules/add_extra_fields_to_client_events_unsigned.html create mode 100644 v1.106/modules/background_update_controller_callbacks.html create mode 100644 v1.106/modules/index.html create mode 100644 v1.106/modules/password_auth_provider_callbacks.html create mode 100644 v1.106/modules/porting_legacy_module.html create mode 100644 v1.106/modules/presence_router_callbacks.html create mode 100644 v1.106/modules/spam_checker_callbacks.html create mode 100644 v1.106/modules/third_party_rules_callbacks.html create mode 100644 v1.106/modules/writing_a_module.html (limited to 'v1.106/modules') diff --git a/v1.106/modules/account_data_callbacks.html b/v1.106/modules/account_data_callbacks.html new file mode 100644 index 0000000000..39c41a583f --- /dev/null +++ b/v1.106/modules/account_data_callbacks.html @@ -0,0 +1,295 @@ + + + + + + Account data callbacks - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Account data callbacks

+

Account data callbacks allow module developers to react to changes of the account data +of local users. Account data callbacks can be registered using the module API's +register_account_data_callbacks method.

+

Callbacks

+

The available account data callbacks are:

+

on_account_data_updated

+

First introduced in Synapse v1.57.0

+
async def on_account_data_updated(
+    user_id: str,
+    room_id: Optional[str],
+    account_data_type: str,
+    content: "synapse.module_api.JsonDict",
+) -> None:
+
+

Called after user's account data has been updated. The module is given the +Matrix ID of the user whose account data is changing, the room ID the data is associated +with, the type associated with the change, as well as the new content. If the account +data is not associated with a specific room, then the room ID is None.

+

This callback is triggered when new account data is added or when the data associated with +a given type (and optionally room) changes. This includes deletion, since in Matrix, +deleting account data consists of replacing the data associated with a given type +(and optionally room) with an empty dictionary ({}).

+

Note that this doesn't trigger when changing the tags associated with a room, as these are +processed separately by Synapse.

+

If multiple modules implement this callback, Synapse runs them all in order.

+

Example

+

The example below is a module that implements the on_account_data_updated callback, and +sends an event to an audit room when a user changes their account data.

+
import json
+import attr
+from typing import Any, Dict, Optional
+
+from synapse.module_api import JsonDict, ModuleApi
+from synapse.module_api.errors import ConfigError
+
+
+@attr.s(auto_attribs=True)
+class CustomAccountDataConfig:
+    audit_room: str
+    sender: str
+
+
+class CustomAccountDataModule:
+    def __init__(self, config: CustomAccountDataConfig, api: ModuleApi):
+        self.api = api
+        self.config = config
+
+        self.api.register_account_data_callbacks(
+            on_account_data_updated=self.log_new_account_data,
+        )
+
+    @staticmethod
+    def parse_config(config: Dict[str, Any]) -> CustomAccountDataConfig:
+        def check_in_config(param: str):
+            if param not in config:
+                raise ConfigError(f"'{param}' is required")
+
+        check_in_config("audit_room")
+        check_in_config("sender")
+
+        return CustomAccountDataConfig(
+            audit_room=config["audit_room"],
+            sender=config["sender"],
+        )
+
+    async def log_new_account_data(
+        self,
+        user_id: str,
+        room_id: Optional[str],
+        account_data_type: str,
+        content: JsonDict,
+    ) -> None:
+        content_raw = json.dumps(content)
+        msg_content = f"{user_id} has changed their account data for type {account_data_type} to: {content_raw}"
+
+        if room_id is not None:
+            msg_content += f" (in room {room_id})"
+
+        await self.api.create_and_send_event_into_room(
+            {
+                "room_id": self.config.audit_room,
+                "sender": self.config.sender,
+                "type": "m.room.message",
+                "content": {
+                    "msgtype": "m.text",
+                    "body": msg_content
+                }
+            }
+        )
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/account_validity_callbacks.html b/v1.106/modules/account_validity_callbacks.html new file mode 100644 index 0000000000..69074eb568 --- /dev/null +++ b/v1.106/modules/account_validity_callbacks.html @@ -0,0 +1,240 @@ + + + + + + Account validity callbacks - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Account validity callbacks

+

Account validity callbacks allow module developers to add extra steps to verify the +validity on an account, i.e. see if a user can be granted access to their account on the +Synapse instance. Account validity callbacks can be registered using the module API's +register_account_validity_callbacks method.

+

The available account validity callbacks are:

+

is_user_expired

+

First introduced in Synapse v1.39.0

+
async def is_user_expired(user: str) -> Optional[bool]
+
+

Called when processing any authenticated request (except for logout requests). The module +can return a bool to indicate whether the user has expired and should be locked out of +their account, or None if the module wasn't able to figure it out. The user is +represented by their Matrix user ID (e.g. @alice:example.com).

+

If the module returns True, the current request will be denied with the error code +ORG_MATRIX_EXPIRED_ACCOUNT and the HTTP status code 403. Note that this doesn't +invalidate the user's access token.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns None, Synapse falls through to the next one. The value of the first +callback that does not return None will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback.

+

on_user_registration

+

First introduced in Synapse v1.39.0

+
async def on_user_registration(user: str) -> None
+
+

Called after successfully registering a user, in case the module needs to perform extra +operations to keep track of them. (e.g. add them to a database table). The user is +represented by their Matrix user ID.

+

If multiple modules implement this callback, Synapse runs them all in order.

+

on_user_login

+

First introduced in Synapse v1.98.0

+
async def on_user_login(user_id: str, auth_provider_type: str, auth_provider_id: str) -> None
+
+

Called after successfully login or registration of a user for cases when module needs to perform extra operations after auth. +represented by their Matrix user ID.

+

If multiple modules implement this callback, Synapse runs them all in order.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/add_extra_fields_to_client_events_unsigned.html b/v1.106/modules/add_extra_fields_to_client_events_unsigned.html new file mode 100644 index 0000000000..046a7bca9c --- /dev/null +++ b/v1.106/modules/add_extra_fields_to_client_events_unsigned.html @@ -0,0 +1,225 @@ + + + + + + Add extra fields to client events unsigned section callbacks - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Add extra fields to client events unsigned section callbacks

+

First introduced in Synapse v1.96.0

+

This callback allows modules to add extra fields to the unsigned section of +events when they get sent down to clients.

+

These get called every time an event is to be sent to clients, so care should +be taken to ensure with respect to performance.

+

API

+

To register the callback, use +register_add_extra_fields_to_unsigned_client_event_callbacks on the +ModuleApi.

+

The callback should be of the form

+
async def add_field_to_unsigned(
+    event: EventBase,
+) -> JsonDict:
+
+

where the extra fields to add to the event's unsigned section is returned. +(Modules must not attempt to modify the event directly).

+

This cannot be used to alter the "core" fields in the unsigned section emitted +by Synapse itself.

+

If multiple such callbacks try to add the same field to an event's unsigned +section, the last-registered callback wins.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/background_update_controller_callbacks.html b/v1.106/modules/background_update_controller_callbacks.html new file mode 100644 index 0000000000..d743fd2120 --- /dev/null +++ b/v1.106/modules/background_update_controller_callbacks.html @@ -0,0 +1,251 @@ + + + + + + Background update controller callbacks - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Background update controller callbacks

+

Background update controller callbacks allow module developers to control (e.g. rate-limit) +how database background updates are run. A database background update is an operation +Synapse runs on its database in the background after it starts. It's usually used to run +database operations that would take too long if they were run at the same time as schema +updates (which are run on startup) and delay Synapse's startup too much: populating a +table with a big amount of data, adding an index on a big table, deleting superfluous data, +etc.

+

Background update controller callbacks can be registered using the module API's +register_background_update_controller_callbacks method. Only the first module (in order +of appearance in Synapse's configuration file) calling this method can register background +update controller callbacks, subsequent calls are ignored.

+

The available background update controller callbacks are:

+

on_update

+

First introduced in Synapse v1.49.0

+
def on_update(update_name: str, database_name: str, one_shot: bool) -> AsyncContextManager[int]
+
+

Called when about to do an iteration of a background update. The module is given the name +of the update, the name of the database, and a flag to indicate whether the background +update will happen in one go and may take a long time (e.g. creating indices). If this last +argument is set to False, the update will be run in batches.

+

The module must return an async context manager. It will be entered before Synapse runs a +background update; this should return the desired duration of the iteration, in +milliseconds.

+

The context manager will be exited when the iteration completes. Note that the duration +returned by the context manager is a target, and an iteration may take substantially longer +or shorter. If the one_shot flag is set to True, the duration returned is ignored.

+

Note: Unlike most module callbacks in Synapse, this one is synchronous. This is +because asynchronous operations are expected to be run by the async context manager.

+

This callback is required when registering any other background update controller callback.

+

default_batch_size

+

First introduced in Synapse v1.49.0

+
async def default_batch_size(update_name: str, database_name: str) -> int
+
+

Called before the first iteration of a background update, with the name of the update and +of the database. The module must return the number of elements to process in this first +iteration.

+

If this callback is not defined, Synapse will use a default value of 100.

+

min_batch_size

+

First introduced in Synapse v1.49.0

+
async def min_batch_size(update_name: str, database_name: str) -> int
+
+

Called before running a new batch for a background update, with the name of the update and +of the database. The module must return an integer representing the minimum number of +elements to process in this iteration. This number must be at least 1, and is used to +ensure that progress is always made.

+

If this callback is not defined, Synapse will use a default value of 100.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/index.html b/v1.106/modules/index.html new file mode 100644 index 0000000000..5b9ad918f5 --- /dev/null +++ b/v1.106/modules/index.html @@ -0,0 +1,246 @@ + + + + + + Pluggable Modules - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Modules

+

Synapse supports extending its functionality by configuring external modules.

+

Note: When using third-party modules, you effectively allow someone else to run +custom code on your Synapse homeserver. Server admins are encouraged to verify the +provenance of the modules they use on their homeserver and make sure the modules aren't +running malicious code on their instance.

+

Using modules

+

To use a module on Synapse, add it to the modules section of the configuration file:

+
modules:
+  - module: my_super_module.MySuperClass
+    config:
+      do_thing: true
+  - module: my_other_super_module.SomeClass
+    config: {}
+
+

Each module is defined by a path to a Python class as well as a configuration. This +information for a given module should be available in the module's own documentation.

+

Using multiple modules

+

The order in which modules are listed in this section is important. When processing an +action that can be handled by several modules, Synapse will always prioritise the module +that appears first (i.e. is the highest in the list). This means:

+
    +
  • If several modules register the same callback, the callback registered by the module +that appears first is used.
  • +
  • If several modules try to register a handler for the same HTTP path, only the handler +registered by the module that appears first is used. Handlers registered by the other +module(s) are ignored and Synapse will log a warning message about them.
  • +
+

Note that Synapse doesn't allow multiple modules implementing authentication checkers via +the password auth provider feature for the same login type with different fields. If this +happens, Synapse will refuse to start.

+

Current status

+

We are currently in the process of migrating module interfaces to this system. While some +interfaces might be compatible with it, others still require configuring modules in +another part of Synapse's configuration file.

+

Currently, only the following pre-existing interfaces are compatible with this new system:

+
    +
  • spam checker
  • +
  • third-party rules
  • +
  • presence router
  • +
  • password auth providers
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/password_auth_provider_callbacks.html b/v1.106/modules/password_auth_provider_callbacks.html new file mode 100644 index 0000000000..db6510f36e --- /dev/null +++ b/v1.106/modules/password_auth_provider_callbacks.html @@ -0,0 +1,443 @@ + + + + + + Password auth provider callbacks - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Password auth provider callbacks

+

Password auth providers offer a way for server administrators to integrate +their Synapse installation with an external authentication system. The callbacks can be +registered by using the Module API's register_password_auth_provider_callbacks method.

+

Callbacks

+

auth_checkers

+

First introduced in Synapse v1.46.0

+
auth_checkers: Dict[Tuple[str, Tuple[str, ...]], Callable]
+
+

A dict mapping from tuples of a login type identifier (such as m.login.password) and a +tuple of field names (such as ("password", "secret_thing")) to authentication checking +callbacks, which should be of the following form:

+
async def check_auth(
+    user: str,
+    login_type: str,
+    login_dict: "synapse.module_api.JsonDict",
+) -> Optional[
+    Tuple[
+        str, 
+        Optional[Callable[["synapse.module_api.LoginResponse"], Awaitable[None]]]
+    ]
+]
+
+

The login type and field names should be provided by the user in the +request to the /login API. The Matrix specification +defines some types, however user defined ones are also allowed.

+

The callback is passed the user field provided by the client (which might not be in +@username:server form), the login type, and a dictionary of login secrets passed by +the client.

+

If the authentication is successful, the module must return the user's Matrix ID (e.g. +@alice:example.com) and optionally a callback to be called with the response to the +/login request. If the module doesn't wish to return a callback, it must return None +instead.

+

If the authentication is unsuccessful, the module must return None.

+

Note that the user is not automatically registered, the register_user(..) method of +the module API can be used to lazily create users.

+

If multiple modules register an auth checker for the same login type but with different +fields, Synapse will refuse to start.

+

If multiple modules register an auth checker for the same login type with the same fields, +then the callbacks will be executed in order, until one returns a Matrix User ID (and +optionally a callback). In that case, the return value of that callback will be accepted +and subsequent callbacks will not be fired. If every callback returns None, then the +authentication fails.

+

check_3pid_auth

+

First introduced in Synapse v1.46.0

+
async def check_3pid_auth(
+    medium: str, 
+    address: str,
+    password: str,
+) -> Optional[
+    Tuple[
+        str, 
+        Optional[Callable[["synapse.module_api.LoginResponse"], Awaitable[None]]]
+    ]
+]
+
+

Called when a user attempts to register or log in with a third party identifier, +such as email. It is passed the medium (eg. email), an address (eg. jdoe@example.com) +and the user's password.

+

If the authentication is successful, the module must return the user's Matrix ID (e.g. +@alice:example.com) and optionally a callback to be called with the response to the /login request. +If the module doesn't wish to return a callback, it must return None instead.

+

If the authentication is unsuccessful, the module must return None.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns None, Synapse falls through to the next one. The value of the first +callback that does not return None will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback. If every callback returns None, +the authentication is denied.

+

on_logged_out

+

First introduced in Synapse v1.46.0

+
async def on_logged_out(
+    user_id: str,
+    device_id: Optional[str],
+    access_token: str
+) -> None
+
+

Called during a logout request for a user. It is passed the qualified user ID, the ID of the +deactivated device (if any: access tokens are occasionally created without an associated +device ID), and the (now deactivated) access token.

+

Deleting the related pushers is done after calling on_logged_out, so you can rely on them +to still be present.

+

If multiple modules implement this callback, Synapse runs them all in order.

+

get_username_for_registration

+

First introduced in Synapse v1.52.0

+
async def get_username_for_registration(
+    uia_results: Dict[str, Any],
+    params: Dict[str, Any],
+) -> Optional[str]
+
+

Called when registering a new user. The module can return a username to set for the user +being registered by returning it as a string, or None if it doesn't wish to force a +username for this user. If a username is returned, it will be used as the local part of a +user's full Matrix ID (e.g. it's alice in @alice:example.com).

+

This callback is called once User-Interactive Authentication +has been completed by the user. It is not called when registering a user via SSO. It is +passed two dictionaries, which include the information that the user has provided during +the registration process.

+

The first dictionary contains the results of the User-Interactive Authentication +flow followed by the user. Its keys are the identifiers of every step involved in the flow, +associated with either a boolean value indicating whether the step was correctly completed, +or additional information (e.g. email address, phone number...). A list of most existing +identifiers can be found in the Matrix specification. +Here's an example featuring all currently supported keys:

+
{
+    "m.login.dummy": True,  # Dummy authentication
+    "m.login.terms": True,  # User has accepted the terms of service for the homeserver
+    "m.login.recaptcha": True,  # User has completed the recaptcha challenge
+    "m.login.email.identity": {  # User has provided and verified an email address
+        "medium": "email",
+        "address": "alice@example.com",
+        "validated_at": 1642701357084,
+    },
+    "m.login.msisdn": {  # User has provided and verified a phone number
+        "medium": "msisdn",
+        "address": "33123456789",
+        "validated_at": 1642701357084,
+    },
+    "m.login.registration_token": "sometoken",  # User has registered through a registration token
+}
+
+

The second dictionary contains the parameters provided by the user's client in the request +to /_matrix/client/v3/register. See the Matrix specification +for a complete list of these parameters.

+

If the module cannot, or does not wish to, generate a username for this user, it must +return None.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns None, Synapse falls through to the next one. The value of the first +callback that does not return None will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback. If every callback returns None, +the username provided by the user is used, if any (otherwise one is automatically +generated).

+

get_displayname_for_registration

+

First introduced in Synapse v1.54.0

+
async def get_displayname_for_registration(
+    uia_results: Dict[str, Any],
+    params: Dict[str, Any],
+) -> Optional[str]
+
+

Called when registering a new user. The module can return a display name to set for the +user being registered by returning it as a string, or None if it doesn't wish to force a +display name for this user.

+

This callback is called once User-Interactive Authentication +has been completed by the user. It is not called when registering a user via SSO. It is +passed two dictionaries, which include the information that the user has provided during +the registration process. These dictionaries are identical to the ones passed to +get_username_for_registration, so refer to the +documentation of this callback for more information about them.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns None, Synapse falls through to the next one. The value of the first +callback that does not return None will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback. If every callback returns None, +the username will be used (e.g. alice if the user being registered is @alice:example.com).

+

is_3pid_allowed

+

First introduced in Synapse v1.53.0

+
async def is_3pid_allowed(self, medium: str, address: str, registration: bool) -> bool
+
+

Called when attempting to bind a third-party identifier (i.e. an email address or a phone +number). The module is given the medium of the third-party identifier (which is email if +the identifier is an email address, or msisdn if the identifier is a phone number) and +its address, as well as a boolean indicating whether the attempt to bind is happening as +part of registering a new user. The module must return a boolean indicating whether the +identifier can be allowed to be bound to an account on the local homeserver.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns True, Synapse falls through to the next one. The value of the first +callback that does not return True will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback.

+

Example

+

The example module below implements authentication checkers for two different login types:

+
    +
  • my.login.type +
      +
    • Expects a my_field field to be sent to /login
    • +
    • Is checked by the method: self.check_my_login
    • +
    +
  • +
  • m.login.password (defined in the spec) +
      +
    • Expects a password field to be sent to /login
    • +
    • Is checked by the method: self.check_pass
    • +
    +
  • +
+
from typing import Awaitable, Callable, Optional, Tuple
+
+import synapse
+from synapse import module_api
+
+
+class MyAuthProvider:
+    def __init__(self, config: dict, api: module_api):
+
+        self.api = api
+
+        self.credentials = {
+            "bob": "building",
+            "@scoop:matrix.org": "digging",
+        }
+
+        api.register_password_auth_provider_callbacks(
+            auth_checkers={
+                ("my.login_type", ("my_field",)): self.check_my_login,
+                ("m.login.password", ("password",)): self.check_pass,
+            },
+        )
+
+    async def check_my_login(
+        self,
+        username: str,
+        login_type: str,
+        login_dict: "synapse.module_api.JsonDict",
+    ) -> Optional[
+        Tuple[
+            str,
+            Optional[Callable[["synapse.module_api.LoginResponse"], Awaitable[None]]],
+        ]
+    ]:
+        if login_type != "my.login_type":
+            return None
+
+        if self.credentials.get(username) == login_dict.get("my_field"):
+            return (self.api.get_qualified_user_id(username), None)
+
+    async def check_pass(
+        self,
+        username: str,
+        login_type: str,
+        login_dict: "synapse.module_api.JsonDict",
+    ) -> Optional[
+        Tuple[
+            str,
+            Optional[Callable[["synapse.module_api.LoginResponse"], Awaitable[None]]],
+        ]
+    ]:
+        if login_type != "m.login.password":
+            return None
+
+        if self.credentials.get(username) == login_dict.get("password"):
+            return (self.api.get_qualified_user_id(username), None)
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/porting_legacy_module.html b/v1.106/modules/porting_legacy_module.html new file mode 100644 index 0000000000..1a5035f265 --- /dev/null +++ b/v1.106/modules/porting_legacy_module.html @@ -0,0 +1,221 @@ + + + + + + Porting a legacy module to the new interface - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Porting an existing module that uses the old interface

+

In order to port a module that uses Synapse's old module interface, its author needs to:

+
    +
  • ensure the module's callbacks are all asynchronous.
  • +
  • register their callbacks using one or more of the register_[...]_callbacks methods +from the ModuleApi class in the module's __init__ method (see this section +for more info).
  • +
+

Additionally, if the module is packaged with an additional web resource, the module +should register this resource in its __init__ method using the register_web_resource +method from the ModuleApi class (see this section for +more info).

+

There is no longer a get_db_schema_files callback provided for password auth provider modules. Any +changes to the database should now be made by the module using the module API class.

+

The module's author should also update any example in the module's configuration to only +use the new modules section in Synapse's configuration file (see this section +for more info).

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/presence_router_callbacks.html b/v1.106/modules/presence_router_callbacks.html new file mode 100644 index 0000000000..680ed3d537 --- /dev/null +++ b/v1.106/modules/presence_router_callbacks.html @@ -0,0 +1,292 @@ + + + + + + Presence router callbacks - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Presence router callbacks

+

Presence router callbacks allow module developers to define additional users +which receive presence updates from local users. The additional users +can be local or remote.

+

For example, it could be used to direct all of @alice:example.com (a local user)'s +presence updates to @bob:matrix.org (a remote user), even though they don't share a +room. (Note that those presence updates might not make it to @bob:matrix.org's client +unless a similar presence router is running on that homeserver.)

+

Presence router callbacks can be registered using the module API's +register_presence_router_callbacks method.

+

Callbacks

+

The available presence router callbacks are:

+

get_users_for_states

+

First introduced in Synapse v1.42.0

+
async def get_users_for_states(
+    state_updates: Iterable["synapse.api.UserPresenceState"],
+) -> Dict[str, Set["synapse.api.UserPresenceState"]]
+
+

Requires get_interested_users to also be registered

+

Called when processing updates to the presence state of one or more users. This callback can +be used to instruct the server to forward that presence state to specific users. The module +must return a dictionary that maps from Matrix user IDs (which can be local or remote) to the +UserPresenceState changes that they should be forwarded.

+

Synapse will then attempt to send the specified presence updates to each user when possible.

+

If multiple modules implement this callback, Synapse merges all the dictionaries returned +by the callbacks. If multiple callbacks return a dictionary containing the same key, +Synapse concatenates the sets associated with this key from each dictionary.

+

get_interested_users

+

First introduced in Synapse v1.42.0

+
async def get_interested_users(
+    user_id: str
+) -> Union[Set[str], "synapse.module_api.PRESENCE_ALL_USERS"]
+
+

Requires get_users_for_states to also be registered

+

Called when determining which users someone should be able to see the presence state of. This +callback should return complementary results to get_users_for_state or the presence information +may not be properly forwarded.

+

The callback is given the Matrix user ID for a local user that is requesting presence data and +should return the Matrix user IDs of the users whose presence state they are allowed to +query. The returned users can be local or remote.

+

Alternatively the callback can return synapse.module_api.PRESENCE_ALL_USERS +to indicate that the user should receive updates from all known users.

+

If multiple modules implement this callback, they will be considered in order. Synapse +calls each callback one by one, and use a concatenation of all the sets returned by the +callbacks. If one callback returns synapse.module_api.PRESENCE_ALL_USERS, Synapse uses +this value instead. If this happens, Synapse does not call any of the subsequent +implementations of this callback.

+

Example

+

The example below is a module that implements both presence router callbacks, and ensures +that @alice:example.org receives all presence updates from @bob:example.com and +@charlie:somewhere.org, regardless of whether Alice shares a room with any of them.

+
from typing import Dict, Iterable, Set, Union
+
+from synapse.module_api import ModuleApi
+
+
+class CustomPresenceRouter:
+    def __init__(self, config: dict, api: ModuleApi):
+        self.api = api
+
+        self.api.register_presence_router_callbacks(
+            get_users_for_states=self.get_users_for_states,
+            get_interested_users=self.get_interested_users,
+        )
+
+    async def get_users_for_states(
+        self,
+        state_updates: Iterable["synapse.api.UserPresenceState"],
+    ) -> Dict[str, Set["synapse.api.UserPresenceState"]]:
+        res = {}
+        for update in state_updates:
+            if (
+                update.user_id == "@bob:example.com"
+                or update.user_id == "@charlie:somewhere.org"
+            ):
+                res.setdefault("@alice:example.com", set()).add(update)
+
+        return res
+
+    async def get_interested_users(
+        self,
+        user_id: str,
+    ) -> Union[Set[str], "synapse.module_api.PRESENCE_ALL_USERS"]:
+        if user_id == "@alice:example.com":
+            return {"@bob:example.com", "@charlie:somewhere.org"}
+
+        return set()
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/spam_checker_callbacks.html b/v1.106/modules/spam_checker_callbacks.html new file mode 100644 index 0000000000..fc1db0a419 --- /dev/null +++ b/v1.106/modules/spam_checker_callbacks.html @@ -0,0 +1,583 @@ + + + + + + Spam checker callbacks - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Spam checker callbacks

+

Spam checker callbacks allow module developers to implement spam mitigation actions for +Synapse instances. Spam checker callbacks can be registered using the module API's +register_spam_checker_callbacks method.

+

Callbacks

+

The available spam checker callbacks are:

+

check_event_for_spam

+

First introduced in Synapse v1.37.0

+

Changed in Synapse v1.60.0: synapse.module_api.NOT_SPAM and synapse.module_api.errors.Codes can be returned by this callback. Returning a boolean or a string is now deprecated.

+
async def check_event_for_spam(event: "synapse.module_api.EventBase") -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", str, bool]
+
+

Called when receiving an event from a client or via federation. The callback must return one of:

+
    +
  • synapse.module_api.NOT_SPAM, to allow the operation. Other callbacks may still +decide to reject it.
  • +
  • synapse.module_api.errors.Codes to reject the operation with an error code. In case +of doubt, synapse.module_api.errors.Codes.FORBIDDEN is a good error code.
  • +
  • (deprecated) a non-Codes str to reject the operation and specify an error message. Note that clients +typically will not localize the error message to the user's preferred locale.
  • +
  • (deprecated) False, which is the same as returning synapse.module_api.NOT_SPAM.
  • +
  • (deprecated) True, which is the same as returning synapse.module_api.errors.Codes.FORBIDDEN.
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns synapse.module_api.NOT_SPAM, Synapse falls through to the next one. +The value of the first callback that does not return synapse.module_api.NOT_SPAM will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

user_may_join_room

+

First introduced in Synapse v1.37.0

+

Changed in Synapse v1.61.0: synapse.module_api.NOT_SPAM and synapse.module_api.errors.Codes can be returned by this callback. Returning a boolean is now deprecated.

+
async def user_may_join_room(user: str, room: str, is_invited: bool) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
+
+

Called when a user is trying to join a room. The user is represented by their Matrix user ID (e.g. +@alice:example.com) and the room is represented by its Matrix ID (e.g. +!room:example.com). The module is also given a boolean to indicate whether the user +currently has a pending invite in the room.

+

This callback isn't called if the join is performed by a server administrator, or in the +context of a room creation.

+

The callback must return one of:

+
    +
  • synapse.module_api.NOT_SPAM, to allow the operation. Other callbacks may still +decide to reject it.
  • +
  • synapse.module_api.errors.Codes to reject the operation with an error code. In case +of doubt, synapse.module_api.errors.Codes.FORBIDDEN is a good error code.
  • +
  • (deprecated) False, which is the same as returning synapse.module_api.NOT_SPAM.
  • +
  • (deprecated) True, which is the same as returning synapse.module_api.errors.Codes.FORBIDDEN.
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns synapse.module_api.NOT_SPAM, Synapse falls through to the next one. +The value of the first callback that does not return synapse.module_api.NOT_SPAM will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

user_may_invite

+

First introduced in Synapse v1.37.0

+

Changed in Synapse v1.62.0: synapse.module_api.NOT_SPAM and synapse.module_api.errors.Codes can be returned by this callback. Returning a boolean is now deprecated.

+
async def user_may_invite(inviter: str, invitee: str, room_id: str) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
+
+

Called when processing an invitation. Both inviter and invitee are +represented by their Matrix user ID (e.g. @alice:example.com).

+

The callback must return one of:

+
    +
  • +

    synapse.module_api.NOT_SPAM, to allow the operation. Other callbacks may still +decide to reject it.

    +
  • +
  • +

    synapse.module_api.errors.Codes to reject the operation with an error code. In case +of doubt, synapse.module_api.errors.Codes.FORBIDDEN is a good error code.

    +
  • +
  • +

    (deprecated) False, which is the same as returning synapse.module_api.NOT_SPAM.

    +
  • +
  • +

    (deprecated) True, which is the same as returning synapse.module_api.errors.Codes.FORBIDDEN.

    +
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns synapse.module_api.NOT_SPAM, Synapse falls through to the next one. +The value of the first callback that does not return synapse.module_api.NOT_SPAM will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

user_may_send_3pid_invite

+

First introduced in Synapse v1.45.0

+

Changed in Synapse v1.62.0: synapse.module_api.NOT_SPAM and synapse.module_api.errors.Codes can be returned by this callback. Returning a boolean is now deprecated.

+
async def user_may_send_3pid_invite(
+    inviter: str,
+    medium: str,
+    address: str,
+    room_id: str,
+) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
+
+

Called when processing an invitation using a third-party identifier (also called a 3PID, +e.g. an email address or a phone number).

+

The inviter is represented by their Matrix user ID (e.g. @alice:example.com), and the +invitee is represented by its medium (e.g. "email") and its address +(e.g. alice@example.com). See the Matrix specification +for more information regarding third-party identifiers.

+

For example, a call to this callback to send an invitation to the email address +alice@example.com would look like this:

+
await user_may_send_3pid_invite(
+    "@bob:example.com",  # The inviter's user ID
+    "email",  # The medium of the 3PID to invite
+    "alice@example.com",  # The address of the 3PID to invite
+    "!some_room:example.com",  # The ID of the room to send the invite into
+)
+
+

Note: If the third-party identifier is already associated with a matrix user ID, +user_may_invite will be used instead.

+

The callback must return one of:

+
    +
  • +

    synapse.module_api.NOT_SPAM, to allow the operation. Other callbacks may still +decide to reject it.

    +
  • +
  • +

    synapse.module_api.errors.Codes to reject the operation with an error code. In case +of doubt, synapse.module_api.errors.Codes.FORBIDDEN is a good error code.

    +
  • +
  • +

    (deprecated) False, which is the same as returning synapse.module_api.NOT_SPAM.

    +
  • +
  • +

    (deprecated) True, which is the same as returning synapse.module_api.errors.Codes.FORBIDDEN.

    +
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns synapse.module_api.NOT_SPAM, Synapse falls through to the next one. +The value of the first callback that does not return synapse.module_api.NOT_SPAM will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

user_may_create_room

+

First introduced in Synapse v1.37.0

+

Changed in Synapse v1.62.0: synapse.module_api.NOT_SPAM and synapse.module_api.errors.Codes can be returned by this callback. Returning a boolean is now deprecated.

+
async def user_may_create_room(user_id: str) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
+
+

Called when processing a room creation request.

+

The callback must return one of:

+
    +
  • +

    synapse.module_api.NOT_SPAM, to allow the operation. Other callbacks may still +decide to reject it.

    +
  • +
  • +

    synapse.module_api.errors.Codes to reject the operation with an error code. In case +of doubt, synapse.module_api.errors.Codes.FORBIDDEN is a good error code.

    +
  • +
  • +

    (deprecated) False, which is the same as returning synapse.module_api.NOT_SPAM.

    +
  • +
  • +

    (deprecated) True, which is the same as returning synapse.module_api.errors.Codes.FORBIDDEN.

    +
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns synapse.module_api.NOT_SPAM, Synapse falls through to the next one. +The value of the first callback that does not return synapse.module_api.NOT_SPAM will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

user_may_create_room_alias

+

First introduced in Synapse v1.37.0

+

Changed in Synapse v1.62.0: synapse.module_api.NOT_SPAM and synapse.module_api.errors.Codes can be returned by this callback. Returning a boolean is now deprecated.

+
async def user_may_create_room_alias(user_id: str, room_alias: "synapse.module_api.RoomAlias") -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
+
+

Called when trying to associate an alias with an existing room.

+

The callback must return one of:

+
    +
  • +

    synapse.module_api.NOT_SPAM, to allow the operation. Other callbacks may still +decide to reject it.

    +
  • +
  • +

    synapse.module_api.errors.Codes to reject the operation with an error code. In case +of doubt, synapse.module_api.errors.Codes.FORBIDDEN is a good error code.

    +
  • +
  • +

    (deprecated) False, which is the same as returning synapse.module_api.NOT_SPAM.

    +
  • +
  • +

    (deprecated) True, which is the same as returning synapse.module_api.errors.Codes.FORBIDDEN.

    +
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns synapse.module_api.NOT_SPAM, Synapse falls through to the next one. +The value of the first callback that does not return synapse.module_api.NOT_SPAM will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

user_may_publish_room

+

First introduced in Synapse v1.37.0

+

Changed in Synapse v1.62.0: synapse.module_api.NOT_SPAM and synapse.module_api.errors.Codes can be returned by this callback. Returning a boolean is now deprecated.

+
async def user_may_publish_room(user_id: str, room_id: str) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
+
+

Called when trying to publish a room to the homeserver's public rooms directory.

+

The callback must return one of:

+
    +
  • +

    synapse.module_api.NOT_SPAM, to allow the operation. Other callbacks may still +decide to reject it.

    +
  • +
  • +

    synapse.module_api.errors.Codes to reject the operation with an error code. In case +of doubt, synapse.module_api.errors.Codes.FORBIDDEN is a good error code.

    +
  • +
  • +

    (deprecated) False, which is the same as returning synapse.module_api.NOT_SPAM.

    +
  • +
  • +

    (deprecated) True, which is the same as returning synapse.module_api.errors.Codes.FORBIDDEN.

    +
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns synapse.module_api.NOT_SPAM, Synapse falls through to the next one. +The value of the first callback that does not return synapse.module_api.NOT_SPAM will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

check_username_for_spam

+

First introduced in Synapse v1.37.0

+
async def check_username_for_spam(user_profile: synapse.module_api.UserProfile) -> bool
+
+

Called when computing search results in the user directory. The module must return a +bool indicating whether the given user should be excluded from user directory +searches. Return True to indicate that the user is spammy and exclude them from +search results; otherwise return False.

+

The profile is represented as a dictionary with the following keys:

+
    +
  • user_id: str. The Matrix ID for this user.
  • +
  • display_name: Optional[str]. The user's display name, or None if this user +has not set a display name.
  • +
  • avatar_url: Optional[str]. The mxc:// URL to the user's avatar, or None +if this user has not set an avatar.
  • +
+

The module is given a copy of the original dictionary, so modifying it from within the +module cannot modify a user's profile when included in user directory search results.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns False, Synapse falls through to the next one. The value of the first +callback that does not return False will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback.

+

check_registration_for_spam

+

First introduced in Synapse v1.37.0

+
async def check_registration_for_spam(
+    email_threepid: Optional[dict],
+    username: Optional[str],
+    request_info: Collection[Tuple[str, str]],
+    auth_provider_id: Optional[str] = None,
+) -> "synapse.spam_checker_api.RegistrationBehaviour"
+
+

Called when registering a new user. The module must return a RegistrationBehaviour +indicating whether the registration can go through or must be denied, or whether the user +may be allowed to register but will be shadow banned.

+

The arguments passed to this callback are:

+
    +
  • email_threepid: The email address used for registering, if any.
  • +
  • username: The username the user would like to register. Can be None, meaning that +Synapse will generate one later.
  • +
  • request_info: A collection of tuples, which first item is a user agent, and which +second item is an IP address. These user agents and IP addresses are the ones that were +used during the registration process.
  • +
  • auth_provider_id: The identifier of the SSO authentication provider, if any.
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns RegistrationBehaviour.ALLOW, Synapse falls through to the next one. +The value of the first callback that does not return RegistrationBehaviour.ALLOW will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

check_media_file_for_spam

+

First introduced in Synapse v1.37.0

+

Changed in Synapse v1.62.0: synapse.module_api.NOT_SPAM and synapse.module_api.errors.Codes can be returned by this callback. Returning a boolean is now deprecated.

+
async def check_media_file_for_spam(
+    file_wrapper: "synapse.media.media_storage.ReadableFileWrapper",
+    file_info: "synapse.media._base.FileInfo",
+) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
+
+

Called when storing a local or remote file.

+

The callback must return one of:

+
    +
  • +

    synapse.module_api.NOT_SPAM, to allow the operation. Other callbacks may still +decide to reject it.

    +
  • +
  • +

    synapse.module_api.errors.Codes to reject the operation with an error code. In case +of doubt, synapse.module_api.errors.Codes.FORBIDDEN is a good error code.

    +
  • +
  • +

    (deprecated) False, which is the same as returning synapse.module_api.NOT_SPAM.

    +
  • +
  • +

    (deprecated) True, which is the same as returning synapse.module_api.errors.Codes.FORBIDDEN.

    +
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns synapse.module_api.NOT_SPAM, Synapse falls through to the next one. +The value of the first callback that does not return synapse.module_api.NOT_SPAM will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

should_drop_federated_event

+

First introduced in Synapse v1.60.0

+
async def should_drop_federated_event(event: "synapse.events.EventBase") -> bool
+
+

Called when checking whether a remote server can federate an event with us. Returning +True from this function will silently drop a federated event and split-brain our view +of a room's DAG, and thus you shouldn't use this callback unless you know what you are +doing.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns False, Synapse falls through to the next one. The value of the first +callback that does not return False will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback.

+

check_login_for_spam

+

First introduced in Synapse v1.87.0

+
async def check_login_for_spam(
+    user_id: str,
+    device_id: Optional[str],
+    initial_display_name: Optional[str],
+    request_info: Collection[Tuple[Optional[str], str]],
+    auth_provider_id: Optional[str] = None,
+) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes"]
+
+

Called when a user logs in.

+

The arguments passed to this callback are:

+
    +
  • user_id: The user ID the user is logging in with
  • +
  • device_id: The device ID the user is re-logging into.
  • +
  • initial_display_name: The device display name, if any.
  • +
  • request_info: A collection of tuples, which first item is a user agent, and which +second item is an IP address. These user agents and IP addresses are the ones that were +used during the login process.
  • +
  • auth_provider_id: The identifier of the SSO authentication provider, if any.
  • +
+

If multiple modules implement this callback, they will be considered in order. If a +callback returns synapse.module_api.NOT_SPAM, Synapse falls through to the next one. +The value of the first callback that does not return synapse.module_api.NOT_SPAM will +be used. If this happens, Synapse will not call any of the subsequent implementations of +this callback.

+

Note: This will not be called when a user registers.

+

Example

+

The example below is a module that implements the spam checker callback +check_event_for_spam to deny any message sent by users whose Matrix user IDs are +mentioned in a configured list, and registers a web resource to the path +/_synapse/client/list_spam_checker/is_evil that returns a JSON object indicating +whether the provided user appears in that list.

+
import json
+from typing import Union
+
+from twisted.web.resource import Resource
+from twisted.web.server import Request
+
+from synapse.module_api import ModuleApi
+
+
+class IsUserEvilResource(Resource):
+    def __init__(self, config):
+        super(IsUserEvilResource, self).__init__()
+        self.evil_users = config.get("evil_users") or []
+
+    def render_GET(self, request: Request):
+        user = request.args.get(b"user")[0].decode()
+        request.setHeader(b"Content-Type", b"application/json")
+        return json.dumps({"evil": user in self.evil_users}).encode()
+
+
+class ListSpamChecker:
+    def __init__(self, config: dict, api: ModuleApi):
+        self.api = api
+        self.evil_users = config.get("evil_users") or []
+
+        self.api.register_spam_checker_callbacks(
+            check_event_for_spam=self.check_event_for_spam,
+        )
+
+        self.api.register_web_resource(
+            path="/_synapse/client/list_spam_checker/is_evil",
+            resource=IsUserEvilResource(config),
+        )
+
+    async def check_event_for_spam(self, event: "synapse.events.EventBase") -> Union[Literal["NOT_SPAM"], Codes]:
+        if event.sender in self.evil_users:
+          return Codes.FORBIDDEN
+        else:
+          return synapse.module_api.NOT_SPAM
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/third_party_rules_callbacks.html b/v1.106/modules/third_party_rules_callbacks.html new file mode 100644 index 0000000000..e96e8cfb4b --- /dev/null +++ b/v1.106/modules/third_party_rules_callbacks.html @@ -0,0 +1,455 @@ + + + + + + Third-party rules callbacks - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Third party rules callbacks

+

Third party rules callbacks allow module developers to add extra checks to verify the +validity of incoming events. Third party event rules callbacks can be registered using +the module API's register_third_party_rules_callbacks method.

+

Callbacks

+

The available third party rules callbacks are:

+

check_event_allowed

+

First introduced in Synapse v1.39.0

+
async def check_event_allowed(
+    event: "synapse.events.EventBase",
+    state_events: "synapse.types.StateMap",
+) -> Tuple[bool, Optional[dict]]
+
+

+This callback is very experimental and can and will break without notice. Module developers +are encouraged to implement check_event_for_spam from the spam checker category instead. +

+

Called when processing any incoming event, with the event and a StateMap +representing the current state of the room the event is being sent into. A StateMap is +a dictionary that maps tuples containing an event type and a state key to the +corresponding state event. For example retrieving the room's m.room.create event from +the state_events argument would look like this: state_events.get(("m.room.create", "")). +The module must return a boolean indicating whether the event can be allowed.

+

Note that this callback function processes incoming events coming via federation +traffic (on top of client traffic). This means denying an event might cause the local +copy of the room's history to diverge from that of remote servers. This may cause +federation issues in the room. It is strongly recommended to only deny events using this +callback function if the sender is a local user, or in a private federation in which all +servers are using the same module, with the same configuration.

+

If the boolean returned by the module is True, it may also tell Synapse to replace the +event with new data by returning the new event's data as a dictionary. In order to do +that, it is recommended the module calls event.get_dict() to get the current event as a +dictionary, and modify the returned dictionary accordingly.

+

If check_event_allowed raises an exception, the module is assumed to have failed. +The event will not be accepted but is not treated as explicitly rejected, either. +An HTTP request causing the module check will likely result in a 500 Internal +Server Error.

+

When the boolean returned by the module is False, the event is rejected. +(Module developers should not use exceptions for rejection.)

+

Note that replacing the event only works for events sent by local users, not for events +received over federation.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns True, Synapse falls through to the next one. The value of the first +callback that does not return True will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback.

+

on_create_room

+

First introduced in Synapse v1.39.0

+
async def on_create_room(
+    requester: "synapse.types.Requester",
+    request_content: dict,
+    is_requester_admin: bool,
+) -> None
+
+

Called when processing a room creation request, with the Requester object for the user +performing the request, a dictionary representing the room creation request's JSON body +(see the spec +for a list of possible parameters), and a boolean indicating whether the user performing +the request is a server admin.

+

Modules can modify the request_content (by e.g. adding events to its initial_state), +or deny the room's creation by raising a module_api.errors.SynapseError.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns without raising an exception, Synapse falls through to the next one. The +room creation will be forbidden as soon as one of the callbacks raises an exception. If +this happens, Synapse will not call any of the subsequent implementations of this +callback.

+

check_threepid_can_be_invited

+

First introduced in Synapse v1.39.0

+
async def check_threepid_can_be_invited(
+    medium: str,
+    address: str,
+    state_events: "synapse.types.StateMap",
+) -> bool:
+
+

Called when processing an invite via a third-party identifier (i.e. email or phone number). +The module must return a boolean indicating whether the invite can go through.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns True, Synapse falls through to the next one. The value of the first +callback that does not return True will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback.

+

check_visibility_can_be_modified

+

First introduced in Synapse v1.39.0

+
async def check_visibility_can_be_modified(
+    room_id: str,
+    state_events: "synapse.types.StateMap",
+    new_visibility: str,
+) -> bool:
+
+

Called when changing the visibility of a room in the local public room directory. The +visibility is a string that's either "public" or "private". The module must return a +boolean indicating whether the change can go through.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns True, Synapse falls through to the next one. The value of the first +callback that does not return True will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback.

+

on_new_event

+

First introduced in Synapse v1.47.0

+
async def on_new_event(
+    event: "synapse.events.EventBase",
+    state_events: "synapse.types.StateMap",
+) -> None:
+
+

Called after sending an event into a room. The module is passed the event, as well +as the state of the room after the event. This means that if the event is a state event, +it will be included in this state.

+

The state map may not be complete if Synapse hasn't yet loaded the full state +of the room. This can happen for events in rooms that were just joined from +a remote server.

+

Note that this callback is called when the event has already been processed and stored +into the room, which means this callback cannot be used to deny persisting the event. To +deny an incoming event, see check_event_for_spam instead.

+

For any given event, this callback will be called on every worker process, even if that worker will not end up +acting on that event. This callback will not be called for events that are marked as rejected.

+

If multiple modules implement this callback, Synapse runs them all in order.

+

check_can_shutdown_room

+

First introduced in Synapse v1.55.0

+
async def check_can_shutdown_room(
+    user_id: str, room_id: str,
+) -> bool:
+
+

Called when an admin user requests the shutdown of a room. The module must return a +boolean indicating whether the shutdown can go through. If the callback returns False, +the shutdown will not proceed and the caller will see a M_FORBIDDEN error.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns True, Synapse falls through to the next one. The value of the first +callback that does not return True will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback.

+

check_can_deactivate_user

+

First introduced in Synapse v1.55.0

+
async def check_can_deactivate_user(
+    user_id: str, by_admin: bool,
+) -> bool:
+
+

Called when the deactivation of a user is requested. User deactivation can be +performed by an admin or the user themselves, so developers are encouraged to check the +requester when implementing this callback. The module must return a +boolean indicating whether the deactivation can go through. If the callback returns False, +the deactivation will not proceed and the caller will see a M_FORBIDDEN error.

+

The module is passed two parameters, user_id which is the ID of the user being deactivated, and by_admin which is True if the request is made by a serve admin, and False otherwise.

+

If multiple modules implement this callback, they will be considered in order. If a +callback returns True, Synapse falls through to the next one. The value of the first +callback that does not return True will be used. If this happens, Synapse will not call +any of the subsequent implementations of this callback.

+

on_profile_update

+

First introduced in Synapse v1.54.0

+
async def on_profile_update(
+    user_id: str,
+    new_profile: "synapse.module_api.ProfileInfo",
+    by_admin: bool,
+    deactivation: bool,
+) -> None:
+
+

Called after updating a local user's profile. The update can be triggered either by the +user themselves or a server admin. The update can also be triggered by a user being +deactivated (in which case their display name is set to an empty string ("") and the +avatar URL is set to None). The module is passed the Matrix ID of the user whose profile +has been updated, their new profile, as well as a by_admin boolean that is True if the +update was triggered by a server admin (and False otherwise), and a deactivated +boolean that is True if the update is a result of the user being deactivated.

+

Note that the by_admin boolean is also True if the profile change happens as a result +of the user logging in through Single Sign-On, or if a server admin updates their own +profile.

+

Per-room profile changes do not trigger this callback to be called. Synapse administrators +wishing this callback to be called on every profile change are encouraged to disable +per-room profiles globally using the allow_per_room_profiles configuration setting in +Synapse's configuration file. +This callback is not called when registering a user, even when setting it through the +get_displayname_for_registration +module callback.

+

If multiple modules implement this callback, Synapse runs them all in order.

+

on_user_deactivation_status_changed

+

First introduced in Synapse v1.54.0

+
async def on_user_deactivation_status_changed(
+    user_id: str, deactivated: bool, by_admin: bool
+) -> None:
+
+

Called after deactivating a local user, or reactivating them through the admin API. The +deactivation can be triggered either by the user themselves or a server admin. The module +is passed the Matrix ID of the user whose status is changed, as well as a deactivated +boolean that is True if the user is being deactivated and False if they're being +reactivated, and a by_admin boolean that is True if the deactivation was triggered by +a server admin (and False otherwise). This latter by_admin boolean is always True +if the user is being reactivated, as this operation can only be performed through the +admin API.

+

If multiple modules implement this callback, Synapse runs them all in order.

+

on_threepid_bind

+

First introduced in Synapse v1.56.0

+

+This callback is deprecated in favour of the on_add_user_third_party_identifier callback, which +features the same functionality. The only difference is in name. +

+
async def on_threepid_bind(user_id: str, medium: str, address: str) -> None:
+
+

Called after creating an association between a local user and a third-party identifier +(email address, phone number). The module is given the Matrix ID of the user the +association is for, as well as the medium (email or msisdn) and address of the +third-party identifier.

+

Note that this callback is not called after a successful association on an identity +server.

+

If multiple modules implement this callback, Synapse runs them all in order.

+

on_add_user_third_party_identifier

+

First introduced in Synapse v1.79.0

+
async def on_add_user_third_party_identifier(user_id: str, medium: str, address: str) -> None:
+
+

Called after successfully creating an association between a user and a third-party identifier +(email address, phone number). The module is given the Matrix ID of the user the +association is for, as well as the medium (email or msisdn) and address of the +third-party identifier (i.e. an email address).

+

Note that this callback is not called if a user attempts to bind their third-party identifier +to an identity server (via a call to POST /_matrix/client/v3/account/3pid/bind).

+

If multiple modules implement this callback, Synapse runs them all in order.

+

on_remove_user_third_party_identifier

+

First introduced in Synapse v1.79.0

+
async def on_remove_user_third_party_identifier(user_id: str, medium: str, address: str) -> None:
+
+

Called after successfully removing an association between a user and a third-party identifier +(email address, phone number). The module is given the Matrix ID of the user the +association is for, as well as the medium (email or msisdn) and address of the +third-party identifier (i.e. an email address).

+

Note that this callback is not called if a user attempts to unbind their third-party +identifier from an identity server (via a call to POST /_matrix/client/v3/account/3pid/unbind).

+

If multiple modules implement this callback, Synapse runs them all in order.

+

Example

+

The example below is a module that implements the third-party rules callback +check_event_allowed to censor incoming messages as dictated by a third-party service.

+
from typing import Optional, Tuple
+
+from synapse.module_api import ModuleApi
+
+_DEFAULT_CENSOR_ENDPOINT = "https://my-internal-service.local/censor-event"
+
+class EventCensorer:
+    def __init__(self, config: dict, api: ModuleApi):
+        self.api = api
+        self._endpoint = config.get("endpoint", _DEFAULT_CENSOR_ENDPOINT)
+
+        self.api.register_third_party_rules_callbacks(
+            check_event_allowed=self.check_event_allowed,
+        )
+
+    async def check_event_allowed(
+        self,
+        event: "synapse.events.EventBase",
+        state_events: "synapse.types.StateMap",
+    ) -> Tuple[bool, Optional[dict]]:
+        event_dict = event.get_dict()
+        new_event_content = await self.api.http_client.post_json_get_json(
+            uri=self._endpoint, post_json=event_dict,
+        )
+        event_dict["content"] = new_event_content
+        return event_dict
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + diff --git a/v1.106/modules/writing_a_module.html b/v1.106/modules/writing_a_module.html new file mode 100644 index 0000000000..9a1525490c --- /dev/null +++ b/v1.106/modules/writing_a_module.html @@ -0,0 +1,313 @@ + + + + + + Writing a module - Synapse + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +

Writing a module

+

A module is a Python class that uses Synapse's module API to interact with the +homeserver. It can register callbacks that Synapse will call on specific operations, as +well as web resources to attach to Synapse's web server.

+

When instantiated, a module is given its parsed configuration as well as an instance of +the synapse.module_api.ModuleApi class. The configuration is a dictionary, and is +either the output of the module's parse_config static method (see below), or the +configuration associated with the module in Synapse's configuration file.

+

See the documentation for the ModuleApi class +here.

+

When Synapse runs with several modules configured

+

If Synapse is running with other modules configured, the order each module appears in +within the modules section of the Synapse configuration file might restrict what it can +or cannot register. See this section for more +information.

+

On top of the rules listed in the link above, if a callback returns a value that should +cause the current operation to fail (e.g. if a callback checking an event returns with a +value that should cause the event to be denied), Synapse will fail the operation and +ignore any subsequent callbacks that should have been run after this one.

+

The documentation for each callback mentions how Synapse behaves when +multiple modules implement it.

+

Handling the module's configuration

+

A module can implement the following static method:

+
@staticmethod
+def parse_config(config: dict) -> Any
+
+

This method is given a dictionary resulting from parsing the YAML configuration for the +module. It may modify it (for example by parsing durations expressed as strings (e.g. +"5d") into milliseconds, etc.), and return the modified dictionary. It may also verify +that the configuration is correct, and raise an instance of +synapse.module_api.errors.ConfigError if not.

+

Registering a web resource

+

Modules can register web resources onto Synapse's web server using the following module +API method:

+
def ModuleApi.register_web_resource(path: str, resource: IResource) -> None
+
+

The path is the full absolute path to register the resource at. For example, if you +register a resource for the path /_synapse/client/my_super_module/say_hello, Synapse +will serve it at http(s)://[HS_URL]/_synapse/client/my_super_module/say_hello. Note +that Synapse does not allow registering resources for several sub-paths in the /_matrix +namespace (such as anything under /_matrix/client for example). It is strongly +recommended that modules register their web resources under the /_synapse/client +namespace.

+

The provided resource is a Python class that implements Twisted's IResource +interface (such as Resource).

+

Only one resource can be registered for a given path. If several modules attempt to +register a resource for the same path, the module that appears first in Synapse's +configuration file takes priority.

+

Modules must register their web resources in their __init__ method.

+

Registering a callback

+

Modules can use Synapse's module API to register callbacks. Callbacks are functions that +Synapse will call when performing specific actions. Callbacks must be asynchronous (unless +specified otherwise), and are split in categories. A single module may implement callbacks +from multiple categories, and is under no obligation to implement all callbacks from the +categories it registers callbacks for.

+

Modules can register callbacks using one of the module API's register_[...]_callbacks +methods. The callback functions are passed to these methods as keyword arguments, with +the callback name as the argument name and the function as its value. A +register_[...]_callbacks method exists for each category.

+

Callbacks for each category can be found on their respective page of the +Synapse documentation website.

+

Caching

+

Added in Synapse 1.74.0.

+

Modules can leverage Synapse's caching tools to manage their own cached functions. This +can be helpful for modules that need to repeatedly request the same data from the database +or a remote service.

+

Functions that need to be wrapped with a cache need to be decorated with a @cached() +decorator (which can be imported from synapse.module_api) and registered with the +ModuleApi.register_cached_function +API when initialising the module. If the module needs to invalidate an entry in a cache, +it needs to use the ModuleApi.invalidate_cache +API, with the function to invalidate the cache of and the key(s) of the entry to +invalidate.

+

Below is an example of a simple module using a cached function:

+
from typing import Any
+from synapse.module_api import cached, ModuleApi
+
+class MyModule:
+    def __init__(self, config: Any, api: ModuleApi):
+        self.api = api
+
+        # Register the cached function so Synapse knows how to correctly invalidate
+        # entries for it.
+        self.api.register_cached_function(self.get_user_from_id)
+
+    @cached()
+    async def get_department_for_user(self, user_id: str) -> str:
+        """A function with a cache."""
+        # Request a department from an external service.
+        return await self.http_client.get_json(
+            "https://int.example.com/users", {"user_id": user_id)
+        )["department"]
+
+    async def do_something_with_users(self) -> None:
+        """Calls the cached function and then invalidates an entry in its cache."""
+
+        user_id = "@alice:example.com"
+
+        # Get the user. Since get_department_for_user is wrapped with a cache,
+        # the return value for this user_id will be cached.
+        department = await self.get_department_for_user(user_id)
+
+        # Do something with `department`...
+
+        # Let's say something has changed with our user, and the entry we have for
+        # them in the cache is out of date, so we want to invalidate it.
+        await self.api.invalidate_cache(self.get_department_for_user, (user_id,))
+
+

See the cached docstring for more details.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + -- cgit 1.5.1