From 05cd4a142da05493f99d3769ad8df7ebeaf0129e Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Fri, 13 Mar 2020 08:05:48 -0400 Subject: [PATCH 1/4] Update the APIs to optionally logout devices. --- docs/admin_api/user_admin_api.rst | 6 +++- synapse/handlers/set_password.py | 39 +++++++++++++++---------- synapse/rest/admin/users.py | 6 ++-- synapse/rest/client/v2_alpha/account.py | 5 +++- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/docs/admin_api/user_admin_api.rst b/docs/admin_api/user_admin_api.rst index 6b02d963e6e3..9ce10119ff5e 100644 --- a/docs/admin_api/user_admin_api.rst +++ b/docs/admin_api/user_admin_api.rst @@ -38,6 +38,7 @@ The parameter ``threepids`` is optional. The parameter ``avatar_url`` is optional. The parameter ``admin`` is optional and defaults to 'false'. The parameter ``deactivated`` is optional and defaults to 'false'. +The parameter ``password`` is optional. If provided the user's password is updated and all devices are logged out. If the user already exists then optional parameters default to the current value. List Accounts @@ -168,11 +169,14 @@ with a body of: .. code:: json { - "new_password": "" + "new_password": "", + "logout_devices": true, } including an ``access_token`` of a server admin. +The parameter ``new_password`` is required. +The parameter ``logout_devices`` is optional and defaults to ``true``. Get whether a user is a server administrator or not =================================================== diff --git a/synapse/handlers/set_password.py b/synapse/handlers/set_password.py index d90c9e0108f6..1b723ef6ffb5 100644 --- a/synapse/handlers/set_password.py +++ b/synapse/handlers/set_password.py @@ -13,10 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging +from typing import Optional from twisted.internet import defer from synapse.api.errors import Codes, StoreError, SynapseError +from synapse.types import Requester from ._base import BaseHandler @@ -32,15 +34,18 @@ def __init__(self, hs): self._device_handler = hs.get_device_handler() @defer.inlineCallbacks - def set_password(self, user_id, newpassword, requester=None): + def set_password( + self, + user_id: str, + newpassword: str, + logout_devices: bool, + requester: Optional[Requester] = None, + ): if not self.hs.config.password_localdb_enabled: raise SynapseError(403, "Password change disabled", errcode=Codes.FORBIDDEN) password_hash = yield self._auth_handler.hash(newpassword) - except_device_id = requester.device_id if requester else None - except_access_token_id = requester.access_token_id if requester else None - try: yield self.store.user_set_password_hash(user_id, password_hash) except StoreError as e: @@ -48,14 +53,18 @@ def set_password(self, user_id, newpassword, requester=None): raise SynapseError(404, "Unknown user", Codes.NOT_FOUND) raise e - # we want to log out all of the user's other sessions. First delete - # all his other devices. - yield self._device_handler.delete_all_devices_for_user( - user_id, except_device_id=except_device_id - ) - - # and now delete any access tokens which weren't associated with - # devices (or were associated with this device). - yield self._auth_handler.delete_access_tokens_for_user( - user_id, except_token_id=except_access_token_id - ) + # Optionally, log out all of the user's other sessions. + if logout_devices: + except_device_id = requester.device_id if requester else None + except_access_token_id = requester.access_token_id if requester else None + + # First delete all his other devices. + yield self._device_handler.delete_all_devices_for_user( + user_id, except_device_id=except_device_id + ) + + # and now delete any access tokens which weren't associated with + # devices (or were associated with this device). + yield self._auth_handler.delete_access_tokens_for_user( + user_id, except_token_id=except_access_token_id + ) diff --git a/synapse/rest/admin/users.py b/synapse/rest/admin/users.py index 80f959248dcd..8551ac19b832 100644 --- a/synapse/rest/admin/users.py +++ b/synapse/rest/admin/users.py @@ -221,8 +221,9 @@ async def on_PUT(self, request, user_id): raise SynapseError(400, "Invalid password") else: new_password = body["password"] + logout_devices = True await self.set_password_handler.set_password( - target_user.to_string(), new_password, requester + target_user.to_string(), new_password, logout_devices, requester ) if "deactivated" in body: @@ -536,9 +537,10 @@ async def on_POST(self, request, target_user_id): params = parse_json_object_from_request(request) assert_params_in_dict(params, ["new_password"]) new_password = params["new_password"] + logout_devices = params.get("logout_devices", True) await self._set_password_handler.set_password( - target_user_id, new_password, requester + target_user_id, new_password, logout_devices, requester ) return 200, {} diff --git a/synapse/rest/client/v2_alpha/account.py b/synapse/rest/client/v2_alpha/account.py index dc837d6c7582..631cc74cb42f 100644 --- a/synapse/rest/client/v2_alpha/account.py +++ b/synapse/rest/client/v2_alpha/account.py @@ -265,8 +265,11 @@ async def on_POST(self, request): assert_params_in_dict(params, ["new_password"]) new_password = params["new_password"] + logout_devices = params.get("logout_devices", True) - await self._set_password_handler.set_password(user_id, new_password, requester) + await self._set_password_handler.set_password( + user_id, new_password, logout_devices, requester + ) return 200, {} From 0a46470da073b00e08a2aad5fd98961014d69e35 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 16 Mar 2020 09:52:46 -0400 Subject: [PATCH 2/4] Add a changelog entry. --- changelog.d/7085.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7085.feature diff --git a/changelog.d/7085.feature b/changelog.d/7085.feature new file mode 100644 index 000000000000..df6d0f990d7c --- /dev/null +++ b/changelog.d/7085.feature @@ -0,0 +1 @@ +Add an optional parameter to control whether other sessions are logged out when a user's password is modified. From ff625a9d7527b761e2bed48c88280a7436a718d9 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 17 Mar 2020 08:22:33 -0400 Subject: [PATCH 3/4] Rename a variable to match code style. --- synapse/handlers/set_password.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synapse/handlers/set_password.py b/synapse/handlers/set_password.py index 1b723ef6ffb5..584ff500da4b 100644 --- a/synapse/handlers/set_password.py +++ b/synapse/handlers/set_password.py @@ -37,14 +37,14 @@ def __init__(self, hs): def set_password( self, user_id: str, - newpassword: str, + new_password: str, logout_devices: bool, requester: Optional[Requester] = None, ): if not self.hs.config.password_localdb_enabled: raise SynapseError(403, "Password change disabled", errcode=Codes.FORBIDDEN) - password_hash = yield self._auth_handler.hash(newpassword) + password_hash = yield self._auth_handler.hash(new_password) try: yield self.store.user_set_password_hash(user_id, password_hash) From d914cd99e72e0c34e0fcef4e9aba54fc8e4bc98f Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 17 Mar 2020 08:23:57 -0400 Subject: [PATCH 4/4] Update comment to be more generic. Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- synapse/handlers/set_password.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/handlers/set_password.py b/synapse/handlers/set_password.py index 584ff500da4b..12657ca69836 100644 --- a/synapse/handlers/set_password.py +++ b/synapse/handlers/set_password.py @@ -58,7 +58,7 @@ def set_password( except_device_id = requester.device_id if requester else None except_access_token_id = requester.access_token_id if requester else None - # First delete all his other devices. + # First delete all of their other devices. yield self._device_handler.delete_all_devices_for_user( user_id, except_device_id=except_device_id )