summary refs log tree commit diff
path: root/synapse
diff options
context:
space:
mode:
authorMathieu Velten <matmaul@gmail.com>2022-07-08 16:46:23 +0200
committerMathieu Velten <mathieuv@matrix.org>2022-08-03 14:52:20 +0200
commit0274a7f2f52dc108d5ae6815fb7dcc2af208dc3c (patch)
treead5e7af83959a2452f7e00ffe333ef6ec0c4badd /synapse
parentReturn 404 or member list when getting joined_members after leaving (#13374) (diff)
downloadsynapse-0274a7f2f52dc108d5ae6815fb7dcc2af208dc3c.tar.xz
Add 3pid unbind callback to module API
Diffstat (limited to 'synapse')
-rw-r--r--synapse/events/third_party_rules.py47
-rw-r--r--synapse/handlers/identity.py94
-rw-r--r--synapse/module_api/__init__.py3
3 files changed, 106 insertions, 38 deletions
diff --git a/synapse/events/third_party_rules.py b/synapse/events/third_party_rules.py

index 72ab696898..490acd657a 100644 --- a/synapse/events/third_party_rules.py +++ b/synapse/events/third_party_rules.py
@@ -45,6 +45,9 @@ CHECK_CAN_DEACTIVATE_USER_CALLBACK = Callable[[str, bool], Awaitable[bool]] ON_PROFILE_UPDATE_CALLBACK = Callable[[str, ProfileInfo, bool, bool], Awaitable] ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK = Callable[[str, bool, bool], Awaitable] ON_THREEPID_BIND_CALLBACK = Callable[[str, str, str], Awaitable] +ON_THREEPID_UNBIND_CALLBACK = Callable[ + [str, str, str, str], Awaitable[Tuple[bool, bool]] +] def load_legacy_third_party_event_rules(hs: "HomeServer") -> None: @@ -174,6 +177,7 @@ class ThirdPartyEventRules: ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK ] = [] self._on_threepid_bind_callbacks: List[ON_THREEPID_BIND_CALLBACK] = [] + self._on_threepid_unbind_callbacks: List[ON_THREEPID_UNBIND_CALLBACK] = [] def register_third_party_rules_callbacks( self, @@ -193,6 +197,7 @@ class ThirdPartyEventRules: ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK ] = None, on_threepid_bind: Optional[ON_THREEPID_BIND_CALLBACK] = None, + on_threepid_unbind: Optional[ON_THREEPID_UNBIND_CALLBACK] = None, ) -> None: """Register callbacks from modules for each hook.""" if check_event_allowed is not None: @@ -230,6 +235,9 @@ class ThirdPartyEventRules: if on_threepid_bind is not None: self._on_threepid_bind_callbacks.append(on_threepid_bind) + if on_threepid_unbind is not None: + self._on_threepid_unbind_callbacks.append(on_threepid_unbind) + async def check_event_allowed( self, event: EventBase, context: EventContext ) -> Tuple[bool, Optional[dict]]: @@ -523,3 +531,42 @@ class ThirdPartyEventRules: logger.exception( "Failed to run module API callback %s: %s", callback, e ) + + async def on_threepid_unbind( + self, user_id: str, medium: str, address: str, identity_server: str + ) -> Tuple[bool, bool]: + """Called before a threepid association is removed. + + Note that this callback is called before an association is deleted on the + local homeserver. + + Args: + user_id: the user being associated with the threepid. + medium: the threepid's medium. + address: the threepid's address. + identity_server: the identity server where the threepid was successfully registered. + + Returns: + A tuple of 2 booleans reporting if a changed happened for the first, and if unbind + needs to stop there for the second (True value). In this case no other module unbind will be + called, and the default unbind made to the IS that was used on bind will also be skipped. + In any case the mapping will be removed from the Synapse 3pid remote table, except if an Exception + was raised at some point. + """ + + global_changed = True + for callback in self._on_threepid_unbind_callbacks: + try: + (changed, stop) = await callback( + user_id, medium, address, identity_server + ) + global_changed &= changed + if stop: + return (global_changed, True) + except Exception as e: + logger.exception( + "Failed to run module API callback %s: %s", callback, e + ) + raise e + + return (global_changed, False) diff --git a/synapse/handlers/identity.py b/synapse/handlers/identity.py
index e5afe84df9..61b9622697 100644 --- a/synapse/handlers/identity.py +++ b/synapse/handlers/identity.py
@@ -276,49 +276,67 @@ class IdentityHandler: server doesn't support unbinding """ - if not valid_id_server_location(id_server): - raise SynapseError( - 400, - "id_server must be a valid hostname with optional port and path components", - ) + medium = threepid["medium"] + address = threepid["address"] + + ( + changed, + stop, + ) = await self.hs.get_third_party_event_rules().on_threepid_unbind( + mxid, medium, address, id_server + ) - url = "https://%s/_matrix/identity/v2/3pid/unbind" % (id_server,) - url_bytes = b"/_matrix/identity/v2/3pid/unbind" + # If a module wants to take over unbind it will return stop = True, + # in this case we should just purge the table from the 3pid record + if not stop: + if not valid_id_server_location(id_server): + raise SynapseError( + 400, + "id_server must be a valid hostname with optional port and path components", + ) - content = { - "mxid": mxid, - "threepid": {"medium": threepid["medium"], "address": threepid["address"]}, - } + url = "https://%s/_matrix/identity/v2/3pid/unbind" % (id_server,) + url_bytes = b"/_matrix/identity/v2/3pid/unbind" - # we abuse the federation http client to sign the request, but we have to send it - # using the normal http client since we don't want the SRV lookup and want normal - # 'browser-like' HTTPS. - auth_headers = self.federation_http_client.build_auth_headers( - destination=None, - method=b"POST", - url_bytes=url_bytes, - content=content, - destination_is=id_server.encode("ascii"), - ) - headers = {b"Authorization": auth_headers} + content = { + "mxid": mxid, + "threepid": { + "medium": threepid["medium"], + "address": threepid["address"], + }, + } - try: - # Use the blacklisting http client as this call is only to identity servers - # provided by a client - await self.blacklisting_http_client.post_json_get_json( - url, content, headers + # we abuse the federation http client to sign the request, but we have to send it + # using the normal http client since we don't want the SRV lookup and want normal + # 'browser-like' HTTPS. + auth_headers = self.federation_http_client.build_auth_headers( + destination=None, + method=b"POST", + url_bytes=url_bytes, + content=content, + destination_is=id_server.encode("ascii"), ) - changed = True - except HttpResponseException as e: - changed = False - if e.code in (400, 404, 501): - # The remote server probably doesn't support unbinding (yet) - logger.warning("Received %d response while unbinding threepid", e.code) - else: - logger.error("Failed to unbind threepid on identity server: %s", e) - raise SynapseError(500, "Failed to contact identity server") - except RequestTimedOutError: - raise SynapseError(500, "Timed out contacting identity server") + headers = {b"Authorization": auth_headers} + + try: + # Use the blacklisting http client as this call is only to identity servers + # provided by a client + await self.blacklisting_http_client.post_json_get_json( + url, content, headers + ) + changed &= True + except HttpResponseException as e: + changed &= False + if e.code in (400, 404, 501): + # The remote server probably doesn't support unbinding (yet) + logger.warning( + "Received %d response while unbinding threepid", e.code + ) + else: + logger.error("Failed to unbind threepid on identity server: %s", e) + raise SynapseError(500, "Failed to contact identity server") + except RequestTimedOutError: + raise SynapseError(500, "Timed out contacting identity server") await self.store.remove_user_bound_threepid( user_id=mxid, diff --git a/synapse/module_api/__init__.py b/synapse/module_api/__init__.py
index 18d6d1058a..778608280f 100644 --- a/synapse/module_api/__init__.py +++ b/synapse/module_api/__init__.py
@@ -67,6 +67,7 @@ from synapse.events.third_party_rules import ( ON_NEW_EVENT_CALLBACK, ON_PROFILE_UPDATE_CALLBACK, ON_THREEPID_BIND_CALLBACK, + ON_THREEPID_UNBIND_CALLBACK, ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK, ) from synapse.handlers.account_data import ON_ACCOUNT_DATA_UPDATED_CALLBACK @@ -317,6 +318,7 @@ class ModuleApi: ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK ] = None, on_threepid_bind: Optional[ON_THREEPID_BIND_CALLBACK] = None, + on_threepid_unbind: Optional[ON_THREEPID_UNBIND_CALLBACK] = None, ) -> None: """Registers callbacks for third party event rules capabilities. @@ -333,6 +335,7 @@ class ModuleApi: on_profile_update=on_profile_update, on_user_deactivation_status_changed=on_user_deactivation_status_changed, on_threepid_bind=on_threepid_bind, + on_threepid_unbind=on_threepid_unbind, ) def register_presence_router_callbacks(