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

Filter locked users in the admin API #16328

Merged
merged 6 commits into from
Sep 18, 2023
Merged
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/16328.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Report whether a user is `locked` in the [List Accounts admin API](https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html#list-accounts), and exclude locked users by default.
17 changes: 12 additions & 5 deletions docs/admin_api/user_admin_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ It returns a JSON body like the following:
"external_id": "<user_id_provider_2>"
}
],
"user_type": null
"user_type": null,
"locked": false
}
```

Expand Down Expand Up @@ -103,7 +104,8 @@ with a body of:
],
"admin": false,
"deactivated": false,
"user_type": null
"user_type": null,
"locked": false
}
```

Expand Down Expand Up @@ -184,7 +186,8 @@ A response body like the following is returned:
"shadow_banned": 0,
"displayname": "<User One>",
"avatar_url": null,
"creation_ts": 1560432668000
"creation_ts": 1560432668000,
"locked": false
}, {
"name": "<user_id2>",
"is_guest": 0,
Expand All @@ -195,7 +198,8 @@ A response body like the following is returned:
"shadow_banned": 0,
"displayname": "<User Two>",
"avatar_url": "<avatar_url>",
"creation_ts": 1561550621000
"creation_ts": 1561550621000,
"locked": false
}
],
"next_token": "100",
Expand Down Expand Up @@ -249,6 +253,8 @@ The following parameters should be set in the URL:
- `not_user_type` - Exclude certain user types, such as bot users, from the request.
Can be provided multiple times. Possible values are `bot`, `support` or "empty string".
"empty string" here means to exclude users without a type.
- `locked` - string representing a bool - Is optional and if `true` will **include** locked users.
Defaults to `false` to exclude locked users. Note: Introduced in v1.93.

Caution. The database only has indexes on the columns `name` and `creation_ts`.
This means that if a different sort order is used (`is_guest`, `admin`,
Expand All @@ -274,10 +280,11 @@ The following fields are returned in the JSON response body:
- `avatar_url` - string - The user's avatar URL if they have set one.
- `creation_ts` - integer - The user's creation timestamp in ms.
- `last_seen_ts` - integer - The user's last activity timestamp in ms.

- `locked` - bool - Status if that user has been marked as locked. Note: Introduced in v1.93.
- `next_token`: string representing a positive integer - Indication for pagination. See above.
- `total` - integer - Total number of media.

hanadi92 marked this conversation as resolved.
Show resolved Hide resolved
*Added in Synapse 1.93:* the `locked` query parameter and response field.

## Query current sessions for a user

Expand Down
6 changes: 5 additions & 1 deletion synapse/rest/admin/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class UsersRestServletV2(RestServlet):
The parameter `deactivated` can be used to include 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.
Possible values are `bot`, `support` or "empty string".
"empty string" here means to exclude users without a type.
"""
Expand Down Expand Up @@ -107,8 +108,9 @@ async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
"The guests parameter is not supported when MSC3861 is enabled.",
errcode=Codes.INVALID_PARAM,
)
deactivated = parse_boolean(request, "deactivated", default=False)

deactivated = parse_boolean(request, "deactivated", default=False)
locked = parse_boolean(request, "locked", default=False)
admins = parse_boolean(request, "admins")

# If support for MSC3866 is not enabled, apply no filtering based on the
Expand All @@ -133,6 +135,7 @@ async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
UserSortOrder.SHADOW_BANNED.value,
UserSortOrder.CREATION_TS.value,
UserSortOrder.LAST_SEEN_TS.value,
UserSortOrder.LOCKED.value,
),
)

Expand All @@ -154,6 +157,7 @@ async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
direction,
approved,
not_user_types,
locked,
)

# If support for MSC3866 is not enabled, don't show the approval flag.
Expand Down
7 changes: 6 additions & 1 deletion synapse/storage/databases/main/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ async def get_users_paginate(
direction: Direction = Direction.FORWARDS,
approved: bool = True,
not_user_types: Optional[List[str]] = None,
locked: bool = False,
) -> Tuple[List[JsonDict], int]:
"""Function to retrieve a paginated list of users from
users list. This will return a json list of users and the
Expand All @@ -194,6 +195,7 @@ async def get_users_paginate(
direction: sort ascending or descending
approved: whether to include approved users
not_user_types: list of user types to exclude
locked: whether to include locked users
Returns:
A tuple of a list of mappings from user to information and a count of total users.
"""
Expand Down Expand Up @@ -226,6 +228,9 @@ def get_users_paginate_txn(
if not deactivated:
filters.append("deactivated = 0")

if not locked:
filters.append("locked IS FALSE")

if admins is not None:
if admins:
filters.append("admin = 1")
Expand Down Expand Up @@ -290,7 +295,7 @@ def get_users_paginate_txn(
sql = f"""
SELECT name, user_type, is_guest, admin, deactivated, shadow_banned,
displayname, avatar_url, creation_ts * 1000 as creation_ts, approved,
eu.user_id is not null as erased, last_seen_ts
eu.user_id is not null as erased, last_seen_ts, locked
{sql_base}
ORDER BY {order_by_column} {order}, u.name ASC
LIMIT ? OFFSET ?
Expand Down
1 change: 1 addition & 0 deletions synapse/storage/databases/main/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class UserSortOrder(Enum):
SHADOW_BANNED = "shadow_banned"
CREATION_TS = "creation_ts"
LAST_SEEN_TS = "last_seen_ts"
LOCKED = "locked"


class StatsStore(StateDeltasStore):
Expand Down
26 changes: 26 additions & 0 deletions tests/rest/admin/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,32 @@ def test_erasure_status(self) -> None:
users = {user["name"]: user for user in channel.json_body["users"]}
self.assertIs(users[user_id]["erased"], True)

def test_filter_locked(self) -> None:
# Create a new user.
user_id = self.register_user("lockme", "lockme")

# Lock them
self.get_success(self.store.set_user_locked_status(user_id, True))

# Locked user should appear in list users API
channel = self.make_request(
"GET",
self.url + "?locked=true",
access_token=self.admin_user_tok,
)
users = {user["name"]: user for user in channel.json_body["users"]}
self.assertIn(user_id, users)
self.assertTrue(users[user_id]["locked"])

# Locked user should not appear in list users API
channel = self.make_request(
"GET",
self.url + "?locked=false",
access_token=self.admin_user_tok,
)
users = {user["name"]: user for user in channel.json_body["users"]}
self.assertNotIn(user_id, users)

def _order_test(
self,
expected_user_list: List[str],
Expand Down
Loading