From 8d300e2e71a5c7483241d71a44cb15382e51e220 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Fri, 15 Oct 2021 17:24:40 +0100 Subject: [PATCH 1/5] Document Synapse's behaviour when dealing with multiple modules Document Synapse's behaviour when multiple modules register the same callback/web resource/etc. --- docs/modules/account_validity_callbacks.md | 7 +++ docs/modules/index.md | 33 ++++++++++--- .../password_auth_provider_callbacks.md | 15 ++++++ docs/modules/presence_router_callbacks.md | 8 ++++ docs/modules/spam_checker_callbacks.md | 48 +++++++++++++++++++ docs/modules/third_party_rules_callbacks.md | 18 +++++++ docs/modules/writing_a_module.md | 15 ++++++ 7 files changed, 137 insertions(+), 7 deletions(-) diff --git a/docs/modules/account_validity_callbacks.md b/docs/modules/account_validity_callbacks.md index 80684b7828f6..54bd9919c0df 100644 --- a/docs/modules/account_validity_callbacks.md +++ b/docs/modules/account_validity_callbacks.md @@ -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, the first callback returning `bool` (i.e. +not `None`) causes the returned value to be used to determine whether the user is expired, +and the subsequent implementations of this callback to be ignored. If all callbacks return +`None`, the user is considered not expired. + ### `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. diff --git a/docs/modules/index.md b/docs/modules/index.md index 3fda8cb7f0c5..967981a10c2e 100644 --- a/docs/modules/index.md +++ b/docs/modules/index.md @@ -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 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 diff --git a/docs/modules/password_auth_provider_callbacks.md b/docs/modules/password_auth_provider_callbacks.md index 36417dd39e20..f9e7c63d870b 100644 --- a/docs/modules/password_auth_provider_callbacks.md +++ b/docs/modules/password_auth_provider_callbacks.md @@ -44,6 +44,14 @@ 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, +the first callback returning a Matrix user ID (and optionally a callback) will cause +Synapse to consider this user ID, and the subsequent implementations of this callback to +be ignored. If all the callbacks return `None`, the authentication is denied. + ### `check_3pid_auth` ```python @@ -69,6 +77,11 @@ 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, the first callback returning a Matrix user +ID (and optionally a callback) will cause Synapse to consider this user ID, and the +subsequent implementations of this callback to be ignored. If all the callbacks return +`None`, the authentication is denied. + ### `on_logged_out` ```python @@ -82,6 +95,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: diff --git a/docs/modules/presence_router_callbacks.md b/docs/modules/presence_router_callbacks.md index 4abcc9af47b9..5b4986b0e81d 100644 --- a/docs/modules/presence_router_callbacks.md +++ b/docs/modules/presence_router_callbacks.md @@ -24,6 +24,9 @@ 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 considers the concatenation of all +the dicts returned by the callbacks. + ### `get_interested_users` ```python @@ -44,6 +47,11 @@ 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, the first callback returning +`synapse.module_api.PRESENCE_ALL_USERS` causes Synapse to consider all known users, and +the subsequent implementations of this callback to be ignored. If all the callbacks +return a `set` then Synapse considers the concatenation of all the `set`s. + ## Example The example below is a module that implements both presence router callbacks, and ensures diff --git a/docs/modules/spam_checker_callbacks.md b/docs/modules/spam_checker_callbacks.md index 787e99074af2..3868a0caf4c1 100644 --- a/docs/modules/spam_checker_callbacks.md +++ b/docs/modules/spam_checker_callbacks.md @@ -19,6 +19,10 @@ 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, the first callback returning either `True` +or a `str` causes the event to be denied, and the subsequent implementations of this +callback to be ignored. If all callbacks return `False` then the event is allowed. + ### `user_may_join_room` ```python @@ -34,6 +38,10 @@ 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, the first callback returning `False` causes +the room join to be denied, and the subsequent implementations of this callback to be +ignored. If all callbacks return `True` then the room join is allowed. + ### `user_may_invite` ```python @@ -44,6 +52,10 @@ 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, the first callback returning `False` causes +the invite to be denied, and the subsequent implementations of this callback to be +ignored. If all callbacks return `True` then the invite is allowed. + ### `user_may_send_3pid_invite` ```python @@ -79,6 +91,10 @@ 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, the first callback returning `False` causes +the invite to be denied, and the subsequent implementations of this callback to be +ignored. If all callbacks return `True` then the invite is allowed. + ### `user_may_create_room` ```python @@ -88,6 +104,10 @@ 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, the first callback returning `False` causes +the room creation to be denied, and the subsequent implementations of this callback to be +ignored. If all callbacks return `True` then the room creation is allowed. + ### `user_may_create_room_with_invites` ```python @@ -117,6 +137,10 @@ 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, the first callback returning `False` causes +the room creation to be denied, and the subsequent implementations of this callback to be +ignored. If all callbacks return `True` then the room creation is allowed. + ### `user_may_create_room_alias` ```python @@ -127,6 +151,10 @@ 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, the first callback returning `False` causes +the room alias to be denied, and the subsequent implementations of this callback to be +ignored. If all callbacks return `True` then the room alias is allowed. + ### `user_may_publish_room` ```python @@ -137,6 +165,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, the first callback returning `False` causes +the publication of the room to be denied, and the subsequent implementations of this +callback to be ignored. If all callbacks return `True` then the publication of the room +is allowed. + ### `check_username_for_spam` ```python @@ -154,6 +187,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, the first callback returning `True` causes +the username to be excluded from user search, and the subsequent implementations of this +callback to be ignored. If all callbacks return `False` then the username is included in +the user search results. + ### `check_registration_for_spam` ```python @@ -179,6 +217,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, the first callback returning a +`RegistrationBehaviour` different than `RegistrationBehaviour.ALLOW` causes the +registration to be denied, and the subsequent implementations of this callback to be +ignored. If all callbacks return `RegistrationBehaviour.ALLOW` then the registration is +allowed. + ### `check_media_file_for_spam` ```python @@ -191,6 +235,10 @@ 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, the first callback returning `True` causes +the file to be denied, and the subsequent implementations of this callback to be ignored. +If all callbacks return `False` then the file is allowed. + ## Example The example below is a module that implements the spam checker callback diff --git a/docs/modules/third_party_rules_callbacks.md b/docs/modules/third_party_rules_callbacks.md index 2ba6f3945303..b9540a79e0c4 100644 --- a/docs/modules/third_party_rules_callbacks.md +++ b/docs/modules/third_party_rules_callbacks.md @@ -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, the first callback returning `False`, or +`True` but with a replacement dictionary for the event, causes the event to be denied, +and the subsequent implementations of this callback to be ignored. If all callbacks +return `True` with no replacement content then the event is allowed. + ### `on_create_room` ```python @@ -63,6 +68,11 @@ 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, the first callback raising an exception +causes the room creation to be denied, and the subsequent implementations of this callback +to be ignored. If none of the callbacks raise an exception then the room creation is +allowed. + ### `check_threepid_can_be_invited` ```python @@ -76,6 +86,10 @@ 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, the first callback returning `False` causes +the invite to be denied, and the subsequent implementations of this callback to be +ignored. If all callbacks return `True` then the invite is allowed. + ### `check_visibility_can_be_modified` ```python @@ -90,6 +104,10 @@ 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, the first callback returning `False` causes +the visibility change to be denied, and the subsequent implementations of this callback +to be ignored. If all callbacks return `True` then the visibility change is allowed. + ## Example The example below is a module that implements the third-party rules callback diff --git a/docs/modules/writing_a_module.md b/docs/modules/writing_a_module.md index 4f2fec8dc9fa..b36b50143cc1 100644 --- a/docs/modules/writing_a_module.md +++ b/docs/modules/writing_a_module.md @@ -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 Synapse's behaviour if multiple modules +implement it. + ## Handling the module's configuration A module can implement the following static method: From 816844853387ab34d1aaea0a16dbb949d646eaca Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Fri, 15 Oct 2021 17:26:44 +0100 Subject: [PATCH 2/5] Changelog --- changelog.d/11096.doc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/11096.doc diff --git a/changelog.d/11096.doc b/changelog.d/11096.doc new file mode 100644 index 000000000000..d8e742428976 --- /dev/null +++ b/changelog.d/11096.doc @@ -0,0 +1 @@ +Document Synapse's behaviour when dealing with multiple modules registering the same callbacks and/or handlers for the same HTTP endpoints. From c9b76625ec7a7fabe7237a1474ea17013f31dd77 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 18 Oct 2021 16:09:04 +0100 Subject: [PATCH 3/5] Incorporate review --- docs/modules/account_validity_callbacks.md | 8 +- .../password_auth_provider_callbacks.md | 18 ++-- docs/modules/presence_router_callbacks.md | 14 ++-- docs/modules/spam_checker_callbacks.md | 82 ++++++++++--------- docs/modules/third_party_rules_callbacks.md | 31 +++---- 5 files changed, 84 insertions(+), 69 deletions(-) diff --git a/docs/modules/account_validity_callbacks.md b/docs/modules/account_validity_callbacks.md index 54bd9919c0df..836bda70bf60 100644 --- a/docs/modules/account_validity_callbacks.md +++ b/docs/modules/account_validity_callbacks.md @@ -22,10 +22,10 @@ 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, the first callback returning `bool` (i.e. -not `None`) causes the returned value to be used to determine whether the user is expired, -and the subsequent implementations of this callback to be ignored. If all callbacks return -`None`, the user is considered not expired. +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` diff --git a/docs/modules/password_auth_provider_callbacks.md b/docs/modules/password_auth_provider_callbacks.md index f9e7c63d870b..bb921def886e 100644 --- a/docs/modules/password_auth_provider_callbacks.md +++ b/docs/modules/password_auth_provider_callbacks.md @@ -48,9 +48,10 @@ If multiple modules register an auth checker for the same login type but with di fields, Synapse will refuse to start. If multiple modules register an auth checker for the same login type with the same fields, -the first callback returning a Matrix user ID (and optionally a callback) will cause -Synapse to consider this user ID, and the subsequent implementations of this callback to -be ignored. If all the callbacks return `None`, the authentication is denied. +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` @@ -75,12 +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, the first callback returning a Matrix user -ID (and optionally a callback) will cause Synapse to consider this user ID, and the -subsequent implementations of this callback to be ignored. If all the callbacks return -`None`, the authentication is denied. +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` diff --git a/docs/modules/presence_router_callbacks.md b/docs/modules/presence_router_callbacks.md index 5b4986b0e81d..349e185bd6a6 100644 --- a/docs/modules/presence_router_callbacks.md +++ b/docs/modules/presence_router_callbacks.md @@ -24,8 +24,9 @@ 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 considers the concatenation of all -the dicts returned by the callbacks. +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` @@ -47,10 +48,11 @@ 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, the first callback returning -`synapse.module_api.PRESENCE_ALL_USERS` causes Synapse to consider all known users, and -the subsequent implementations of this callback to be ignored. If all the callbacks -return a `set` then Synapse considers the concatenation of all the `set`s. +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 diff --git a/docs/modules/spam_checker_callbacks.md b/docs/modules/spam_checker_callbacks.md index 3868a0caf4c1..7d954cbe948a 100644 --- a/docs/modules/spam_checker_callbacks.md +++ b/docs/modules/spam_checker_callbacks.md @@ -19,9 +19,10 @@ 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, the first callback returning either `True` -or a `str` causes the event to be denied, and the subsequent implementations of this -callback to be ignored. If all callbacks return `False` then the event is allowed. +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` @@ -38,9 +39,10 @@ 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, the first callback returning `False` causes -the room join to be denied, and the subsequent implementations of this callback to be -ignored. If all callbacks return `True` then the room join is allowed. +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` @@ -52,9 +54,10 @@ 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, the first callback returning `False` causes -the invite to be denied, and the subsequent implementations of this callback to be -ignored. If all callbacks return `True` then the invite is allowed. +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` @@ -91,9 +94,10 @@ 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, the first callback returning `False` causes -the invite to be denied, and the subsequent implementations of this callback to be -ignored. If all callbacks return `True` then the invite is allowed. +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` @@ -104,9 +108,10 @@ 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, the first callback returning `False` causes -the room creation to be denied, and the subsequent implementations of this callback to be -ignored. If all callbacks return `True` then the room creation is allowed. +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` @@ -137,9 +142,10 @@ 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, the first callback returning `False` causes -the room creation to be denied, and the subsequent implementations of this callback to be -ignored. If all callbacks return `True` then the room creation is allowed. +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` @@ -151,9 +157,10 @@ 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, the first callback returning `False` causes -the room alias to be denied, and the subsequent implementations of this callback to be -ignored. If all callbacks return `True` then the room alias is allowed. +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` @@ -165,10 +172,10 @@ 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, the first callback returning `False` causes -the publication of the room to be denied, and the subsequent implementations of this -callback to be ignored. If all callbacks return `True` then the publication of the room -is allowed. +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` @@ -187,10 +194,10 @@ 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, the first callback returning `True` causes -the username to be excluded from user search, and the subsequent implementations of this -callback to be ignored. If all callbacks return `False` then the username is included in -the user 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` @@ -217,11 +224,11 @@ 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, the first callback returning a -`RegistrationBehaviour` different than `RegistrationBehaviour.ALLOW` causes the -registration to be denied, and the subsequent implementations of this callback to be -ignored. If all callbacks return `RegistrationBehaviour.ALLOW` then the registration is -allowed. +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` @@ -235,9 +242,10 @@ 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, the first callback returning `True` causes -the file to be denied, and the subsequent implementations of this callback to be ignored. -If all callbacks return `False` then the file is allowed. +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 diff --git a/docs/modules/third_party_rules_callbacks.md b/docs/modules/third_party_rules_callbacks.md index b9540a79e0c4..5371e7f80707 100644 --- a/docs/modules/third_party_rules_callbacks.md +++ b/docs/modules/third_party_rules_callbacks.md @@ -44,10 +44,10 @@ 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, the first callback returning `False`, or -`True` but with a replacement dictionary for the event, causes the event to be denied, -and the subsequent implementations of this callback to be ignored. If all callbacks -return `True` with no replacement content then the event is allowed. +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` @@ -68,10 +68,11 @@ 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, the first callback raising an exception -causes the room creation to be denied, and the subsequent implementations of this callback -to be ignored. If none of the callbacks raise an exception then the room creation is -allowed. +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` @@ -86,9 +87,10 @@ 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, the first callback returning `False` causes -the invite to be denied, and the subsequent implementations of this callback to be -ignored. If all callbacks return `True` then the invite is allowed. +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` @@ -104,9 +106,10 @@ 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, the first callback returning `False` causes -the visibility change to be denied, and the subsequent implementations of this callback -to be ignored. If all callbacks return `True` then the visibility change is allowed. +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 From 6e0b2105072f3679432b4b85ede538f5965ebd94 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 18 Oct 2021 17:09:58 +0200 Subject: [PATCH 4/5] Update docs/modules/index.md Co-authored-by: reivilibre --- docs/modules/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/index.md b/docs/modules/index.md index 967981a10c2e..0a868b309f2f 100644 --- a/docs/modules/index.md +++ b/docs/modules/index.md @@ -31,7 +31,7 @@ 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 register a handler for the same HTTP path, only the handler +* 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. From 84304b8a4bc46d7c8e1ec06d955d06258ba7c09e Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 18 Oct 2021 17:10:05 +0200 Subject: [PATCH 5/5] Update docs/modules/writing_a_module.md Co-authored-by: reivilibre --- docs/modules/writing_a_module.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/writing_a_module.md b/docs/modules/writing_a_module.md index b36b50143cc1..7764e066926b 100644 --- a/docs/modules/writing_a_module.md +++ b/docs/modules/writing_a_module.md @@ -24,8 +24,8 @@ cause the current operation to fail (e.g. if a callback checking an event return 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 Synapse's behaviour if multiple modules -implement it. +The documentation for each callback mentions how Synapse behaves when +multiple modules implement it. ## Handling the module's configuration