Add module API callbacks for adding and deleting local 3PID associations (#15044
parent
4fc8875876
commit
b40657314e
|
@ -0,0 +1 @@
|
||||||
|
Add two new Third Party Rules module API callbacks: [`on_add_user_third_party_identifier`](https://matrix-org.github.io/synapse/v1.79/modules/third_party_rules_callbacks.html#on_add_user_third_party_identifier) and [`on_remove_user_third_party_identifier`](https://matrix-org.github.io/synapse/v1.79/modules/third_party_rules_callbacks.html#on_remove_user_third_party_identifier).
|
|
@ -254,6 +254,11 @@ If multiple modules implement this callback, Synapse runs them all in order.
|
||||||
|
|
||||||
_First introduced in Synapse v1.56.0_
|
_First introduced in Synapse v1.56.0_
|
||||||
|
|
||||||
|
**<span style="color:red">
|
||||||
|
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.
|
||||||
|
</span>**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def on_threepid_bind(user_id: str, medium: str, address: str) -> None:
|
async def on_threepid_bind(user_id: str, medium: str, address: str) -> None:
|
||||||
```
|
```
|
||||||
|
@ -268,6 +273,44 @@ server_.
|
||||||
|
|
||||||
If multiple modules implement this callback, Synapse runs them all in order.
|
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_
|
||||||
|
|
||||||
|
```python
|
||||||
|
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`](https://spec.matrix.org/v1.5/client-server-api/#post_matrixclientv3account3pidbind)).
|
||||||
|
|
||||||
|
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_
|
||||||
|
|
||||||
|
```python
|
||||||
|
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`](https://spec.matrix.org/v1.5/client-server-api/#post_matrixclientv3account3pidunbind)).
|
||||||
|
|
||||||
|
If multiple modules implement this callback, Synapse runs them all in order.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
The example below is a module that implements the third-party rules callback
|
The example below is a module that implements the third-party rules callback
|
||||||
|
|
|
@ -88,6 +88,30 @@ process, for example:
|
||||||
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Upgrading to v1.79.0
|
||||||
|
|
||||||
|
## The `on_threepid_bind` module callback method has been deprecated
|
||||||
|
|
||||||
|
Synapse v1.79.0 deprecates the
|
||||||
|
[`on_threepid_bind`](modules/third_party_rules_callbacks.md#on_threepid_bind)
|
||||||
|
"third-party rules" Synapse module callback method in favour of a new module method,
|
||||||
|
[`on_add_user_third_party_identifier`](modules/third_party_rules_callbacks.md#on_add_user_third_party_identifier).
|
||||||
|
`on_threepid_bind` will be removed in a future version of Synapse. You should check whether any Synapse
|
||||||
|
modules in use in your deployment are making use of `on_threepid_bind`, and update them where possible.
|
||||||
|
|
||||||
|
The arguments and functionality of the new method are the same.
|
||||||
|
|
||||||
|
The justification behind the name change is that the old method's name, `on_threepid_bind`, was
|
||||||
|
misleading. A user is considered to "bind" their third-party ID to their Matrix ID only if they
|
||||||
|
do so via an [identity server](https://spec.matrix.org/latest/identity-service-api/)
|
||||||
|
(so that users on other homeservers may find them). But this method was not called in that case -
|
||||||
|
it was only called when a user added a third-party identifier on the local homeserver.
|
||||||
|
|
||||||
|
Module developers may also be interested in the related
|
||||||
|
[`on_remove_user_third_party_identifier`](modules/third_party_rules_callbacks.md#on_remove_user_third_party_identifier)
|
||||||
|
module callback method that was also added in Synapse v1.79.0. This new method is called when a
|
||||||
|
user removes a third-party identifier from their account.
|
||||||
|
|
||||||
# Upgrading to v1.78.0
|
# Upgrading to v1.78.0
|
||||||
|
|
||||||
## Deprecate the `/_synapse/admin/v1/media/<server_name>/delete` admin API
|
## Deprecate the `/_synapse/admin/v1/media/<server_name>/delete` admin API
|
||||||
|
|
|
@ -45,6 +45,8 @@ CHECK_CAN_DEACTIVATE_USER_CALLBACK = Callable[[str, bool], Awaitable[bool]]
|
||||||
ON_PROFILE_UPDATE_CALLBACK = Callable[[str, ProfileInfo, bool, bool], Awaitable]
|
ON_PROFILE_UPDATE_CALLBACK = Callable[[str, ProfileInfo, bool, bool], Awaitable]
|
||||||
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK = Callable[[str, 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_BIND_CALLBACK = Callable[[str, str, str], Awaitable]
|
||||||
|
ON_ADD_USER_THIRD_PARTY_IDENTIFIER_CALLBACK = Callable[[str, str, str], Awaitable]
|
||||||
|
ON_REMOVE_USER_THIRD_PARTY_IDENTIFIER_CALLBACK = Callable[[str, str, str], Awaitable]
|
||||||
|
|
||||||
|
|
||||||
def load_legacy_third_party_event_rules(hs: "HomeServer") -> None:
|
def load_legacy_third_party_event_rules(hs: "HomeServer") -> None:
|
||||||
|
@ -172,6 +174,12 @@ class ThirdPartyEventRules:
|
||||||
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK
|
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK
|
||||||
] = []
|
] = []
|
||||||
self._on_threepid_bind_callbacks: List[ON_THREEPID_BIND_CALLBACK] = []
|
self._on_threepid_bind_callbacks: List[ON_THREEPID_BIND_CALLBACK] = []
|
||||||
|
self._on_add_user_third_party_identifier_callbacks: List[
|
||||||
|
ON_ADD_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
|
||||||
|
] = []
|
||||||
|
self._on_remove_user_third_party_identifier_callbacks: List[
|
||||||
|
ON_REMOVE_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
|
||||||
|
] = []
|
||||||
|
|
||||||
def register_third_party_rules_callbacks(
|
def register_third_party_rules_callbacks(
|
||||||
self,
|
self,
|
||||||
|
@ -191,6 +199,12 @@ class ThirdPartyEventRules:
|
||||||
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK
|
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK
|
||||||
] = None,
|
] = None,
|
||||||
on_threepid_bind: Optional[ON_THREEPID_BIND_CALLBACK] = None,
|
on_threepid_bind: Optional[ON_THREEPID_BIND_CALLBACK] = None,
|
||||||
|
on_add_user_third_party_identifier: Optional[
|
||||||
|
ON_ADD_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
|
||||||
|
] = None,
|
||||||
|
on_remove_user_third_party_identifier: Optional[
|
||||||
|
ON_REMOVE_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
|
||||||
|
] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Register callbacks from modules for each hook."""
|
"""Register callbacks from modules for each hook."""
|
||||||
if check_event_allowed is not None:
|
if check_event_allowed is not None:
|
||||||
|
@ -228,6 +242,11 @@ class ThirdPartyEventRules:
|
||||||
if on_threepid_bind is not None:
|
if on_threepid_bind is not None:
|
||||||
self._on_threepid_bind_callbacks.append(on_threepid_bind)
|
self._on_threepid_bind_callbacks.append(on_threepid_bind)
|
||||||
|
|
||||||
|
if on_add_user_third_party_identifier is not None:
|
||||||
|
self._on_add_user_third_party_identifier_callbacks.append(
|
||||||
|
on_add_user_third_party_identifier
|
||||||
|
)
|
||||||
|
|
||||||
async def check_event_allowed(
|
async def check_event_allowed(
|
||||||
self,
|
self,
|
||||||
event: EventBase,
|
event: EventBase,
|
||||||
|
@ -511,6 +530,9 @@ class ThirdPartyEventRules:
|
||||||
local homeserver, not when it's created on an identity server (and then kept track
|
local homeserver, not when it's created on an identity server (and then kept track
|
||||||
of so that it can be unbound on the same IS later on).
|
of so that it can be unbound on the same IS later on).
|
||||||
|
|
||||||
|
THIS MODULE CALLBACK METHOD HAS BEEN DEPRECATED. Please use the
|
||||||
|
`on_add_user_third_party_identifier` callback method instead.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user_id: the user being associated with the threepid.
|
user_id: the user being associated with the threepid.
|
||||||
medium: the threepid's medium.
|
medium: the threepid's medium.
|
||||||
|
@ -523,3 +545,44 @@ class ThirdPartyEventRules:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
"Failed to run module API callback %s: %s", callback, e
|
"Failed to run module API callback %s: %s", callback, e
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def on_add_user_third_party_identifier(
|
||||||
|
self, user_id: str, medium: str, address: str
|
||||||
|
) -> None:
|
||||||
|
"""Called when an association between a user's Matrix ID and a third-party ID
|
||||||
|
(email, phone number) has successfully been registered on the homeserver.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The User ID included in the association.
|
||||||
|
medium: The medium of the third-party ID (email, msisdn).
|
||||||
|
address: The address of the third-party ID (i.e. an email address).
|
||||||
|
"""
|
||||||
|
for callback in self._on_add_user_third_party_identifier_callbacks:
|
||||||
|
try:
|
||||||
|
await callback(user_id, medium, address)
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(
|
||||||
|
"Failed to run module API callback %s: %s", callback, e
|
||||||
|
)
|
||||||
|
|
||||||
|
async def on_remove_user_third_party_identifier(
|
||||||
|
self, user_id: str, medium: str, address: str
|
||||||
|
) -> None:
|
||||||
|
"""Called when an association between a user's Matrix ID and a third-party ID
|
||||||
|
(email, phone number) has been successfully removed on the homeserver.
|
||||||
|
|
||||||
|
This is called *after* any known bindings on identity servers for this
|
||||||
|
association have been removed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The User ID included in the removed association.
|
||||||
|
medium: The medium of the third-party ID (email, msisdn).
|
||||||
|
address: The address of the third-party ID (i.e. an email address).
|
||||||
|
"""
|
||||||
|
for callback in self._on_remove_user_third_party_identifier_callbacks:
|
||||||
|
try:
|
||||||
|
await callback(user_id, medium, address)
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(
|
||||||
|
"Failed to run module API callback %s: %s", callback, e
|
||||||
|
)
|
||||||
|
|
|
@ -1542,6 +1542,17 @@ class AuthHandler:
|
||||||
async def add_threepid(
|
async def add_threepid(
|
||||||
self, user_id: str, medium: str, address: str, validated_at: int
|
self, user_id: str, medium: str, address: str, validated_at: int
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""
|
||||||
|
Adds an association between a user's Matrix ID and a third-party ID (email,
|
||||||
|
phone number).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The ID of the user to associate.
|
||||||
|
medium: The medium of the third-party ID (email, msisdn).
|
||||||
|
address: The address of the third-party ID (i.e. an email address).
|
||||||
|
validated_at: The timestamp in ms of when the validation that the user owns
|
||||||
|
this third-party ID occurred.
|
||||||
|
"""
|
||||||
# check if medium has a valid value
|
# check if medium has a valid value
|
||||||
if medium not in ["email", "msisdn"]:
|
if medium not in ["email", "msisdn"]:
|
||||||
raise SynapseError(
|
raise SynapseError(
|
||||||
|
@ -1566,42 +1577,44 @@ class AuthHandler:
|
||||||
user_id, medium, address, validated_at, self.hs.get_clock().time_msec()
|
user_id, medium, address, validated_at, self.hs.get_clock().time_msec()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Inform Synapse modules that a 3PID association has been created.
|
||||||
|
await self._third_party_rules.on_add_user_third_party_identifier(
|
||||||
|
user_id, medium, address
|
||||||
|
)
|
||||||
|
|
||||||
|
# Deprecated method for informing Synapse modules that a 3PID association
|
||||||
|
# has successfully been created.
|
||||||
await self._third_party_rules.on_threepid_bind(user_id, medium, address)
|
await self._third_party_rules.on_threepid_bind(user_id, medium, address)
|
||||||
|
|
||||||
async def delete_threepid(
|
async def delete_local_threepid(
|
||||||
self, user_id: str, medium: str, address: str, id_server: Optional[str] = None
|
self, user_id: str, medium: str, address: str
|
||||||
) -> bool:
|
) -> None:
|
||||||
"""Attempts to unbind the 3pid on the identity servers and deletes it
|
"""Deletes an association between a third-party ID and a user ID from the local
|
||||||
from the local database.
|
database. This method does not unbind the association from any identity servers.
|
||||||
|
|
||||||
|
If `medium` is 'email' and a pusher is associated with this third-party ID, the
|
||||||
|
pusher will also be deleted.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user_id: ID of user to remove the 3pid from.
|
user_id: ID of user to remove the 3pid from.
|
||||||
medium: The medium of the 3pid being removed: "email" or "msisdn".
|
medium: The medium of the 3pid being removed: "email" or "msisdn".
|
||||||
address: The 3pid address to remove.
|
address: The 3pid address to remove.
|
||||||
id_server: Use the given identity server when unbinding
|
|
||||||
any threepids. If None then will attempt to unbind using the
|
|
||||||
identity server specified when binding (if known).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Returns True if successfully unbound the 3pid on
|
|
||||||
the identity server, False if identity server doesn't support the
|
|
||||||
unbind API.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 'Canonicalise' email addresses as per above
|
# 'Canonicalise' email addresses as per above
|
||||||
if medium == "email":
|
if medium == "email":
|
||||||
address = canonicalise_email(address)
|
address = canonicalise_email(address)
|
||||||
|
|
||||||
result = await self.hs.get_identity_handler().try_unbind_threepid(
|
await self.store.user_delete_threepid(user_id, medium, address)
|
||||||
user_id, medium, address, id_server
|
|
||||||
|
# Inform Synapse modules that a 3PID association has been deleted.
|
||||||
|
await self._third_party_rules.on_remove_user_third_party_identifier(
|
||||||
|
user_id, medium, address
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.store.user_delete_threepid(user_id, medium, address)
|
|
||||||
if medium == "email":
|
if medium == "email":
|
||||||
await self.store.delete_pusher_by_app_id_pushkey_user_id(
|
await self.store.delete_pusher_by_app_id_pushkey_user_id(
|
||||||
app_id="m.email", pushkey=address, user_id=user_id
|
app_id="m.email", pushkey=address, user_id=user_id
|
||||||
)
|
)
|
||||||
return result
|
|
||||||
|
|
||||||
async def hash(self, password: str) -> str:
|
async def hash(self, password: str) -> str:
|
||||||
"""Computes a secure hash of password.
|
"""Computes a secure hash of password.
|
||||||
|
|
|
@ -100,26 +100,28 @@ class DeactivateAccountHandler:
|
||||||
# unbinding
|
# unbinding
|
||||||
identity_server_supports_unbinding = True
|
identity_server_supports_unbinding = True
|
||||||
|
|
||||||
# Retrieve the 3PIDs this user has bound to an identity server
|
# Attempt to unbind any known bound threepids to this account from identity
|
||||||
threepids = await self.store.user_get_bound_threepids(user_id)
|
# server(s).
|
||||||
|
bound_threepids = await self.store.user_get_bound_threepids(user_id)
|
||||||
for threepid in threepids:
|
for threepid in bound_threepids:
|
||||||
try:
|
try:
|
||||||
result = await self._identity_handler.try_unbind_threepid(
|
result = await self._identity_handler.try_unbind_threepid(
|
||||||
user_id, threepid["medium"], threepid["address"], id_server
|
user_id, threepid["medium"], threepid["address"], id_server
|
||||||
)
|
)
|
||||||
identity_server_supports_unbinding &= result
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# Do we want this to be a fatal error or should we carry on?
|
# Do we want this to be a fatal error or should we carry on?
|
||||||
logger.exception("Failed to remove threepid from ID server")
|
logger.exception("Failed to remove threepid from ID server")
|
||||||
raise SynapseError(400, "Failed to remove threepid from ID server")
|
raise SynapseError(400, "Failed to remove threepid from ID server")
|
||||||
await self.store.user_delete_threepid(
|
|
||||||
|
identity_server_supports_unbinding &= result
|
||||||
|
|
||||||
|
# Remove any local threepid associations for this account.
|
||||||
|
local_threepids = await self.store.user_get_threepids(user_id)
|
||||||
|
for threepid in local_threepids:
|
||||||
|
await self._auth_handler.delete_local_threepid(
|
||||||
user_id, threepid["medium"], threepid["address"]
|
user_id, threepid["medium"], threepid["address"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Remove all 3PIDs this user has bound to the homeserver
|
|
||||||
await self.store.user_delete_threepids(user_id)
|
|
||||||
|
|
||||||
# delete any devices belonging to the user, which will also
|
# delete any devices belonging to the user, which will also
|
||||||
# delete corresponding access tokens.
|
# delete corresponding access tokens.
|
||||||
await self._device_handler.delete_all_devices_for_user(user_id)
|
await self._device_handler.delete_all_devices_for_user(user_id)
|
||||||
|
|
|
@ -64,9 +64,11 @@ from synapse.events.third_party_rules import (
|
||||||
CHECK_EVENT_ALLOWED_CALLBACK,
|
CHECK_EVENT_ALLOWED_CALLBACK,
|
||||||
CHECK_THREEPID_CAN_BE_INVITED_CALLBACK,
|
CHECK_THREEPID_CAN_BE_INVITED_CALLBACK,
|
||||||
CHECK_VISIBILITY_CAN_BE_MODIFIED_CALLBACK,
|
CHECK_VISIBILITY_CAN_BE_MODIFIED_CALLBACK,
|
||||||
|
ON_ADD_USER_THIRD_PARTY_IDENTIFIER_CALLBACK,
|
||||||
ON_CREATE_ROOM_CALLBACK,
|
ON_CREATE_ROOM_CALLBACK,
|
||||||
ON_NEW_EVENT_CALLBACK,
|
ON_NEW_EVENT_CALLBACK,
|
||||||
ON_PROFILE_UPDATE_CALLBACK,
|
ON_PROFILE_UPDATE_CALLBACK,
|
||||||
|
ON_REMOVE_USER_THIRD_PARTY_IDENTIFIER_CALLBACK,
|
||||||
ON_THREEPID_BIND_CALLBACK,
|
ON_THREEPID_BIND_CALLBACK,
|
||||||
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK,
|
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK,
|
||||||
)
|
)
|
||||||
|
@ -357,6 +359,12 @@ class ModuleApi:
|
||||||
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK
|
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK
|
||||||
] = None,
|
] = None,
|
||||||
on_threepid_bind: Optional[ON_THREEPID_BIND_CALLBACK] = None,
|
on_threepid_bind: Optional[ON_THREEPID_BIND_CALLBACK] = None,
|
||||||
|
on_add_user_third_party_identifier: Optional[
|
||||||
|
ON_ADD_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
|
||||||
|
] = None,
|
||||||
|
on_remove_user_third_party_identifier: Optional[
|
||||||
|
ON_REMOVE_USER_THIRD_PARTY_IDENTIFIER_CALLBACK
|
||||||
|
] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Registers callbacks for third party event rules capabilities.
|
"""Registers callbacks for third party event rules capabilities.
|
||||||
|
|
||||||
|
@ -373,6 +381,8 @@ class ModuleApi:
|
||||||
on_profile_update=on_profile_update,
|
on_profile_update=on_profile_update,
|
||||||
on_user_deactivation_status_changed=on_user_deactivation_status_changed,
|
on_user_deactivation_status_changed=on_user_deactivation_status_changed,
|
||||||
on_threepid_bind=on_threepid_bind,
|
on_threepid_bind=on_threepid_bind,
|
||||||
|
on_add_user_third_party_identifier=on_add_user_third_party_identifier,
|
||||||
|
on_remove_user_third_party_identifier=on_remove_user_third_party_identifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
def register_presence_router_callbacks(
|
def register_presence_router_callbacks(
|
||||||
|
|
|
@ -304,13 +304,20 @@ class UserRestServletV2(RestServlet):
|
||||||
# remove old threepids
|
# remove old threepids
|
||||||
for medium, address in del_threepids:
|
for medium, address in del_threepids:
|
||||||
try:
|
try:
|
||||||
await self.auth_handler.delete_threepid(
|
# Attempt to remove any known bindings of this third-party ID
|
||||||
user_id, medium, address, None
|
# and user ID from identity servers.
|
||||||
|
await self.hs.get_identity_handler().try_unbind_threepid(
|
||||||
|
user_id, medium, address, id_server=None
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Failed to remove threepids")
|
logger.exception("Failed to remove threepids")
|
||||||
raise SynapseError(500, "Failed to remove threepids")
|
raise SynapseError(500, "Failed to remove threepids")
|
||||||
|
|
||||||
|
# Delete the local association of this user ID and third-party ID.
|
||||||
|
await self.auth_handler.delete_local_threepid(
|
||||||
|
user_id, medium, address
|
||||||
|
)
|
||||||
|
|
||||||
# add new threepids
|
# add new threepids
|
||||||
current_time = self.hs.get_clock().time_msec()
|
current_time = self.hs.get_clock().time_msec()
|
||||||
for medium, address in add_threepids:
|
for medium, address in add_threepids:
|
||||||
|
|
|
@ -768,7 +768,9 @@ class ThreepidDeleteRestServlet(RestServlet):
|
||||||
user_id = requester.user.to_string()
|
user_id = requester.user.to_string()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ret = await self.auth_handler.delete_threepid(
|
# Attempt to remove any known bindings of this third-party ID
|
||||||
|
# and user ID from identity servers.
|
||||||
|
ret = await self.hs.get_identity_handler().try_unbind_threepid(
|
||||||
user_id, body.medium, body.address, body.id_server
|
user_id, body.medium, body.address, body.id_server
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -783,6 +785,11 @@ class ThreepidDeleteRestServlet(RestServlet):
|
||||||
else:
|
else:
|
||||||
id_server_unbind_result = "no-support"
|
id_server_unbind_result = "no-support"
|
||||||
|
|
||||||
|
# Delete the local association of this user ID and third-party ID.
|
||||||
|
await self.auth_handler.delete_local_threepid(
|
||||||
|
user_id, body.medium, body.address
|
||||||
|
)
|
||||||
|
|
||||||
return 200, {"id_server_unbind_result": id_server_unbind_result}
|
return 200, {"id_server_unbind_result": id_server_unbind_result}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1002,19 +1002,6 @@ class RegistrationWorkerStore(CacheInvalidationWorkerStore):
|
||||||
desc="user_delete_threepid",
|
desc="user_delete_threepid",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def user_delete_threepids(self, user_id: str) -> None:
|
|
||||||
"""Delete all threepid this user has bound
|
|
||||||
|
|
||||||
Args:
|
|
||||||
user_id: The user id to delete all threepids of
|
|
||||||
|
|
||||||
"""
|
|
||||||
await self.db_pool.simple_delete(
|
|
||||||
"user_threepids",
|
|
||||||
keyvalues={"user_id": user_id},
|
|
||||||
desc="user_delete_threepids",
|
|
||||||
)
|
|
||||||
|
|
||||||
async def add_user_bound_threepid(
|
async def add_user_bound_threepid(
|
||||||
self, user_id: str, medium: str, address: str, id_server: str
|
self, user_id: str, medium: str, address: str, id_server: str
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
@ -369,10 +369,8 @@ class EmailPusherTests(HomeserverTestCase):
|
||||||
|
|
||||||
# disassociate the user's email address
|
# disassociate the user's email address
|
||||||
self.get_success(
|
self.get_success(
|
||||||
self.auth_handler.delete_threepid(
|
self.auth_handler.delete_local_threepid(
|
||||||
user_id=self.user_id,
|
user_id=self.user_id, medium="email", address="a@example.com"
|
||||||
medium="email",
|
|
||||||
address="a@example.com",
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -934,3 +934,124 @@ class ThirdPartyRulesTestCase(unittest.FederatingHomeserverTestCase):
|
||||||
|
|
||||||
# Check that the mock was called with the right parameters
|
# Check that the mock was called with the right parameters
|
||||||
self.assertEqual(args, (user_id, "email", "foo@example.com"))
|
self.assertEqual(args, (user_id, "email", "foo@example.com"))
|
||||||
|
|
||||||
|
def test_on_add_and_remove_user_third_party_identifier(self) -> None:
|
||||||
|
"""Tests that the on_add_user_third_party_identifier and
|
||||||
|
on_remove_user_third_party_identifier module callbacks are called
|
||||||
|
just before associating and removing a 3PID to/from an account.
|
||||||
|
"""
|
||||||
|
# Pretend to be a Synapse module and register both callbacks as mocks.
|
||||||
|
third_party_rules = self.hs.get_third_party_event_rules()
|
||||||
|
on_add_user_third_party_identifier_callback_mock = Mock(
|
||||||
|
return_value=make_awaitable(None)
|
||||||
|
)
|
||||||
|
on_remove_user_third_party_identifier_callback_mock = Mock(
|
||||||
|
return_value=make_awaitable(None)
|
||||||
|
)
|
||||||
|
third_party_rules._on_threepid_bind_callbacks.append(
|
||||||
|
on_add_user_third_party_identifier_callback_mock
|
||||||
|
)
|
||||||
|
third_party_rules._on_threepid_bind_callbacks.append(
|
||||||
|
on_remove_user_third_party_identifier_callback_mock
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register an admin user.
|
||||||
|
self.register_user("admin", "password", admin=True)
|
||||||
|
admin_tok = self.login("admin", "password")
|
||||||
|
|
||||||
|
# Also register a normal user we can modify.
|
||||||
|
user_id = self.register_user("user", "password")
|
||||||
|
|
||||||
|
# Add a 3PID to the user.
|
||||||
|
channel = self.make_request(
|
||||||
|
"PUT",
|
||||||
|
"/_synapse/admin/v2/users/%s" % user_id,
|
||||||
|
{
|
||||||
|
"threepids": [
|
||||||
|
{
|
||||||
|
"medium": "email",
|
||||||
|
"address": "foo@example.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
access_token=admin_tok,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check that the mocked add callback was called with the appropriate
|
||||||
|
# 3PID details.
|
||||||
|
self.assertEqual(channel.code, 200, channel.json_body)
|
||||||
|
on_add_user_third_party_identifier_callback_mock.assert_called_once()
|
||||||
|
args = on_add_user_third_party_identifier_callback_mock.call_args[0]
|
||||||
|
self.assertEqual(args, (user_id, "email", "foo@example.com"))
|
||||||
|
|
||||||
|
# Now remove the 3PID from the user
|
||||||
|
channel = self.make_request(
|
||||||
|
"PUT",
|
||||||
|
"/_synapse/admin/v2/users/%s" % user_id,
|
||||||
|
{
|
||||||
|
"threepids": [],
|
||||||
|
},
|
||||||
|
access_token=admin_tok,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check that the mocked remove callback was called with the appropriate
|
||||||
|
# 3PID details.
|
||||||
|
self.assertEqual(channel.code, 200, channel.json_body)
|
||||||
|
on_remove_user_third_party_identifier_callback_mock.assert_called_once()
|
||||||
|
args = on_remove_user_third_party_identifier_callback_mock.call_args[0]
|
||||||
|
self.assertEqual(args, (user_id, "email", "foo@example.com"))
|
||||||
|
|
||||||
|
def test_on_remove_user_third_party_identifier_is_called_on_deactivate(
|
||||||
|
self,
|
||||||
|
) -> None:
|
||||||
|
"""Tests that the on_remove_user_third_party_identifier module callback is called
|
||||||
|
when a user is deactivated and their third-party ID associations are deleted.
|
||||||
|
"""
|
||||||
|
# Pretend to be a Synapse module and register both callbacks as mocks.
|
||||||
|
third_party_rules = self.hs.get_third_party_event_rules()
|
||||||
|
on_remove_user_third_party_identifier_callback_mock = Mock(
|
||||||
|
return_value=make_awaitable(None)
|
||||||
|
)
|
||||||
|
third_party_rules._on_threepid_bind_callbacks.append(
|
||||||
|
on_remove_user_third_party_identifier_callback_mock
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register an admin user.
|
||||||
|
self.register_user("admin", "password", admin=True)
|
||||||
|
admin_tok = self.login("admin", "password")
|
||||||
|
|
||||||
|
# Also register a normal user we can modify.
|
||||||
|
user_id = self.register_user("user", "password")
|
||||||
|
|
||||||
|
# Add a 3PID to the user.
|
||||||
|
channel = self.make_request(
|
||||||
|
"PUT",
|
||||||
|
"/_synapse/admin/v2/users/%s" % user_id,
|
||||||
|
{
|
||||||
|
"threepids": [
|
||||||
|
{
|
||||||
|
"medium": "email",
|
||||||
|
"address": "foo@example.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
access_token=admin_tok,
|
||||||
|
)
|
||||||
|
self.assertEqual(channel.code, 200, channel.json_body)
|
||||||
|
|
||||||
|
# Now deactivate the user.
|
||||||
|
channel = self.make_request(
|
||||||
|
"PUT",
|
||||||
|
"/_synapse/admin/v2/users/%s" % user_id,
|
||||||
|
{
|
||||||
|
"deactivated": True,
|
||||||
|
},
|
||||||
|
access_token=admin_tok,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check that the mocked remove callback was called with the appropriate
|
||||||
|
# 3PID details.
|
||||||
|
self.assertEqual(channel.code, 200, channel.json_body)
|
||||||
|
on_remove_user_third_party_identifier_callback_mock.assert_called_once()
|
||||||
|
args = on_remove_user_third_party_identifier_callback_mock.call_args[0]
|
||||||
|
self.assertEqual(args, (user_id, "email", "foo@example.com"))
|
||||||
|
|
Loading…
Reference in New Issue