diff --git a/docs/admin_api/user_admin_api.md b/docs/admin_api/user_admin_api.md index 9dc600b8753..26ca768eeb5 100644 --- a/docs/admin_api/user_admin_api.md +++ b/docs/admin_api/user_admin_api.md @@ -226,8 +226,10 @@ The following parameters should be set in the URL: 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 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. +- `deactivated` - Optional flag to filter deactivated users. If `true`, only deactivated users are queried. + If `false`, deactivated users are excluded from the query. When the flag is absent (the default), + **both** active and deactivated users are included in the + search results. - `limit` - string representing a positive integer - Is optional but is used for pagination, denoting the maximum number of items to return in this call. Defaults to `100`. - `from` - string representing a positive integer - Is optional but used for pagination, diff --git a/synapse/rest/admin/users.py b/synapse/rest/admin/users.py index 0229e87f431..ae0c6bbdffd 100644 --- a/synapse/rest/admin/users.py +++ b/synapse/rest/admin/users.py @@ -72,7 +72,7 @@ class UsersRestServletV2(RestServlet): The parameter `user_id` can be used to filter by user id. The parameter `name` can be used to filter by user id or display name. The parameter `guests` can be used to exclude guest users. - The parameter `deactivated` can be used to include deactivated users. + The parameter `deactivated` can be used to filter deactivated users. The parameter `order_by` can be used to order the result. The parameter `not_user_type` can be used to exclude certain user types. The parameter `locked` can be used to include locked users. @@ -118,7 +118,7 @@ async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: errcode=Codes.INVALID_PARAM, ) - deactivated = parse_boolean(request, "deactivated", default=False) + deactivated = parse_boolean(request, "deactivated") locked = parse_boolean(request, "locked", default=False) admins = parse_boolean(request, "admins") diff --git a/synapse/storage/databases/main/__init__.py b/synapse/storage/databases/main/__init__.py index 394985d87f5..bf779587d95 100644 --- a/synapse/storage/databases/main/__init__.py +++ b/synapse/storage/databases/main/__init__.py @@ -176,7 +176,7 @@ async def get_users_paginate( user_id: Optional[str] = None, name: Optional[str] = None, guests: bool = True, - deactivated: bool = False, + deactivated: Optional[bool] = None, admins: Optional[bool] = None, order_by: str = UserSortOrder.NAME.value, direction: Direction = Direction.FORWARDS, @@ -232,8 +232,11 @@ def get_users_paginate_txn( if not guests: filters.append("is_guest = 0") - if not deactivated: - filters.append("deactivated = 0") + if deactivated is not None: + if deactivated: + filters.append("deactivated = 1") + else: + filters.append("deactivated = 0") if not locked: filters.append("locked IS FALSE") diff --git a/tests/rest/admin/test_user.py b/tests/rest/admin/test_user.py index 92658542235..5722c157b21 100644 --- a/tests/rest/admin/test_user.py +++ b/tests/rest/admin/test_user.py @@ -503,7 +503,7 @@ def test_all_users(self) -> None: channel = self.make_request( "GET", - self.url + "?deactivated=true", + self.url, {}, access_token=self.admin_user_tok, ) @@ -982,6 +982,55 @@ def test_filter_admins(self) -> None: self.assertEqual(1, channel.json_body["total"]) self.assertFalse(channel.json_body["users"][0]["admin"]) + def test_filter_deactivated_users(self) -> None: + """ + Tests whether the various values of the query parameter `deactivated` lead to the + expected result set. + """ + + # Register an additional non admin user + user_id = self.register_user("user", "pass", admin=False) + + # Deactivate that user, requesting erasure. + deactivate_account_handler = self.hs.get_deactivate_account_handler() + self.get_success( + deactivate_account_handler.deactivate_account( + user_id, erase_data=True, requester=create_requester(user_id) + ) + ) + + # Query all users + channel = self.make_request( + "GET", + f"{self.url}", + access_token=self.admin_user_tok, + ) + + self.assertEqual(200, channel.code, channel.result) + self.assertEqual(2, channel.json_body["total"]) + + # Query deactivated users + channel = self.make_request( + "GET", + f"{self.url}?deactivated=true", + access_token=self.admin_user_tok, + ) + + self.assertEqual(200, channel.code, channel.result) + self.assertEqual(1, channel.json_body["total"]) + self.assertEqual("@user:test", channel.json_body["users"][0]["name"]) + + # Query deactivated users + channel = self.make_request( + "GET", + f"{self.url}?deactivated=false", + access_token=self.admin_user_tok, + ) + + self.assertEqual(200, channel.code, channel.result) + self.assertEqual(1, channel.json_body["total"]) + self.assertEqual("@admin:test", channel.json_body["users"][0]["name"]) + @override_config( { "experimental_features": { @@ -1130,7 +1179,7 @@ def test_erasure_status(self) -> None: # They should appear in the list users API, marked as not erased. channel = self.make_request( "GET", - self.url + "?deactivated=true", + self.url, access_token=self.admin_user_tok, ) users = {user["name"]: user for user in channel.json_body["users"]} @@ -1194,7 +1243,7 @@ def _order_test( dir: The direction of ordering to give the server """ - url = self.url + "?deactivated=true&" + url = self.url + "?" if order_by is not None: url += "order_by=%s&" % (order_by,) if dir is not None and dir in ("b", "f"):