Enable user without password (#16770)

Closes:
- https://github.com/matrix-org/synapse/issues/10397
- #10397 

An administrator should know whether he wants to set a password or not.
There are many uses cases where a blank password is required.

- Use of only some users with SSO.
- Use of bots with password, users with SSO
This commit is contained in:
Dirk Klimpel 2024-01-02 12:52:51 +01:00 committed by GitHub
parent c1fe945dd5
commit 14ed84ac33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 31 deletions

1
changelog.d/16770.bugfix Normal file
View File

@ -0,0 +1 @@
Allow reactivate user without password with Admin API in some edge cases.

View File

@ -149,10 +149,11 @@ Body parameters:
granting them access to the Admin API, among other things. granting them access to the Admin API, among other things.
- `deactivated` - **bool**, optional. If unspecified, deactivation state will be left unchanged. - `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: Note:
- `deactivated` is set to `false` and the user was previously deactivated (you are reactivating this user) - For the password field there is no strict check of the necessity for its presence.
- Users are allowed to set their password on this homeserver (both `password_config.enabled` and It is possible to have active users without a password, e.g. when authenticating with OIDC is configured.
`password_config.localdb_enabled` config options are set to `true`). 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. 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 Note: a user cannot be erased with this API. For more details on
@ -223,7 +224,7 @@ The following parameters should be set in the URL:
**or** displaynames that contain this value. **or** displaynames that contain this value.
- `guests` - string representing a bool - Is optional and if `false` will **exclude** guest users. - `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) 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. 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. - `deactivated` - string representing a bool - Is optional and if `true` will **include** deactivated users.
Defaults to `false` to exclude deactivated users. Defaults to `false` to exclude deactivated users.
@ -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. - `is_guest` - bool - Status if that user is a guest account.
- `admin` - bool - Status if that user is a server administrator. - `admin` - bool - Status if that user is a server administrator.
- `user_type` - string - Type of the user. Normal users are type `None`. - `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. - `deactivated` - bool - Status if that user has been marked as deactivated.
- `erased` - bool - Status if that user has been marked as erased. - `erased` - bool - Status if that user has been marked as erased.
- `shadow_banned` - bool - Status if that user has been marked as shadow banned. - `shadow_banned` - bool - Status if that user has been marked as shadow banned.
@ -887,7 +888,7 @@ The following fields are returned in the JSON response body:
### Create a device ### 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. exists already.
The API is: The API is:
@ -1254,11 +1255,11 @@ The following parameters should be set in the URL:
## Check username availability ## 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) API](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available)
for more information. 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`. `/_matrix/client/r0/register/available`.
The API is: The API is:

View File

@ -412,15 +412,6 @@ class UserRestServletV2(RestServlet):
target_user.to_string(), False, requester, by_admin=True target_user.to_string(), False, requester, by_admin=True
) )
elif not deactivate and user["deactivated"]: 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( await self.deactivate_account_handler.activate_account(
target_user.to_string() target_user.to_string()
) )

View File

@ -2747,7 +2747,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
profile = self.get_success(self.store._get_user_in_directory(self.other_user)) profile = self.get_success(self.store._get_user_in_directory(self.other_user))
self.assertIsNone(profile) self.assertIsNone(profile)
def test_reactivate_user(self) -> None: def test_reactivate_user_with_password(self) -> None:
""" """
Test reactivating another user. Test reactivating another user.
""" """
@ -2755,16 +2755,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
# Deactivate the user. # Deactivate the user.
self._deactivate_user("@user:test") 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},
)
self.assertEqual(400, channel.code, msg=channel.json_body)
# Reactivate the user.
channel = self.make_request( channel = self.make_request(
"PUT", "PUT",
self.url_other_user, self.url_other_user,
@ -2779,6 +2770,30 @@ class UserRestTestCase(unittest.HomeserverTestCase):
# This key was removed intentionally. Ensure it is not accidentally re-included. # This key was removed intentionally. Ensure it is not accidentally re-included.
self.assertNotIn("password_hash", channel.json_body) self.assertNotIn("password_hash", channel.json_body)
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},
)
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)
@override_config({"password_config": {"localdb_enabled": False}}) @override_config({"password_config": {"localdb_enabled": False}})
def test_reactivate_user_localdb_disabled(self) -> None: def test_reactivate_user_localdb_disabled(self) -> None:
""" """
@ -2788,7 +2803,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
# Deactivate the user. # Deactivate the user.
self._deactivate_user("@user:test") self._deactivate_user("@user:test")
# Reactivate the user with a password # Reactivate the user with a password.
channel = self.make_request( channel = self.make_request(
"PUT", "PUT",
self.url_other_user, self.url_other_user,
@ -2822,7 +2837,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
# Deactivate the user. # Deactivate the user.
self._deactivate_user("@user:test") self._deactivate_user("@user:test")
# Reactivate the user with a password # Reactivate the user with a password.
channel = self.make_request( channel = self.make_request(
"PUT", "PUT",
self.url_other_user, self.url_other_user,