Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Allow reactivate a user without password #16739

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/4.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow reactivate user without password with Admin API in some edge cases.
19 changes: 10 additions & 9 deletions docs/admin_api/user_admin_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,11 @@ Body parameters:
granting them access to the Admin API, among other things.
- `deactivated` - **bool**, optional. If unspecified, deactivation state will be left unchanged.

Note: the `password` field must also be set if both of the following are true:
- `deactivated` is set to `false` and the user was previously deactivated (you are reactivating this user)
- Users are allowed to set their password on this homeserver (both `password_config.enabled` and
`password_config.localdb_enabled` config options are set to `true`).
Note:
- For the password field there is no strict check of the necessity for its presence.
It is possible to have active users without a password, e.g. when authenticating with OIDC is configured.
You must check yourself whether a password is required when reactivating a user or not.
- It is not possible to set a password if the config option `password_config.localdb_enabled` is set `false`.
Users' passwords are wiped upon account deactivation, hence the need to set a new one here.

Note: a user cannot be erased with this API. For more details on
Expand Down Expand Up @@ -223,7 +224,7 @@ The following parameters should be set in the URL:
**or** displaynames that contain this value.
- `guests` - string representing a bool - Is optional and if `false` will **exclude** guest users.
Defaults to `true` to include guest users. This parameter is not supported when MSC3861 is enabled. [See #15582](https://github.com/matrix-org/synapse/pull/15582)
- `admins` - Optional flag to filter admins. If `true`, only admins are queried. If `false`, admins are excluded from
- `admins` - Optional flag to filter admins. If `true`, only admins are queried. If `false`, admins are excluded from
the query. When the flag is absent (the default), **both** admins and non-admins are included in the search results.
- `deactivated` - string representing a bool - Is optional and if `true` will **include** deactivated users.
Defaults to `false` to exclude deactivated users.
Expand Down Expand Up @@ -272,7 +273,7 @@ The following fields are returned in the JSON response body:
- `is_guest` - bool - Status if that user is a guest account.
- `admin` - bool - Status if that user is a server administrator.
- `user_type` - string - Type of the user. Normal users are type `None`.
This allows user type specific behaviour. There are also types `support` and `bot`.
This allows user type specific behaviour. There are also types `support` and `bot`.
- `deactivated` - bool - Status if that user has been marked as deactivated.
- `erased` - bool - Status if that user has been marked as erased.
- `shadow_banned` - bool - Status if that user has been marked as shadow banned.
Expand Down Expand Up @@ -887,7 +888,7 @@ The following fields are returned in the JSON response body:

### Create a device

Creates a new device for a specific `user_id` and `device_id`. Does nothing if the `device_id`
Creates a new device for a specific `user_id` and `device_id`. Does nothing if the `device_id`
exists already.

The API is:
Expand Down Expand Up @@ -1254,11 +1255,11 @@ The following parameters should be set in the URL:

## Check username availability

Checks to see if a username is available, and valid, for the server. See [the client-server
Checks to see if a username is available, and valid, for the server. See [the client-server
API](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available)
for more information.

This endpoint will work even if registration is disabled on the server, unlike
This endpoint will work even if registration is disabled on the server, unlike
`/_matrix/client/r0/register/available`.

The API is:
Expand Down
9 changes: 0 additions & 9 deletions synapse/rest/admin/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,15 +406,6 @@ async def on_PUT(
target_user.to_string(), False, requester, by_admin=True
)
elif not deactivate and user["deactivated"]:
if (
"password" not in body
and self.auth_handler.can_change_password()
):
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"Must provide a password to re-activate an account.",
)

await self.deactivate_account_handler.activate_account(
target_user.to_string()
)
Expand Down
31 changes: 23 additions & 8 deletions tests/rest/admin/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -2741,29 +2741,44 @@ def test_change_name_deactivate_user_user_directory(self) -> None:
profile = self.get_success(self.store._get_user_in_directory(self.other_user))
self.assertIsNone(profile)

def test_reactivate_user(self) -> None:
def test_reactivate_user_with_password(self) -> None:
"""
Test reactivating another user.
"""

# Deactivate the user.
self._deactivate_user("@user:test")

# Attempt to reactivate the user (without a password).
# Reactivate the user with password.
channel = self.make_request(
"PUT",
self.url_other_user,
access_token=self.admin_user_tok,
content={"deactivated": False},
content={"deactivated": False, "password": "foo"},
)
self.assertEqual(400, channel.code, msg=channel.json_body)
self.assertEqual(200, channel.code, msg=channel.json_body)
self.assertEqual("@user:test", channel.json_body["name"])
self.assertFalse(channel.json_body["deactivated"])
self._is_erased("@user:test", False)

# This key was removed intentionally. Ensure it is not accidentally re-included.
self.assertNotIn("password_hash", channel.json_body)

# Reactivate the user.
def test_reactivate_user_without_password(self) -> None:
"""
Test reactivating another user without a password.
This can be using some local users and some user with SSO (password = `null`).
"""

# Deactivate the user.
self._deactivate_user("@user:test")

# Reactivate the user without a password.
channel = self.make_request(
"PUT",
self.url_other_user,
access_token=self.admin_user_tok,
content={"deactivated": False, "password": "foo"},
content={"deactivated": False},
)
self.assertEqual(200, channel.code, msg=channel.json_body)
self.assertEqual("@user:test", channel.json_body["name"])
Expand All @@ -2782,7 +2797,7 @@ def test_reactivate_user_localdb_disabled(self) -> None:
# Deactivate the user.
self._deactivate_user("@user:test")

# Reactivate the user with a password
# Reactivate the user with a password.
channel = self.make_request(
"PUT",
self.url_other_user,
Expand Down Expand Up @@ -2816,7 +2831,7 @@ def test_reactivate_user_password_disabled(self) -> None:
# Deactivate the user.
self._deactivate_user("@user:test")

# Reactivate the user with a password
# Reactivate the user with a password.
channel = self.make_request(
"PUT",
self.url_other_user,
Expand Down