Document Synapse's behaviour when multiple modules register the same callback/web resource/etc. Co-authored-by: reivilibre <oliverw@matrix.org>tags/v1.46.0rc1
@@ -0,0 +1 @@ | |||
Document Synapse's behaviour when dealing with multiple modules registering the same callbacks and/or handlers for the same HTTP endpoints. |
@@ -22,6 +22,11 @@ If the module returns `True`, the current request will be denied with the error | |||
`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` | |||
```python | |||
@@ -31,3 +36,5 @@ 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. |
@@ -2,6 +2,11 @@ | |||
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: | |||
@@ -18,17 +23,31 @@ modules: | |||
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. | |||
**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 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 | |||
Also note that 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. | |||
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 |
@@ -44,6 +44,15 @@ instead. | |||
If the authentication is unsuccessful, the module must return `None`. | |||
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` | |||
```python | |||
@@ -67,7 +76,13 @@ If the authentication is successful, the module must return the user's Matrix ID | |||
`@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 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 return `None`, | |||
the authentication is denied. | |||
### `on_logged_out` | |||
@@ -82,6 +97,8 @@ Called during a logout request for a user. It is passed the qualified user ID, t | |||
deactivated device (if any: access tokens are occasionally created without an associated | |||
device ID), and the (now deactivated) access token. | |||
If multiple modules implement this callback, Synapse runs them all in order. | |||
## Example | |||
The example module below implements authentication checkers for two different login types: | |||
@@ -24,6 +24,10 @@ must return a dictionary that maps from Matrix user IDs (which can be local or r | |||
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` | |||
```python | |||
@@ -44,6 +48,12 @@ 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 `set`s 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 | |||
@@ -19,6 +19,11 @@ either a `bool` to indicate whether the event must be rejected because of spam, | |||
to indicate the event must be rejected because of spam and to give a rejection reason to | |||
forward to clients. | |||
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. | |||
### `user_may_join_room` | |||
```python | |||
@@ -34,6 +39,11 @@ 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. | |||
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. | |||
### `user_may_invite` | |||
```python | |||
@@ -44,6 +54,11 @@ Called when processing an invitation. The module must return a `bool` indicating | |||
the inviter can invite the invitee to the given room. Both inviter and invitee are | |||
represented by their Matrix user ID (e.g. `@alice:example.com`). | |||
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. | |||
### `user_may_send_3pid_invite` | |||
```python | |||
@@ -79,6 +94,11 @@ await user_may_send_3pid_invite( | |||
**Note**: If the third-party identifier is already associated with a matrix user ID, | |||
[`user_may_invite`](#user_may_invite) will be used instead. | |||
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. | |||
### `user_may_create_room` | |||
```python | |||
@@ -88,6 +108,11 @@ async def user_may_create_room(user: str) -> bool | |||
Called when processing a room creation request. The module must return a `bool` indicating | |||
whether the given user (represented by their Matrix user ID) is allowed to create a room. | |||
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. | |||
### `user_may_create_room_with_invites` | |||
```python | |||
@@ -117,6 +142,11 @@ corresponding list(s) will be empty. | |||
since no invites are sent when cloning a room. To cover this case, modules also need to | |||
implement `user_may_create_room`. | |||
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. | |||
### `user_may_create_room_alias` | |||
```python | |||
@@ -127,6 +157,11 @@ Called when trying to associate an alias with an existing room. The module must | |||
`bool` indicating whether the given user (represented by their Matrix user ID) is allowed | |||
to set the given alias. | |||
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. | |||
### `user_may_publish_room` | |||
```python | |||
@@ -137,6 +172,11 @@ Called when trying to publish a room to the homeserver's public rooms directory. | |||
module must return a `bool` indicating whether the given user (represented by their | |||
Matrix user ID) is allowed to publish the given room. | |||
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_username_for_spam` | |||
```python | |||
@@ -154,6 +194,11 @@ is represented as a dictionary with the following keys: | |||
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` | |||
```python | |||
@@ -179,6 +224,12 @@ The arguments passed to this callback are: | |||
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` | |||
```python | |||
@@ -191,6 +242,11 @@ async def check_media_file_for_spam( | |||
Called when storing a local or remote file. The module must return a boolean indicating | |||
whether the given file can be stored in the homeserver's media store. | |||
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. | |||
## Example | |||
The example below is a module that implements the spam checker callback | |||
@@ -44,6 +44,11 @@ dictionary, and modify the returned dictionary accordingly. | |||
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` | |||
```python | |||
@@ -63,6 +68,12 @@ 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` | |||
```python | |||
@@ -76,6 +87,11 @@ async def check_threepid_can_be_invited( | |||
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` | |||
```python | |||
@@ -90,6 +106,11 @@ Called when changing the visibility of a room in the local public room directory | |||
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. | |||
## Example | |||
The example below is a module that implements the third-party rules callback | |||
@@ -12,6 +12,21 @@ configuration associated with the module in Synapse's configuration file. | |||
See the documentation for the `ModuleApi` class | |||
[here](https://github.com/matrix-org/synapse/blob/master/synapse/module_api/__init__.py). | |||
## 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](index.html#using-multiple-modules) 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: | |||