From 94ae672d3ace3dc2e9e232ea8e85d5e4b2d9ac62 Mon Sep 17 00:00:00 2001 From: yasinishyn Date: Sat, 4 Mar 2023 11:36:55 +0200 Subject: [PATCH 1/9] ModuleAPI SSO auth callbacks --- docs/modules/account_validity_callbacks.md | 13 +++++++++++++ synapse/handlers/account_validity.py | 15 +++++++++++++++ synapse/handlers/auth.py | 4 ++++ synapse/module_api/__init__.py | 3 +++ 4 files changed, 35 insertions(+) diff --git a/docs/modules/account_validity_callbacks.md b/docs/modules/account_validity_callbacks.md index 3cd0e7219894..e7b93e604e47 100644 --- a/docs/modules/account_validity_callbacks.md +++ b/docs/modules/account_validity_callbacks.md @@ -42,3 +42,16 @@ operations to keep track of them. (e.g. add them to a database table). The user represented by their Matrix user ID. If multiple modules implement this callback, Synapse runs them all in order. + +### `on_sso_login` + +_First introduced in Synapse < TODO > + +```python +async def on_sso_login(user: str) -> None +``` + +Called after successfully login or registration of a user using SSO for cases when module needs to perform extra operations after SSO auth. +represented by their Matrix user ID. + +If multiple modules implement this callback, Synapse runs them all in order. diff --git a/synapse/handlers/account_validity.py b/synapse/handlers/account_validity.py index 33e45e3a1136..0e1496e967fc 100644 --- a/synapse/handlers/account_validity.py +++ b/synapse/handlers/account_validity.py @@ -33,6 +33,7 @@ # Types for callbacks to be registered via the module api IS_USER_EXPIRED_CALLBACK = Callable[[str], Awaitable[Optional[bool]]] ON_USER_REGISTRATION_CALLBACK = Callable[[str], Awaitable] +ON_SSO_LOGIN_CALLBACK = Callable[[str], Awaitable] # Temporary hooks to allow for a transition from `/_matrix/client` endpoints # to `/_synapse/client/account_validity`. See `register_account_validity_callbacks`. ON_LEGACY_SEND_MAIL_CALLBACK = Callable[[str], Awaitable] @@ -80,6 +81,7 @@ def __init__(self, hs: "HomeServer"): self._is_user_expired_callbacks: List[IS_USER_EXPIRED_CALLBACK] = [] self._on_user_registration_callbacks: List[ON_USER_REGISTRATION_CALLBACK] = [] + self._on_sso_login_callbacks: List[ON_SSO_LOGIN_CALLBACK] = [] self._on_legacy_send_mail_callback: Optional[ ON_LEGACY_SEND_MAIL_CALLBACK ] = None @@ -93,6 +95,7 @@ def register_account_validity_callbacks( self, is_user_expired: Optional[IS_USER_EXPIRED_CALLBACK] = None, on_user_registration: Optional[ON_USER_REGISTRATION_CALLBACK] = None, + on_sso_login: Optional[ON_SSO_LOGIN_CALLBACK] = None, on_legacy_send_mail: Optional[ON_LEGACY_SEND_MAIL_CALLBACK] = None, on_legacy_renew: Optional[ON_LEGACY_RENEW_CALLBACK] = None, on_legacy_admin_request: Optional[ON_LEGACY_ADMIN_REQUEST] = None, @@ -103,6 +106,9 @@ def register_account_validity_callbacks( if on_user_registration is not None: self._on_user_registration_callbacks.append(on_user_registration) + + if on_sso_login is not None: + self._on_sso_login_callbacks.append(on_sso_login) # The builtin account validity feature exposes 3 endpoints (send_mail, renew, and # an admin one). As part of moving the feature into a module, we need to change @@ -171,6 +177,15 @@ async def on_user_registration(self, user_id: str) -> None: for callback in self._on_user_registration_callbacks: await callback(user_id) + async def on_sso_login(self, user_id: str) -> None: + """Tell third-party modules about a user's SSO logins. + + Args: + user_id: The mxID of the user. + """ + for callback in self._on_sso_login_callbacks: + await callback(user_id) + @wrap_as_background_process("send_renewals") async def _send_renewal_emails(self) -> None: """Gets the list of users whose account is expiring in the amount of time diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py index 308e38edea2d..f90755523ae3 100644 --- a/synapse/handlers/auth.py +++ b/synapse/handlers/auth.py @@ -213,6 +213,7 @@ def __init__(self, hs: "HomeServer"): self._password_enabled_for_reauth = hs.config.auth.password_enabled_for_reauth self._password_localdb_enabled = hs.config.auth.password_localdb_enabled self._third_party_rules = hs.get_third_party_event_rules() + self._account_validity_handler = hs.get_account_validity_handler() # Ratelimiter for failed auth during UIA. Uses same ratelimit config # as per `rc_login.failed_attempts`. @@ -1779,6 +1780,9 @@ async def complete_sso_login( client_redirect_url, "loginToken", login_token ) + # Run post-login module callback handlers + await self._account_validity_handler.on_sso_login(registered_user_id) + # if the client is whitelisted, we can redirect straight to it if client_redirect_url.startswith(self._whitelisted_sso_clients): request.redirect(redirect_url) diff --git a/synapse/module_api/__init__.py b/synapse/module_api/__init__.py index 424239e3df86..9be6890fcfa8 100644 --- a/synapse/module_api/__init__.py +++ b/synapse/module_api/__init__.py @@ -79,6 +79,7 @@ ON_LEGACY_RENEW_CALLBACK, ON_LEGACY_SEND_MAIL_CALLBACK, ON_USER_REGISTRATION_CALLBACK, + ON_SSO_LOGIN_CALLBACK, ) from synapse.handlers.auth import ( CHECK_3PID_AUTH_CALLBACK, @@ -324,6 +325,7 @@ def register_account_validity_callbacks( *, is_user_expired: Optional[IS_USER_EXPIRED_CALLBACK] = None, on_user_registration: Optional[ON_USER_REGISTRATION_CALLBACK] = None, + on_sso_login: Optional[ON_SSO_LOGIN_CALLBACK] = None, on_legacy_send_mail: Optional[ON_LEGACY_SEND_MAIL_CALLBACK] = None, on_legacy_renew: Optional[ON_LEGACY_RENEW_CALLBACK] = None, on_legacy_admin_request: Optional[ON_LEGACY_ADMIN_REQUEST] = None, @@ -335,6 +337,7 @@ def register_account_validity_callbacks( return self._account_validity_handler.register_account_validity_callbacks( is_user_expired=is_user_expired, on_user_registration=on_user_registration, + on_sso_login=on_sso_login, on_legacy_send_mail=on_legacy_send_mail, on_legacy_renew=on_legacy_renew, on_legacy_admin_request=on_legacy_admin_request, From 92d0e693fc9921c612db88a748c307558c0ef5a8 Mon Sep 17 00:00:00 2001 From: yasinishyn Date: Sat, 4 Mar 2023 19:18:12 +0200 Subject: [PATCH 2/9] changelog --- changelog.d/15192.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/15192.feature diff --git a/changelog.d/15192.feature b/changelog.d/15192.feature new file mode 100644 index 000000000000..fb6ccb140338 --- /dev/null +++ b/changelog.d/15192.feature @@ -0,0 +1 @@ +Adds on_sso_login ModuleAPI callback allowing to execute custom code on SSO Auth \ No newline at end of file From bc6044bbfd7352b9379c3e292083d2ab5e1b610b Mon Sep 17 00:00:00 2001 From: yasinishyn Date: Wed, 8 Mar 2023 07:22:43 +0200 Subject: [PATCH 3/9] fixed lint --- synapse/handlers/account_validity.py | 2 +- synapse/module_api/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/synapse/handlers/account_validity.py b/synapse/handlers/account_validity.py index 0e1496e967fc..9116d0af1cff 100644 --- a/synapse/handlers/account_validity.py +++ b/synapse/handlers/account_validity.py @@ -106,7 +106,7 @@ def register_account_validity_callbacks( if on_user_registration is not None: self._on_user_registration_callbacks.append(on_user_registration) - + if on_sso_login is not None: self._on_sso_login_callbacks.append(on_sso_login) diff --git a/synapse/module_api/__init__.py b/synapse/module_api/__init__.py index 9be6890fcfa8..8f08341e7e73 100644 --- a/synapse/module_api/__init__.py +++ b/synapse/module_api/__init__.py @@ -78,8 +78,8 @@ ON_LEGACY_ADMIN_REQUEST, ON_LEGACY_RENEW_CALLBACK, ON_LEGACY_SEND_MAIL_CALLBACK, - ON_USER_REGISTRATION_CALLBACK, ON_SSO_LOGIN_CALLBACK, + ON_USER_REGISTRATION_CALLBACK, ) from synapse.handlers.auth import ( CHECK_3PID_AUTH_CALLBACK, From cd9c1a4d8df1ae20152f8d623cf8389047bfef10 Mon Sep 17 00:00:00 2001 From: yasinishyn Date: Wed, 8 Mar 2023 22:24:50 +0200 Subject: [PATCH 4/9] changelog lint fix --- changelog.d/15192.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/15192.feature b/changelog.d/15192.feature index fb6ccb140338..f722322cfa78 100644 --- a/changelog.d/15192.feature +++ b/changelog.d/15192.feature @@ -1 +1 @@ -Adds on_sso_login ModuleAPI callback allowing to execute custom code on SSO Auth \ No newline at end of file +Adds on_sso_login ModuleAPI callback allowing to execute custom code on SSO Auth. \ No newline at end of file From cdbb0bc53266eb8bdd8d92bc15144b677f03e8c2 Mon Sep 17 00:00:00 2001 From: yasinishyn Date: Wed, 8 Mar 2023 22:45:54 +0200 Subject: [PATCH 5/9] renamed changelog fragment file --- changelog.d/{15192.feature => 15207.feature} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{15192.feature => 15207.feature} (100%) diff --git a/changelog.d/15192.feature b/changelog.d/15207.feature similarity index 100% rename from changelog.d/15192.feature rename to changelog.d/15207.feature From b0399e9561face2b984b7041c506257c8f5dfad8 Mon Sep 17 00:00:00 2001 From: yasinishyn Date: Mon, 16 Oct 2023 18:28:10 +0300 Subject: [PATCH 6/9] get_account_validity_handler assingment --- docs/modules/account_validity_callbacks.md | 2 +- synapse/handlers/auth.py | 2 +- synapse/rest/client/login.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/modules/account_validity_callbacks.md b/docs/modules/account_validity_callbacks.md index c262dd42363c..28e9e6417440 100644 --- a/docs/modules/account_validity_callbacks.md +++ b/docs/modules/account_validity_callbacks.md @@ -48,7 +48,7 @@ If multiple modules implement this callback, Synapse runs them all in order. _First introduced in Synapse < TODO > ```python -async def on_sson_user_logino_login(user_id: str, auth_provider_type: LoginType, auth_provider_id: str) -> None +async def on_user_login(user_id: str, auth_provider_type: LoginType, 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. diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py index 46ae92b3c419..89cbaff864d4 100644 --- a/synapse/handlers/auth.py +++ b/synapse/handlers/auth.py @@ -212,7 +212,7 @@ def __init__(self, hs: "HomeServer"): self._password_enabled_for_reauth = hs.config.auth.password_enabled_for_reauth self._password_localdb_enabled = hs.config.auth.password_localdb_enabled self._third_party_rules = hs.get_module_api_callbacks().third_party_event_rules - self._account_validity_handler = hs.get_module_api_callbacks().account_validity + self._account_validity_handler = hs.get_account_validity_handler() # Ratelimiter for failed auth during UIA. Uses same ratelimit config # as per `rc_login.failed_attempts`. diff --git a/synapse/rest/client/login.py b/synapse/rest/client/login.py index 3f9247280490..546f042f87c5 100644 --- a/synapse/rest/client/login.py +++ b/synapse/rest/client/login.py @@ -115,7 +115,7 @@ def __init__(self, hs: "HomeServer"): self.registration_handler = hs.get_registration_handler() self._sso_handler = hs.get_sso_handler() self._spam_checker = hs.get_module_api_callbacks().spam_checker - self._account_validity_handler = hs.get_module_api_callbacks().account_validity + self._account_validity_handler = hs.get_account_validity_handler() self._well_known_builder = WellKnownBuilder(hs) self._address_ratelimiter = Ratelimiter( From 9f81d74c76253372302e4ca64efc6dcf661a3912 Mon Sep 17 00:00:00 2001 From: yasinishyn Date: Wed, 22 Nov 2023 10:30:46 +0200 Subject: [PATCH 7/9] linters fix --- docs/modules/account_validity_callbacks.md | 2 +- synapse/handlers/account_validity.py | 3 +-- synapse/module_api/callbacks/account_validity_callbacks.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/modules/account_validity_callbacks.md b/docs/modules/account_validity_callbacks.md index 28e9e6417440..87796f8f3787 100644 --- a/docs/modules/account_validity_callbacks.md +++ b/docs/modules/account_validity_callbacks.md @@ -48,7 +48,7 @@ If multiple modules implement this callback, Synapse runs them all in order. _First introduced in Synapse < TODO > ```python -async def on_user_login(user_id: str, auth_provider_type: LoginType, auth_provider_id: str) -> None +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. diff --git a/synapse/handlers/account_validity.py b/synapse/handlers/account_validity.py index 0d58433c6b63..73e0d4608bf7 100644 --- a/synapse/handlers/account_validity.py +++ b/synapse/handlers/account_validity.py @@ -17,7 +17,6 @@ import logging from typing import TYPE_CHECKING, List, Optional, Tuple -from synapse.api.constants import LoginType from synapse.api.errors import AuthError, StoreError, SynapseError from synapse.metrics.background_process_metrics import wrap_as_background_process from synapse.types import UserID @@ -100,7 +99,7 @@ async def on_user_registration(self, user_id: str) -> None: await callback(user_id) async def on_user_login( - self, user_id: str, auth_provider_type: LoginType, auth_provider_id: str + self, user_id: str, auth_provider_type: Optional[str], auth_provider_id: Optional[str] ) -> None: """Tell third-party modules about a user logins. diff --git a/synapse/module_api/callbacks/account_validity_callbacks.py b/synapse/module_api/callbacks/account_validity_callbacks.py index 3c59cdf2c7f3..cbfdfbd3d186 100644 --- a/synapse/module_api/callbacks/account_validity_callbacks.py +++ b/synapse/module_api/callbacks/account_validity_callbacks.py @@ -22,7 +22,7 @@ # Types for callbacks to be registered via the module api IS_USER_EXPIRED_CALLBACK = Callable[[str], Awaitable[Optional[bool]]] ON_USER_REGISTRATION_CALLBACK = Callable[[str], Awaitable] -ON_USER_LOGIN_CALLBACK = Callable[[str], Awaitable] +ON_USER_LOGIN_CALLBACK = Callable[[str, Optional[str], Optional[str]], Awaitable] # Temporary hooks to allow for a transition from `/_matrix/client` endpoints # to `/_synapse/client/account_validity`. See `register_callbacks` below. ON_LEGACY_SEND_MAIL_CALLBACK = Callable[[str], Awaitable] From df191a1ef8667b957ff98192d4ac224d37b880b3 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 29 Nov 2023 10:35:05 +0000 Subject: [PATCH 8/9] Update version number --- docs/modules/account_validity_callbacks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/account_validity_callbacks.md b/docs/modules/account_validity_callbacks.md index 87796f8f3787..f5eefcd7d639 100644 --- a/docs/modules/account_validity_callbacks.md +++ b/docs/modules/account_validity_callbacks.md @@ -45,7 +45,7 @@ If multiple modules implement this callback, Synapse runs them all in order. ### `on_user_login` -_First introduced in Synapse < TODO > +_First introduced in Synapse v1.98.0_ ```python async def on_user_login(user_id: str, auth_provider_type: str, auth_provider_id: str) -> None From 20131b0589ca108247e8cad46a55fd48157874aa Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 1 Dec 2023 12:51:32 +0000 Subject: [PATCH 9/9] Fix lint --- synapse/handlers/account_validity.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/synapse/handlers/account_validity.py b/synapse/handlers/account_validity.py index 73e0d4608bf7..c66bb6364f08 100644 --- a/synapse/handlers/account_validity.py +++ b/synapse/handlers/account_validity.py @@ -99,7 +99,10 @@ async def on_user_registration(self, user_id: str) -> None: await callback(user_id) async def on_user_login( - self, user_id: str, auth_provider_type: Optional[str], auth_provider_id: Optional[str] + self, + user_id: str, + auth_provider_type: Optional[str], + auth_provider_id: Optional[str], ) -> None: """Tell third-party modules about a user logins.