From 05b0d0602df63217bfa8641a9569706f18e875d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 19 Dec 2025 12:46:54 +0100 Subject: [PATCH 1/6] Spec User suspension & locking endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per MSC4323. Signed-off-by: Kévin Commaille --- content/client-server-api/_index.md | 17 +- data/api/client-server/admin.yaml | 389 ++++++++++++++++++++++- data/api/client-server/capabilities.yaml | 34 +- 3 files changed, 429 insertions(+), 11 deletions(-) diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 8f8092b5..7a4e04b3 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -2280,9 +2280,12 @@ The server SHOULD return one of the following responses: Server administrators may apply locks to prevent users from usefully using their accounts, for instance, due to safety or security concerns. In contrast to account deactivation, locking is a non-destructive action -that can be reversed. This specification describes the behaviour of clients -and servers when an account is locked. It deliberately leaves the methods -for locking and unlocking accounts as a server implementation detail. +that can be reversed. + +{{% added-in v="1.18" %}} To lock or unlock an account, the administrators +SHOULD use the [`PUT /admin/lock/{userId}`](#post_matrixclientv1adminlockuserid) +endpoint. They MAY also use [`GET /admin/lock/{userId}`](#get_matrixclientv1adminlockuserid) +to check whether a user's account is locked. When an account is locked, servers MUST return a `401 Unauthorized` error response with an `M_USER_LOCKED` error code and [`soft_logout`](#soft-logout) @@ -2331,6 +2334,11 @@ from that account. The effect is similar to [locking](#account-locking), though without risk of the client losing state from a logout. Suspensions are reversible, like locks and unlike deactivations. +{{% added-in v="1.18" %}} To suspend or unsuspend an account, the administrators +SHOULD use the [`PUT /admin/suspend/{userId}`](#post_matrixclientv1adminsuspenduserid) +endpoint. They MAY also use [`GET /admin/suspend/{userId}`](#get_matrixclientv1adminsuspenduserid) +to check whether a user's account is suspended. + The actions a user can perform while suspended is deliberately left as an implementation detail. Servers SHOULD permit the user to perform at least the following, however: @@ -2386,9 +2394,6 @@ Content-Type: application/json } ``` -APIs for initiating suspension or unsuspension are not included in this version -of the specification, and left as an implementation detail. - ### Adding Account Administrative Contact Information A homeserver may keep some contact information for administrative use. diff --git a/data/api/client-server/admin.yaml b/data/api/client-server/admin.yaml index 19f084b7..1fca6dcd 100644 --- a/data/api/client-server/admin.yaml +++ b/data/api/client-server/admin.yaml @@ -16,7 +16,7 @@ info: title: Matrix Client-Server Administration API version: 1.0.0 paths: - "/admin/whois/{userId}": + "/v3/admin/whois/{userId}": get: summary: Gets information about a particular user. description: |- @@ -107,6 +107,391 @@ paths: } tags: - Server administration + "/v1/admin/suspend/{userId}": + get: + summary: Gets information about the suspended status of a particular user. + x-addedInMatrixVersion: "1.18" + description: |- + Gets information about the suspended status of a particular server-local user. + + The user calling this endpoint MUST be a server admin. + + In order to prevent user enumeration, servers MUST ensure that authorization is checked + prior to trying to do account lookups. + operationId: getAdminSuspendUser + security: + - accessTokenQuery: [] + - accessTokenBearer: [] + parameters: + - in: path + name: userId + description: The user to look up. + required: true + example: "@peter:rabbit.rocks" + schema: + type: string + format: mx-user-id + pattern: "^@" + + responses: + "200": + description: The lookup was successful. + content: + application/json: + schema: + type: object + properties: + suspended: + type: boolean + description: Whether the target account is suspended. + example: true + required: + - suspended + examples: + response: + value: { + "suspended": "true", + } + "400": + description: |- + The user ID does not belong to the local server. The errcode is `M_INVALID_PARAM`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_INVALID_PARAM", + "error": "User does not belong to the local server." + } + "403": + description: |- + The requesting user is not a server administrator, or the target user is another + administrator. The errcode is `M_FORBIDDEN`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_FORBIDDEN", + "error": "Requesting user is not a server administrator." + } + "404": + description: |- + The user ID is not found, or is deactivated. The errcode is `M_NOT_FOUND`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_NOT_FOUND", + "error": "User not found." + } + tags: + - Server administration + put: + summary: Set the suspended status of a particular user. + x-addedInMatrixVersion: "1.18" + description: |- + Sets the suspended status of a particular server-local user. + + The user calling this endpoint MUST be a server admin. The client SHOULD check that the user + is allowed to suspend other users at the [`GET /capabilities`](/client-server-api/#get_matrixclientv3capabilities) + endpoint prior to using this endpoint. + + In order to prevent user enumeration, servers MUST ensure that authorization is checked + prior to trying to do account lookups. + operationId: setAdminSuspendUser + security: + - accessTokenQuery: [] + - accessTokenBearer: [] + parameters: + - in: path + name: userId + description: The user to change the suspended status of. + required: true + example: "@peter:rabbit.rocks" + schema: + type: string + format: mx-user-id + pattern: "^@" + requestBody: + content: + application/json: + schema: + type: object + properties: + suspended: + type: boolean + description: Whether to suspend the target account. + example: true + required: + - suspended + examples: + request: + value: { + "suspended": "true", + } + required: true + + responses: + "200": + description: The action was successful. + content: + application/json: + schema: + type: object + properties: + suspended: + type: boolean + description: Whether the target account is suspended. + example: true + required: + - suspended + examples: + response: + value: { + "suspended": "true", + } + "400": + description: |- + The user ID does not belong to the local server. The errcode is `M_INVALID_PARAM`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_INVALID_PARAM", + "error": "User does not belong to the local server." + } + "403": + description: |- + The requesting user is not a server administrator, is trying to suspend their own + account, or the target user is another administrator. The errcode is `M_FORBIDDEN`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_FORBIDDEN", + "error": "Requesting user is not a server administrator." + } + "404": + description: |- + The user ID is not found, or is deactivated. The errcode is `M_NOT_FOUND`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_NOT_FOUND", + "error": "User not found." + } + tags: + - Server administration + "/v1/admin/lock/{userId}": + get: + summary: Gets information about the locked status of a particular user. + x-addedInMatrixVersion: "1.18" + description: |- + Gets information about the locked status of a particular server-local user. + + The user calling this endpoint MUST be a server admin. + + In order to prevent user enumeration, servers MUST ensure that authorization is checked + prior to trying to do account lookups. + operationId: getAdminLockUser + security: + - accessTokenQuery: [] + - accessTokenBearer: [] + parameters: + - in: path + name: userId + description: The user to look up. + required: true + example: "@peter:rabbit.rocks" + schema: + type: string + format: mx-user-id + pattern: "^@" + + responses: + "200": + description: The lookup was successful. + content: + application/json: + schema: + type: object + properties: + locked: + type: boolean + description: Whether the target account is locked. + required: + - locked + examples: + response: + value: { + "locked": "true", + } + "400": + description: |- + The user ID does not belong to the local server. The errcode is `M_INVALID_PARAM`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_INVALID_PARAM", + "error": "User does not belong to the local server." + } + "403": + description: |- + The requesting user is not a server administrator, or the target user is another + administrator. The errcode is `M_FORBIDDEN`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_FORBIDDEN", + "error": "Requesting user is not a server administrator." + } + "404": + description: |- + The user ID is not found, or is deactivated. The errcode is `M_NOT_FOUND`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_NOT_FOUND", + "error": "User not found." + } + tags: + - Server administration + put: + summary: Set the locked status of a particular user. + x-addedInMatrixVersion: "1.18" + description: |- + Sets the locked status of a particular server-local user. + + The user calling this endpoint MUST be a server admin. The client SHOULD check that the user + is allowed to lock other users at the [`GET /capabilities`](/client-server-api/#get_matrixclientv3capabilities) + endpoint prior to using this endpoint. + + In order to prevent user enumeration, servers MUST ensure that authorization is checked + prior to trying to do account lookups. + operationId: setAdminLockUser + security: + - accessTokenQuery: [] + - accessTokenBearer: [] + parameters: + - in: path + name: userId + description: The user to change the locked status of. + required: true + example: "@peter:rabbit.rocks" + schema: + type: string + format: mx-user-id + pattern: "^@" + requestBody: + content: + application/json: + schema: + type: object + properties: + locked: + type: boolean + description: Whether to lock the target account. + example: true + required: + - locked + examples: + request: + value: { + "locked": "true", + } + required: true + + responses: + "200": + description: The action was successful. + content: + application/json: + schema: + type: object + properties: + locked: + type: boolean + description: Whether the target account is locked. + example: true + required: + - locked + examples: + response: + value: { + "locked": "true", + } + "400": + description: |- + The user ID does not belong to the local server. The errcode is `M_INVALID_PARAM`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_INVALID_PARAM", + "error": "User does not belong to the local server." + } + "403": + description: |- + The requesting user is not a server administrator, is trying to lock their own + account, or the target user is another administrator. The errcode is `M_FORBIDDEN`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_FORBIDDEN", + "error": "Requesting user is not a server administrator." + } + "404": + description: |- + The user ID is not found, or is deactivated. The errcode is `M_NOT_FOUND`. + content: + application/json: + schema: + $ref: definitions/errors/error.yaml + examples: + response: + value: { + "errcode": "M_NOT_FOUND", + "error": "User not found." + } + tags: + - Server administration servers: - url: "{protocol}://{hostname}{basePath}" variables: @@ -118,7 +503,7 @@ servers: hostname: default: localhost:8008 basePath: - default: /_matrix/client/v3 + default: /_matrix/client components: securitySchemes: accessTokenQuery: diff --git a/data/api/client-server/capabilities.yaml b/data/api/client-server/capabilities.yaml index 2e8c8849..f9a23a83 100644 --- a/data/api/client-server/capabilities.yaml +++ b/data/api/client-server/capabilities.yaml @@ -78,7 +78,7 @@ paths: description: | **Deprecated:** Capability to indicate if the user can change their display name. Refer to `m.profile_fields` for extended profile management. - + For backwards compatibility, servers that directly or indirectly include the `displayname` profile field in the `m.profile_fields` capability MUST also set this capability accordingly. @@ -115,7 +115,7 @@ paths: description: | If present, a list of profile fields that clients are allowed to create, modify or delete, provided `enabled` is `true`; no other profile fields may be changed. - + If absent, clients may set all profile fields except those forbidden by the `disallowed` list, where present. items: @@ -127,7 +127,7 @@ paths: type: array description: | This property has no meaning if `allowed` is also specified. - + Otherwise, if present, a list of profile fields that clients are _not_ allowed to create, modify or delete. Provided `enabled` is `true`, clients MAY assume that they can set any profile field which is not included in this list. @@ -141,6 +141,34 @@ paths: example: true required: - enabled + m.account_moderation: + x-addedInMatrixVersion: "1.18" + type: object + title: AccountModerationCapability + description: |- + Capability to indicate if the user can perform account moderation actions + via [server administration](/client-server-api/#server-administration) + endpoints. + + This property should be omitted altogether if `suspend` and `lock` would + be `false`. + properties: + suspend: + type: boolean + description: |- + `true` if the user can suspend a user via [`PUT /admin/suspend/{userId}`](/client-server-api/#put_matrixclientv1adminsuspenduserid), + `false` otherwise. + + Defaults to `false`. + example: true + lock: + type: boolean + description: |- + `true` if the user can lock a user via [`PUT /admin/lock/{userId}`](/client-server-api/#put_matrixclientv1adminlockuserid), + `false` otherwise. + + Defaults to `false`. + example: true examples: response: value: { From bcd5f6bcfb164d1d64d0c2baf6884ac0628c2716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 19 Dec 2025 12:54:20 +0100 Subject: [PATCH 2/6] Add changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- changelogs/client_server/newsfragments/2278.feature | 1 + changelogs/client_server/newsfragments/2278.new.1 | 1 + changelogs/client_server/newsfragments/2278.new.2 | 1 + changelogs/client_server/newsfragments/2278.new.3 | 1 + changelogs/client_server/newsfragments/2278.new.4 | 1 + 5 files changed, 5 insertions(+) create mode 100644 changelogs/client_server/newsfragments/2278.feature create mode 100644 changelogs/client_server/newsfragments/2278.new.1 create mode 100644 changelogs/client_server/newsfragments/2278.new.2 create mode 100644 changelogs/client_server/newsfragments/2278.new.3 create mode 100644 changelogs/client_server/newsfragments/2278.new.4 diff --git a/changelogs/client_server/newsfragments/2278.feature b/changelogs/client_server/newsfragments/2278.feature new file mode 100644 index 00000000..ba906004 --- /dev/null +++ b/changelogs/client_server/newsfragments/2278.feature @@ -0,0 +1 @@ +Add `GET /_matrix/client/v1/admin/suspend/{userId}`, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). diff --git a/changelogs/client_server/newsfragments/2278.new.1 b/changelogs/client_server/newsfragments/2278.new.1 new file mode 100644 index 00000000..ba906004 --- /dev/null +++ b/changelogs/client_server/newsfragments/2278.new.1 @@ -0,0 +1 @@ +Add `GET /_matrix/client/v1/admin/suspend/{userId}`, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). diff --git a/changelogs/client_server/newsfragments/2278.new.2 b/changelogs/client_server/newsfragments/2278.new.2 new file mode 100644 index 00000000..e2d0e630 --- /dev/null +++ b/changelogs/client_server/newsfragments/2278.new.2 @@ -0,0 +1 @@ +Add endpoints to lock and suspend server-local users for administrations and add the `m.account_management` capability, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). diff --git a/changelogs/client_server/newsfragments/2278.new.3 b/changelogs/client_server/newsfragments/2278.new.3 new file mode 100644 index 00000000..4802ede7 --- /dev/null +++ b/changelogs/client_server/newsfragments/2278.new.3 @@ -0,0 +1 @@ +Add `GET /_matrix/client/v1/admin/lock/{userId}`, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). diff --git a/changelogs/client_server/newsfragments/2278.new.4 b/changelogs/client_server/newsfragments/2278.new.4 new file mode 100644 index 00000000..a5aa9ab6 --- /dev/null +++ b/changelogs/client_server/newsfragments/2278.new.4 @@ -0,0 +1 @@ +Add `PUT /_matrix/client/v1/admin/lock/{userId}`, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). From 147f8703d44e7e2d1488b7c0bd2c6f22f6bb2b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 19 Dec 2025 12:59:01 +0100 Subject: [PATCH 3/6] Fix fragments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- content/client-server-api/_index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index 7a4e04b3..d7a6399b 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -2283,7 +2283,7 @@ In contrast to account deactivation, locking is a non-destructive action that can be reversed. {{% added-in v="1.18" %}} To lock or unlock an account, the administrators -SHOULD use the [`PUT /admin/lock/{userId}`](#post_matrixclientv1adminlockuserid) +SHOULD use the [`PUT /admin/lock/{userId}`](#put_matrixclientv1adminlockuserid) endpoint. They MAY also use [`GET /admin/lock/{userId}`](#get_matrixclientv1adminlockuserid) to check whether a user's account is locked. @@ -2335,7 +2335,7 @@ without risk of the client losing state from a logout. Suspensions are reversibl like locks and unlike deactivations. {{% added-in v="1.18" %}} To suspend or unsuspend an account, the administrators -SHOULD use the [`PUT /admin/suspend/{userId}`](#post_matrixclientv1adminsuspenduserid) +SHOULD use the [`PUT /admin/suspend/{userId}`](#put_matrixclientv1adminsuspenduserid) endpoint. They MAY also use [`GET /admin/suspend/{userId}`](#get_matrixclientv1adminsuspenduserid) to check whether a user's account is suspended. From 19bf443d0ec317bed2b7cfcae778cc5105cbd62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 19 Dec 2025 13:03:03 +0100 Subject: [PATCH 4/6] Fix examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- data/api/client-server/admin.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data/api/client-server/admin.yaml b/data/api/client-server/admin.yaml index 1fca6dcd..b204b9d4 100644 --- a/data/api/client-server/admin.yaml +++ b/data/api/client-server/admin.yaml @@ -150,7 +150,7 @@ paths: examples: response: value: { - "suspended": "true", + "suspended": true, } "400": description: |- @@ -235,7 +235,7 @@ paths: examples: request: value: { - "suspended": "true", + "suspended": true, } required: true @@ -256,7 +256,7 @@ paths: examples: response: value: { - "suspended": "true", + "suspended": true, } "400": description: |- @@ -342,7 +342,7 @@ paths: examples: response: value: { - "locked": "true", + "locked": true, } "400": description: |- @@ -427,7 +427,7 @@ paths: examples: request: value: { - "locked": "true", + "locked": true, } required: true @@ -448,7 +448,7 @@ paths: examples: response: value: { - "locked": "true", + "locked": true, } "400": description: |- From c98c03b32c2fd3ad09f39012ade750cf8bac7d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 19 Dec 2025 13:06:50 +0100 Subject: [PATCH 5/6] Fix changelogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- changelogs/client_server/newsfragments/2278.feature | 2 +- changelogs/client_server/newsfragments/2278.new.2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelogs/client_server/newsfragments/2278.feature b/changelogs/client_server/newsfragments/2278.feature index ba906004..e2d0e630 100644 --- a/changelogs/client_server/newsfragments/2278.feature +++ b/changelogs/client_server/newsfragments/2278.feature @@ -1 +1 @@ -Add `GET /_matrix/client/v1/admin/suspend/{userId}`, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). +Add endpoints to lock and suspend server-local users for administrations and add the `m.account_management` capability, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). diff --git a/changelogs/client_server/newsfragments/2278.new.2 b/changelogs/client_server/newsfragments/2278.new.2 index e2d0e630..ba906004 100644 --- a/changelogs/client_server/newsfragments/2278.new.2 +++ b/changelogs/client_server/newsfragments/2278.new.2 @@ -1 +1 @@ -Add endpoints to lock and suspend server-local users for administrations and add the `m.account_management` capability, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). +Add `GET /_matrix/client/v1/admin/suspend/{userId}`, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). From f95dcfb0e79873de2ed8e1f3236351b70fc1871e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 19 Dec 2025 13:09:53 +0100 Subject: [PATCH 6/6] Fix changelog (again) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Commaille --- changelogs/client_server/newsfragments/2278.new.2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/client_server/newsfragments/2278.new.2 b/changelogs/client_server/newsfragments/2278.new.2 index ba906004..c029e44d 100644 --- a/changelogs/client_server/newsfragments/2278.new.2 +++ b/changelogs/client_server/newsfragments/2278.new.2 @@ -1 +1 @@ -Add `GET /_matrix/client/v1/admin/suspend/{userId}`, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323). +Add `PUT /_matrix/client/v1/admin/suspend/{userId}`, as per [MSC4323](https://github.com/matrix-org/matrix-spec-proposals/pull/4323).