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_
|
||||
|
||||
**<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
|
||||
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.
|
||||
|
||||
### `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
|
||||
|
||||
The example below is a module that implements the third-party rules callback
|
||||
|
@ -300,4 +343,4 @@ class EventCensorer:
|
|||
)
|
||||
event_dict["content"] = new_event_content
|
||||
return event_dict
|
||||
```
|
||||
```
|
|
@ -88,6 +88,30 @@ process, for example:
|
|||
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
|
||||
|
||||
## 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_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK = Callable[[str, bool, bool], 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:
|
||||
|
@ -172,6 +174,12 @@ class ThirdPartyEventRules:
|
|||
ON_USER_DEACTIVATION_STATUS_CHANGED_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(
|
||||
self,
|
||||
|
@ -191,6 +199,12 @@ class ThirdPartyEventRules:
|
|||
ON_USER_DEACTIVATION_STATUS_CHANGED_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:
|
||||
"""Register callbacks from modules for each hook."""
|
||||
if check_event_allowed is not None:
|
||||
|
@ -228,6 +242,11 @@ class ThirdPartyEventRules:
|
|||
if on_threepid_bind is not None:
|
||||
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(
|
||||
self,
|
||||
event: EventBase,
|
||||
|
@ -511,6 +530,9 @@ class ThirdPartyEventRules:
|
|||
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).
|
||||
|
||||
THIS MODULE CALLBACK METHOD HAS BEEN DEPRECATED. Please use the
|
||||
`on_add_user_third_party_identifier` callback method instead.
|
||||
|
||||
Args:
|
||||
user_id: the user being associated with the threepid.
|
||||
medium: the threepid's medium.
|
||||
|
@ -523,3 +545,44 @@ class ThirdPartyEventRules:
|
|||
logger.exception(
|
||||
"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(
|
||||
self, user_id: str, medium: str, address: str, validated_at: int
|
||||
) -> 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
|
||||
if medium not in ["email", "msisdn"]:
|
||||
raise SynapseError(
|
||||
|
@ -1566,42 +1577,44 @@ class AuthHandler:
|
|||
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)
|
||||
|
||||
async def delete_threepid(
|
||||
self, user_id: str, medium: str, address: str, id_server: Optional[str] = None
|
||||
) -> bool:
|
||||
"""Attempts to unbind the 3pid on the identity servers and deletes it
|
||||
from the local database.
|
||||
async def delete_local_threepid(
|
||||
self, user_id: str, medium: str, address: str
|
||||
) -> None:
|
||||
"""Deletes an association between a third-party ID and a user ID from the local
|
||||
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:
|
||||
user_id: ID of user to remove the 3pid from.
|
||||
medium: The medium of the 3pid being removed: "email" or "msisdn".
|
||||
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
|
||||
if medium == "email":
|
||||
address = canonicalise_email(address)
|
||||
|
||||
result = await self.hs.get_identity_handler().try_unbind_threepid(
|
||||
user_id, medium, address, id_server
|
||||
await self.store.user_delete_threepid(user_id, medium, address)
|
||||
|
||||
# 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":
|
||||
await self.store.delete_pusher_by_app_id_pushkey_user_id(
|
||||
app_id="m.email", pushkey=address, user_id=user_id
|
||||
)
|
||||
return result
|
||||
|
||||
async def hash(self, password: str) -> str:
|
||||
"""Computes a secure hash of password.
|
||||
|
|
|
@ -100,26 +100,28 @@ class DeactivateAccountHandler:
|
|||
# unbinding
|
||||
identity_server_supports_unbinding = True
|
||||
|
||||
# Retrieve the 3PIDs this user has bound to an identity server
|
||||
threepids = await self.store.user_get_bound_threepids(user_id)
|
||||
|
||||
for threepid in threepids:
|
||||
# Attempt to unbind any known bound threepids to this account from identity
|
||||
# server(s).
|
||||
bound_threepids = await self.store.user_get_bound_threepids(user_id)
|
||||
for threepid in bound_threepids:
|
||||
try:
|
||||
result = await self._identity_handler.try_unbind_threepid(
|
||||
user_id, threepid["medium"], threepid["address"], id_server
|
||||
)
|
||||
identity_server_supports_unbinding &= result
|
||||
except Exception:
|
||||
# Do we want this to be a fatal error or should we carry on?
|
||||
logger.exception("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"]
|
||||
)
|
||||
|
||||
# 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 corresponding access tokens.
|
||||
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_THREEPID_CAN_BE_INVITED_CALLBACK,
|
||||
CHECK_VISIBILITY_CAN_BE_MODIFIED_CALLBACK,
|
||||
ON_ADD_USER_THIRD_PARTY_IDENTIFIER_CALLBACK,
|
||||
ON_CREATE_ROOM_CALLBACK,
|
||||
ON_NEW_EVENT_CALLBACK,
|
||||
ON_PROFILE_UPDATE_CALLBACK,
|
||||
ON_REMOVE_USER_THIRD_PARTY_IDENTIFIER_CALLBACK,
|
||||
ON_THREEPID_BIND_CALLBACK,
|
||||
ON_USER_DEACTIVATION_STATUS_CHANGED_CALLBACK,
|
||||
)
|
||||
|
@ -357,6 +359,12 @@ class ModuleApi:
|
|||
ON_USER_DEACTIVATION_STATUS_CHANGED_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:
|
||||
"""Registers callbacks for third party event rules capabilities.
|
||||
|
||||
|
@ -373,6 +381,8 @@ 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_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(
|
||||
|
|
|
@ -304,13 +304,20 @@ class UserRestServletV2(RestServlet):
|
|||
# remove old threepids
|
||||
for medium, address in del_threepids:
|
||||
try:
|
||||
await self.auth_handler.delete_threepid(
|
||||
user_id, medium, address, None
|
||||
# Attempt to remove any known bindings of this third-party ID
|
||||
# and user ID from identity servers.
|
||||
await self.hs.get_identity_handler().try_unbind_threepid(
|
||||
user_id, medium, address, id_server=None
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("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
|
||||
current_time = self.hs.get_clock().time_msec()
|
||||
for medium, address in add_threepids:
|
||||
|
|
|
@ -768,7 +768,9 @@ class ThreepidDeleteRestServlet(RestServlet):
|
|||
user_id = requester.user.to_string()
|
||||
|
||||
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
|
||||
)
|
||||
except Exception:
|
||||
|
@ -783,6 +785,11 @@ class ThreepidDeleteRestServlet(RestServlet):
|
|||
else:
|
||||
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}
|
||||
|
||||
|
||||
|
|
|
@ -1002,19 +1002,6 @@ class RegistrationWorkerStore(CacheInvalidationWorkerStore):
|
|||
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(
|
||||
self, user_id: str, medium: str, address: str, id_server: str
|
||||
) -> None:
|
||||
|
|
|
@ -369,10 +369,8 @@ class EmailPusherTests(HomeserverTestCase):
|
|||
|
||||
# disassociate the user's email address
|
||||
self.get_success(
|
||||
self.auth_handler.delete_threepid(
|
||||
user_id=self.user_id,
|
||||
medium="email",
|
||||
address="a@example.com",
|
||||
self.auth_handler.delete_local_threepid(
|
||||
user_id=self.user_id, medium="email", address="a@example.com"
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -934,3 +934,124 @@ class ThirdPartyRulesTestCase(unittest.FederatingHomeserverTestCase):
|
|||
|
||||
# Check that the mock was called with the right parameters
|
||||
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