element-hq / element-hq/synapse
Inconsistent behavior for client profile endpoints for legacy fields
- Dominant language
- Python
- Stars
- 4.6k
- Forks
- 600
- Avg merge
- 5d 22h
- Merged PRs (30d)
- 51
Description
### Description
The behavior is inconsistent for the `displayname` and `avatar_url` profile fields between the `GET /_matrix/client/v3/profile/{userId}`, `PUT /_matrix/client/v3/profile/{userId}/{keyName}` endpoints on one side, and the `GET /_matrix/client/v3/profile/{userId}/{keyName}` endpoint on the other side. The latter also doesn't seem to be spec compliant.
This is what I observed, using the `displayname` field as an example and comparing with the [Matrix 1.17 spec](https://spec.matrix.org/v1.17/client-server-api/#profiles):
1. `GET /_matrix/client/v3/profile/{userId}` returns an `HTTP 200` response when at least one field is set and an `HTTP 404` response with an `M_NOT_FOUND` error code when none of the fields are set, which matches the definition in the spec.
2. `PUT /_matrix/client/v3/profile/{userId}/displayname` requires the `displayname` field to be present and a string, which matches the definition in the spec. Setting the value to an empty string appears to unset the field, which seems like an opinionated behavior, since it is not defined in the spec.
3. `GET /_matrix/client/v3/profile/{userId}/displayname` always returns an `HTTP 200` response, whether the `displayname` is set or not. If it is not set, the value of the field is `null`, which is inconsistent with the two other endpoints. If it is not possible to set a `null` value with the `PUT` endpoint, how can it be possible to receive it? According to the spec, a missing field should return an `HTTP 404` response like the `GET /_matrix/client/v3/profile/{userId}` endpoint.
Note that this doesn't apply to other profile fields like `m.tz`: those are allowed to be set to `null` or an empty string, and those "invalid" values appear in the responses of both `GET` endpoints. Only using the `DELETE` endpoint actually unsets the field, and when the field is unset both `GET` endpoints return an `HTTP 404` response.
### Steps to reproduce
The following commands can be used to test this behavior on a local deployment listening on port `8008`, using an existing `@user1:matrix.local` account with a valid access token set into the `MATRIX_ACCESS_TOKEN` environment variable, and starting with an empty profile.
#### 1. Set a non-empty display name
```sh
$ curl -i -X PUT "http://localhost:8008/_matrix/client/v3/profile/%40user1%3Amatrix.local/displayname" -H "Accept: application/json" -H "Authorization: Bearer $MATRIX_ACCESS_TOKEN" -H "Content-Type: application/json" -d '{"displayname":"Alice"}'
HTTP/1.1 200 OK
[… other headers]
{}
```
Check that the field is set:
```sh
$ curl -i -X GET "http://localhost:8008/_matrix/client/v3/profile/%40user1%3Amatrix.local/displayname" -H "Accept: application/json" -H "Authorization: Bearer $MATRIX_ACCESS_TOKEN"
HTTP/1.1 200 OK
[… other headers]
{"displayname":"Alice"}
```
Check that the profile is not empty:
```sh
$ curl -i -X GET "http://localhost:8008/_matrix/client/v3/profile/%40user1%3Amatrix.local" -H "Accept: application/json" -H "Authorization: Bearer $MATRIX_ACCESS_TOKEN"
HTTP/1.1 200 OK
[… other headers]
{"displayname":"Alice"}
```
#### 2. Unsuccessful attempts to unset the display name
Try to omit the field:
```sh
$ curl -i -X PUT "http://localhost:8008/_matrix/client/v3/profile/%40user1%3Amatrix.local/displayname" -H "Accept: application/json" -H "Authorization: Bearer $MATRIX_ACCESS_TOKEN" -H "Content-Type: application/json" -d '{}'
HTTP/1.1 400 Bad Request
[… other headers]
{"errcode":"M_MISSING_PARAM","error":"Missing key 'displayname'"}
```
Try to set the value to `null`:
```sh
$ curl -i -X PUT "http://localhost:8008/_matrix/client/v3/profile/%40user1%3Amatrix.local/displayname" -H "Accept: application/json" -H "Authorization: Bearer $MATRIX_ACCESS_TOKEN" -H "Content-Type: application/json" -d '{"displayname":null}'
HTTP/1.1 400 Bad Request
[… other headers]
{"errcode":"M_INVALID_PARAM","error":"'displayname' must be a string"}
```
#### 2. Successful attempts to unset the display name
Send an empty string:
```sh
$ curl -i -X PUT "http://localhost:8008/_matrix/client/v3/profile/%40user1%3Amatrix.local/displayname" -H "Accept: application/json" -H "Authorization: Bearer $MATRIX_ACCESS_TOKEN" -H "Content-Type: application/json" -d '{"displayname":""}'
HTTP/1.1 200 OK
[… other headers]
{}
```
or use the `DELETE` endpoint:
```sh
$ curl -i -X DELETE "http://localhost:8008/_matrix/client/v3/profile/%40user1%3Amatrix.local/displayname" -H "Accept: application/json" -H "Authorization: Bearer $MATRIX_ACCESS_TOKEN"
HTTP/1.1 200 OK
[… other headers]
{}
```
Check that the field is unset:
```sh
$ curl -i -X GET "http://localhost:8008/_matrix/client/v3/profile/%40user1%3Amatrix.local/displayname" -H "Accept: application/json" -H "Authorization: Bearer $MATRIX_ACCESS_TOKEN"
HTTP/1.1 200 OK
[… other headers]
{"displayname":null}
```
Check that the profile is empty:
```sh
$ curl -i -X GET "http://localhost:8008/_matrix/client/v3/profile/%40user1%3Amatrix.local" -H "Accept: application/json" -H "Authorization: Bearer $MATRIX_ACCESS_TOKEN"
HTTP/1.1 404 Not Found
[… other headers]
{"errcode":"M_NOT_FOUND","error":"Profile was not found"}
```
### Homeserver
Local deployment / any Synapse homeserver
### Synapse Version
1.147.1
### Installation Method
Docker (matrixdotorg/synapse)
### Database
SQLite
### Workers
Single process
### Platform
N/A
### Configuration
_No response_
### Relevant log output
```shell
N/A
```
### Anything else that would be useful to know?
_No response_
Contributor guide
Research direction
Reproduce the behavior with the listed curl commands against the profile endpoints, starting with GET /_matrix/client/v3/profile/{userId}/{keyName} for an unset displayname. Compare its response with GET /_matrix/client/v3/profile/{userId} and the Matrix 1.17 profiles specification; done means the legacy field endpoints agree on unset-field behavior and match the specification.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100