Compare commits
9 Commits
rei/docker
...
v1.52.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cdd491310 | ||
|
|
7d56b6c083 | ||
|
|
b7282fe7d1 | ||
|
|
a35e9db9be | ||
|
|
901b264c0c | ||
|
|
0da2301b21 | ||
|
|
02755c3188 | ||
|
|
7eb198ddc8 | ||
|
|
bf60da1a60 |
73
CHANGES.md
73
CHANGES.md
@@ -1,3 +1,76 @@
|
||||
Synapse 1.52.0 (2022-02-08)
|
||||
===========================
|
||||
|
||||
No significant changes since 1.52.0rc1.
|
||||
|
||||
During the making of this release, the developers of Twisted have released
|
||||
[Twisted 22.1.0](https://github.com/twisted/twisted/releases/tag/twisted-22.1.0), which
|
||||
fixes [a security issue](https://github.com/twisted/twisted/security/advisories/GHSA-92x2-jw7w-xvvx)
|
||||
within Twisted. We do not believe Synapse to be vulnerable to any security problem caused
|
||||
by this issue, though we advise server administrators to update their local version of
|
||||
Twisted if they can.
|
||||
|
||||
|
||||
Synapse 1.52.0rc1 (2022-02-01)
|
||||
==============================
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Remove account data (including client config, push rules and ignored users) upon user deactivation. ([\#11621](https://github.com/matrix-org/synapse/issues/11621), [\#11788](https://github.com/matrix-org/synapse/issues/11788), [\#11789](https://github.com/matrix-org/synapse/issues/11789))
|
||||
- Add an admin API to reset connection timeouts for remote server. ([\#11639](https://github.com/matrix-org/synapse/issues/11639))
|
||||
- Add an admin API to get a list of rooms that federate with a given remote homeserver. ([\#11658](https://github.com/matrix-org/synapse/issues/11658))
|
||||
- Add a config flag to inhibit `M_USER_IN_USE` during registration. ([\#11743](https://github.com/matrix-org/synapse/issues/11743))
|
||||
- Add a module callback to set username at registration. ([\#11790](https://github.com/matrix-org/synapse/issues/11790))
|
||||
- Allow configuring a maximum file size as well as a list of allowed content types for avatars. ([\#11846](https://github.com/matrix-org/synapse/issues/11846))
|
||||
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Include the bundled aggregations in the `/sync` response, per [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675). ([\#11612](https://github.com/matrix-org/synapse/issues/11612))
|
||||
- Fix a long-standing bug when previewing Reddit URLs which do not contain an image. ([\#11767](https://github.com/matrix-org/synapse/issues/11767))
|
||||
- Fix a long-standing bug that media streams could cause long-lived connections when generating URL previews. ([\#11784](https://github.com/matrix-org/synapse/issues/11784))
|
||||
- Include a `prev_content` field in state events sent to Application Services. Contributed by @totallynotvaishnav. ([\#11798](https://github.com/matrix-org/synapse/issues/11798))
|
||||
- Fix a bug introduced in Synapse 0.33.3 causing requests to sometimes log strings such as `HTTPStatus.OK` instead of integer status codes. ([\#11827](https://github.com/matrix-org/synapse/issues/11827))
|
||||
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- Update pypi installation docs to indicate that we now support Python 3.10. ([\#11820](https://github.com/matrix-org/synapse/issues/11820))
|
||||
- Add missing steps to the contribution submission process in the documentation. Contributed by @sequentialread. ([\#11821](https://github.com/matrix-org/synapse/issues/11821))
|
||||
- Remove not needed old table of contents in documentation. ([\#11860](https://github.com/matrix-org/synapse/issues/11860))
|
||||
- Consolidate the `access_token` information at the top of each relevant page in the Admin API documentation. ([\#11861](https://github.com/matrix-org/synapse/issues/11861))
|
||||
|
||||
|
||||
Deprecations and Removals
|
||||
-------------------------
|
||||
|
||||
- Drop support for Python 3.6, which is EOL. ([\#11683](https://github.com/matrix-org/synapse/issues/11683))
|
||||
- Remove the `experimental_msc1849_support_enabled` flag as the features are now stable. ([\#11843](https://github.com/matrix-org/synapse/issues/11843))
|
||||
|
||||
|
||||
Internal Changes
|
||||
----------------
|
||||
|
||||
- Preparation for database schema simplifications: add `state_key` and `rejection_reason` columns to `events` table. ([\#11792](https://github.com/matrix-org/synapse/issues/11792))
|
||||
- Add `FrozenEvent.get_state_key` and use it in a couple of places. ([\#11793](https://github.com/matrix-org/synapse/issues/11793))
|
||||
- Preparation for database schema simplifications: stop reading from `event_reference_hashes`. ([\#11794](https://github.com/matrix-org/synapse/issues/11794))
|
||||
- Drop unused table `public_room_list_stream`. ([\#11795](https://github.com/matrix-org/synapse/issues/11795))
|
||||
- Preparation for reducing Postgres serialization errors: allow setting transaction isolation level. Contributed by Nick @ Beeper. ([\#11799](https://github.com/matrix-org/synapse/issues/11799), [\#11847](https://github.com/matrix-org/synapse/issues/11847))
|
||||
- Docker: skip the initial amd64-only build and go straight to multiarch. ([\#11810](https://github.com/matrix-org/synapse/issues/11810))
|
||||
- Run Complement on the Github Actions VM and not inside a Docker container. ([\#11811](https://github.com/matrix-org/synapse/issues/11811))
|
||||
- Log module names at startup. ([\#11813](https://github.com/matrix-org/synapse/issues/11813))
|
||||
- Improve type safety of bundled aggregations code. ([\#11815](https://github.com/matrix-org/synapse/issues/11815))
|
||||
- Correct a type annotation in the event validation logic. ([\#11817](https://github.com/matrix-org/synapse/issues/11817), [\#11830](https://github.com/matrix-org/synapse/issues/11830))
|
||||
- Minor updates and documentation for database schema delta files. ([\#11823](https://github.com/matrix-org/synapse/issues/11823))
|
||||
- Workaround a type annotation problem in `prometheus_client` 0.13.0. ([\#11834](https://github.com/matrix-org/synapse/issues/11834))
|
||||
- Minor performance improvement in room state lookup. ([\#11836](https://github.com/matrix-org/synapse/issues/11836))
|
||||
- Fix some indentation inconsistencies in the sample config. ([\#11838](https://github.com/matrix-org/synapse/issues/11838))
|
||||
- Add type hints to `tests/rest/admin`. ([\#11851](https://github.com/matrix-org/synapse/issues/11851))
|
||||
|
||||
|
||||
Synapse 1.51.0 (2022-01-25)
|
||||
===========================
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Include the bundled aggregations in the `/sync` response, per [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675).
|
||||
@@ -1 +0,0 @@
|
||||
Remove account data (including client config, push rules and ignored users) upon user deactivation.
|
||||
@@ -1 +0,0 @@
|
||||
Add admin API to reset connection timeouts for remote server.
|
||||
@@ -1 +0,0 @@
|
||||
Add an admin API to get a list of rooms that federate with a given remote homeserver.
|
||||
@@ -1 +0,0 @@
|
||||
Drop support for Python 3.6, which is EOL.
|
||||
@@ -1 +0,0 @@
|
||||
Add a config flag to inhibit M_USER_IN_USE during registration.
|
||||
@@ -1 +0,0 @@
|
||||
Fix a long-standing bug when previewing Reddit URLs which do not contain an image.
|
||||
@@ -1 +0,0 @@
|
||||
Fix a long-standing bug that media streams could cause long-lived connections when generating URL previews.
|
||||
@@ -1 +0,0 @@
|
||||
Remove account data (including client config, push rules and ignored users) upon user deactivation.
|
||||
@@ -1 +0,0 @@
|
||||
Remove account data (including client config, push rules and ignored users) upon user deactivation.
|
||||
@@ -1 +0,0 @@
|
||||
Add a module callback to set username at registration.
|
||||
@@ -1 +0,0 @@
|
||||
Preparation for database schema simplifications: add `state_key` and `rejection_reason` columns to `events` table.
|
||||
@@ -1 +0,0 @@
|
||||
Add `FrozenEvent.get_state_key` and use it in a couple of places.
|
||||
@@ -1 +0,0 @@
|
||||
Preparation for database schema simplifications: stop reading from `event_reference_hashes`.
|
||||
@@ -1 +0,0 @@
|
||||
Drop unused table `public_room_list_stream`.
|
||||
@@ -1 +0,0 @@
|
||||
Include a `prev_content` field in state events sent to Application Services. Contributed by @totallynotvaishnav.
|
||||
@@ -1 +0,0 @@
|
||||
Preparation for reducing Postgres serialization errors: allow setting transaction isolation level. Contributed by Nick @ Beeper.
|
||||
@@ -1 +0,0 @@
|
||||
Docker: skip the initial amd64-only build and go straight to multiarch.
|
||||
@@ -1 +0,0 @@
|
||||
Run Complement on the Github Actions VM and not inside a Docker container.
|
||||
@@ -1 +0,0 @@
|
||||
Log module names at startup.
|
||||
@@ -1 +0,0 @@
|
||||
Improve type safety of bundled aggregations code.
|
||||
@@ -1 +0,0 @@
|
||||
Drop support for Python 3.6, which is EOL.
|
||||
@@ -1 +0,0 @@
|
||||
Correct a type annotation in the event validation logic.
|
||||
@@ -1 +0,0 @@
|
||||
Update pypi installation docs to indicate that we now support Python 3.10.
|
||||
@@ -1 +0,0 @@
|
||||
Add missing steps to the contribution submission process in the documentation. Contributed by @sequentialread.
|
||||
@@ -1 +0,0 @@
|
||||
Minor updates and documentation for database schema delta files.
|
||||
@@ -1 +0,0 @@
|
||||
Fix a bug introduced in Synapse 0.33.3 causing requests to sometimes log strings such as `HTTPStatus.OK` instead of integer status codes.
|
||||
@@ -1 +0,0 @@
|
||||
Correct a type annotation in the event validation logic.
|
||||
@@ -1 +0,0 @@
|
||||
Workaround a type annotation problem in `prometheus_client` 0.13.0.
|
||||
@@ -1 +0,0 @@
|
||||
Minor performance improvement in room state lookup.
|
||||
@@ -1 +0,0 @@
|
||||
Fix some indentation inconsistencies in the sample config.
|
||||
@@ -1 +0,0 @@
|
||||
Preparation for reducing Postgres serialization errors: allow setting transaction isolation level. Contributed by Nick @ Beeper.
|
||||
12
debian/changelog
vendored
12
debian/changelog
vendored
@@ -1,3 +1,15 @@
|
||||
matrix-synapse-py3 (1.52.0) stable; urgency=medium
|
||||
|
||||
* New synapse release 1.52.0.
|
||||
|
||||
-- Synapse Packaging team <packages@matrix.org> Tue, 08 Feb 2022 11:34:54 +0000
|
||||
|
||||
matrix-synapse-py3 (1.52.0~rc1) stable; urgency=medium
|
||||
|
||||
* New synapse release 1.52.0~rc1.
|
||||
|
||||
-- Synapse Packaging team <packages@matrix.org> Tue, 01 Feb 2022 11:04:09 +0000
|
||||
|
||||
matrix-synapse-py3 (1.51.0) stable; urgency=medium
|
||||
|
||||
* New synapse release 1.51.0.
|
||||
|
||||
@@ -44,27 +44,6 @@ For more details and context on the release of the r0.1 Server/Server API and
|
||||
imminent Matrix 1.0 release, you can also see our
|
||||
[main talk from FOSDEM 2019](https://matrix.org/blog/2019/02/04/matrix-at-fosdem-2019/).
|
||||
|
||||
## Contents
|
||||
* Timeline
|
||||
* Configuring certificates for compatibility with Synapse 1.0
|
||||
* FAQ
|
||||
* Synapse 0.99.0 has just been released, what do I need to do right now?
|
||||
* How do I upgrade?
|
||||
* What will happen if I do not set up a valid federation certificate
|
||||
immediately?
|
||||
* What will happen if I do nothing at all?
|
||||
* When do I need a SRV record or .well-known URI?
|
||||
* Can I still use an SRV record?
|
||||
* I have created a .well-known URI. Do I still need an SRV record?
|
||||
* It used to work just fine, why are you breaking everything?
|
||||
* Can I manage my own certificates rather than having Synapse renew
|
||||
certificates itself?
|
||||
* Do you still recommend against using a reverse proxy on the federation port?
|
||||
* Do I still need to give my TLS certificates to Synapse if I am using a
|
||||
reverse proxy?
|
||||
* Do I need the same certificate for the client and federation port?
|
||||
* How do I tell Synapse to reload my keys/certificates after I replace them?
|
||||
|
||||
## Timeline
|
||||
|
||||
**5th Feb 2019 - Synapse 0.99.0 is released.**
|
||||
|
||||
@@ -4,6 +4,9 @@ This API allows a server administrator to manage the validity of an account. To
|
||||
use it, you must enable the account validity feature (under
|
||||
`account_validity`) in Synapse's configuration.
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
## Renew account
|
||||
|
||||
This API extends the validity of an account by as much time as configured in the
|
||||
|
||||
@@ -4,11 +4,11 @@ This API lets a server admin delete a local group. Doing so will kick all
|
||||
users out of the group so that their clients will correctly handle the group
|
||||
being deleted.
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
The API is:
|
||||
|
||||
```
|
||||
POST /_synapse/admin/v1/delete_group/<group_id>
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
This API returns information about reported events.
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
The api is:
|
||||
```
|
||||
GET /_synapse/admin/v1/event_reports?from=0&limit=10
|
||||
```
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
It returns a JSON body like the following:
|
||||
|
||||
@@ -94,8 +95,6 @@ The api is:
|
||||
```
|
||||
GET /_synapse/admin/v1/event_reports/<report_id>
|
||||
```
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
It returns a JSON body like the following:
|
||||
|
||||
|
||||
@@ -1,24 +1,10 @@
|
||||
# Contents
|
||||
- [Querying media](#querying-media)
|
||||
* [List all media in a room](#list-all-media-in-a-room)
|
||||
* [List all media uploaded by a user](#list-all-media-uploaded-by-a-user)
|
||||
- [Quarantine media](#quarantine-media)
|
||||
* [Quarantining media by ID](#quarantining-media-by-id)
|
||||
* [Remove media from quarantine by ID](#remove-media-from-quarantine-by-id)
|
||||
* [Quarantining media in a room](#quarantining-media-in-a-room)
|
||||
* [Quarantining all media of a user](#quarantining-all-media-of-a-user)
|
||||
* [Protecting media from being quarantined](#protecting-media-from-being-quarantined)
|
||||
* [Unprotecting media from being quarantined](#unprotecting-media-from-being-quarantined)
|
||||
- [Delete local media](#delete-local-media)
|
||||
* [Delete a specific local media](#delete-a-specific-local-media)
|
||||
* [Delete local media by date or size](#delete-local-media-by-date-or-size)
|
||||
* [Delete media uploaded by a user](#delete-media-uploaded-by-a-user)
|
||||
- [Purge Remote Media API](#purge-remote-media-api)
|
||||
|
||||
# Querying media
|
||||
|
||||
These APIs allow extracting media information from the homeserver.
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
## List all media in a room
|
||||
|
||||
This API gets a list of known media in a room.
|
||||
@@ -28,8 +14,6 @@ The API is:
|
||||
```
|
||||
GET /_synapse/admin/v1/room/<room_id>/media
|
||||
```
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
The API returns a JSON body like the following:
|
||||
```json
|
||||
@@ -317,8 +301,5 @@ The following fields are returned in the JSON response body:
|
||||
|
||||
* `deleted`: integer - The number of media items successfully deleted
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
If the user re-requests purged remote media, synapse will re-request the media
|
||||
from the originating server.
|
||||
|
||||
@@ -10,15 +10,15 @@ paginate further back in the room from the point being purged from.
|
||||
Note that Synapse requires at least one message in each room, so it will never
|
||||
delete the last message in a room.
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
The API is:
|
||||
|
||||
```
|
||||
POST /_synapse/admin/v1/purge_history/<room_id>[/<event_id>]
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
By default, events sent by local users are not deleted, as they may represent
|
||||
the only copies of this content in existence. (Events sent by remote users are
|
||||
deleted.)
|
||||
@@ -57,9 +57,6 @@ It is possible to poll for updates on recent purges with a second API;
|
||||
GET /_synapse/admin/v1/purge_history_status/<purge_id>
|
||||
```
|
||||
|
||||
Again, you will need to authenticate by providing an `access_token` for a
|
||||
server admin.
|
||||
|
||||
This API returns a JSON body like the following:
|
||||
|
||||
```json
|
||||
|
||||
@@ -5,6 +5,9 @@ to a room with a given `room_id_or_alias`. You can only modify the membership of
|
||||
local users. The server administrator must be in the room and have permission to
|
||||
invite users.
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
## Parameters
|
||||
|
||||
The following parameters are available:
|
||||
@@ -23,9 +26,6 @@ POST /_synapse/admin/v1/join/<room_id_or_alias>
|
||||
}
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
# Contents
|
||||
- [List Room API](#list-room-api)
|
||||
- [Room Details API](#room-details-api)
|
||||
- [Room Members API](#room-members-api)
|
||||
- [Room State API](#room-state-api)
|
||||
- [Block Room API](#block-room-api)
|
||||
- [Delete Room API](#delete-room-api)
|
||||
* [Version 1 (old version)](#version-1-old-version)
|
||||
* [Version 2 (new version)](#version-2-new-version)
|
||||
* [Status of deleting rooms](#status-of-deleting-rooms)
|
||||
* [Undoing room shutdowns](#undoing-room-shutdowns)
|
||||
- [Make Room Admin API](#make-room-admin-api)
|
||||
- [Forward Extremities Admin API](#forward-extremities-admin-api)
|
||||
- [Event Context API](#event-context-api)
|
||||
|
||||
# List Room API
|
||||
|
||||
The List Room admin API allows server admins to get a list of rooms on their
|
||||
server. There are various parameters available that allow for filtering and
|
||||
sorting the returned list. This API supports pagination.
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
**Parameters**
|
||||
|
||||
The following query parameters are available:
|
||||
@@ -493,9 +481,6 @@ several minutes or longer.
|
||||
The local server will only have the power to move local user and room aliases to
|
||||
the new room. Users on other servers will be unaffected.
|
||||
|
||||
To use it, you will need to authenticate by providing an ``access_token`` for a
|
||||
server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
## Version 1 (old version)
|
||||
|
||||
This version works synchronously. That means you only get the response once the server has
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
Returns information about all local media usage of users. Gives the
|
||||
possibility to filter them by time and user.
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
The API is:
|
||||
|
||||
```
|
||||
GET /_synapse/admin/v1/statistics/users/media
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# User Admin API
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token`
|
||||
for a server admin: see [Admin API](../usage/administration/admin_api).
|
||||
|
||||
## Query User Account
|
||||
|
||||
This API returns information about a specific user account.
|
||||
@@ -10,9 +13,6 @@ The api is:
|
||||
GET /_synapse/admin/v2/users/<user_id>
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
It returns a JSON body like the following:
|
||||
|
||||
```jsonc
|
||||
@@ -104,9 +104,6 @@ with a body of:
|
||||
}
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
Returns HTTP status code:
|
||||
- `201` - When a new user object was created.
|
||||
- `200` - When a user was modified.
|
||||
@@ -156,9 +153,6 @@ By default, the response is ordered by ascending user ID.
|
||||
GET /_synapse/admin/v2/users?from=0&limit=10&guests=false
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -278,9 +272,6 @@ GET /_matrix/client/r0/admin/whois/<userId>
|
||||
See also: [Client Server
|
||||
API Whois](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-admin-whois-userid).
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
It returns a JSON body like the following:
|
||||
|
||||
```json
|
||||
@@ -335,9 +326,6 @@ with a body of:
|
||||
}
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
The erase parameter is optional and defaults to `false`.
|
||||
An empty body may be passed for backwards compatibility.
|
||||
|
||||
@@ -394,9 +382,6 @@ with a body of:
|
||||
}
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
The parameter `new_password` is required.
|
||||
The parameter `logout_devices` is optional and defaults to `true`.
|
||||
|
||||
@@ -409,9 +394,6 @@ The api is:
|
||||
GET /_synapse/admin/v1/users/<user_id>/admin
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -439,10 +421,6 @@ with a body of:
|
||||
}
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
|
||||
## List room memberships of a user
|
||||
|
||||
Gets a list of all `room_id` that a specific `user_id` is member.
|
||||
@@ -453,9 +431,6 @@ The API is:
|
||||
GET /_synapse/admin/v1/users/<user_id>/joined_rooms
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -574,9 +549,6 @@ The API is:
|
||||
GET /_synapse/admin/v1/users/<user_id>/media
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -691,9 +663,6 @@ The API is:
|
||||
DELETE /_synapse/admin/v1/users/<user_id>/media
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -766,9 +735,6 @@ The API is:
|
||||
GET /_synapse/admin/v2/users/<user_id>/devices
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -834,9 +800,6 @@ POST /_synapse/admin/v2/users/<user_id>/delete_devices
|
||||
}
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
An empty JSON dict is returned.
|
||||
|
||||
**Parameters**
|
||||
@@ -858,9 +821,6 @@ The API is:
|
||||
GET /_synapse/admin/v2/users/<user_id>/devices/<device_id>
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -906,9 +866,6 @@ PUT /_synapse/admin/v2/users/<user_id>/devices/<device_id>
|
||||
}
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
An empty JSON dict is returned.
|
||||
|
||||
**Parameters**
|
||||
@@ -935,9 +892,6 @@ DELETE /_synapse/admin/v2/users/<user_id>/devices/<device_id>
|
||||
{}
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
An empty JSON dict is returned.
|
||||
|
||||
**Parameters**
|
||||
@@ -956,9 +910,6 @@ The API is:
|
||||
GET /_synapse/admin/v1/users/<user_id>/pushers
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -1053,9 +1004,6 @@ To un-shadow-ban a user the API is:
|
||||
DELETE /_synapse/admin/v1/users/<user_id>/shadow_ban
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
An empty JSON dict is returned in both cases.
|
||||
|
||||
**Parameters**
|
||||
@@ -1078,9 +1026,6 @@ The API is:
|
||||
GET /_synapse/admin/v1/users/<user_id>/override_ratelimit
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -1120,9 +1065,6 @@ The API is:
|
||||
POST /_synapse/admin/v1/users/<user_id>/override_ratelimit
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
A response body like the following is returned:
|
||||
|
||||
```json
|
||||
@@ -1165,9 +1107,6 @@ The API is:
|
||||
DELETE /_synapse/admin/v1/users/<user_id>/override_ratelimit
|
||||
```
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
|
||||
An empty JSON dict is returned.
|
||||
|
||||
```json
|
||||
@@ -1196,7 +1135,5 @@ The API is:
|
||||
GET /_synapse/admin/v1/username_available?username=$localpart
|
||||
```
|
||||
|
||||
The request and response format is the same as the [/_matrix/client/r0/register/available](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available) API.
|
||||
|
||||
To use it, you will need to authenticate by providing an `access_token` for a
|
||||
server admin: [Admin API](../usage/administration/admin_api)
|
||||
The request and response format is the same as the
|
||||
[/_matrix/client/r0/register/available](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available) API.
|
||||
|
||||
@@ -471,6 +471,20 @@ limit_remote_rooms:
|
||||
#
|
||||
#allow_per_room_profiles: false
|
||||
|
||||
# The largest allowed file size for a user avatar. Defaults to no restriction.
|
||||
#
|
||||
# Note that user avatar changes will not work if this is set without
|
||||
# using Synapse's media repository.
|
||||
#
|
||||
#max_avatar_size: 10M
|
||||
|
||||
# The MIME types allowed for user avatars. Defaults to no restriction.
|
||||
#
|
||||
# Note that user avatar changes will not work if this is set without
|
||||
# using Synapse's media repository.
|
||||
#
|
||||
#allowed_avatar_mimetypes: ["image/png", "image/jpeg", "image/gif"]
|
||||
|
||||
# How long to keep redacted events in unredacted form in the database. After
|
||||
# this period redacted events get replaced with their redacted form in the DB.
|
||||
#
|
||||
|
||||
@@ -85,6 +85,17 @@ process, for example:
|
||||
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
||||
```
|
||||
|
||||
# Upgrading to v1.52.0
|
||||
|
||||
## Twisted security release
|
||||
|
||||
During the making of this release, the developers of Twisted have released
|
||||
[Twisted 22.1.0](https://github.com/twisted/twisted/releases/tag/twisted-22.1.0), which
|
||||
fixes [a security issue](https://github.com/twisted/twisted/security/advisories/GHSA-92x2-jw7w-xvvx)
|
||||
within Twisted. We do not believe Synapse to be vulnerable to any security problem caused
|
||||
by this issue, though we advise server administrators to update their local version of
|
||||
Twisted if they can.
|
||||
|
||||
# Upgrading to v1.51.0
|
||||
|
||||
## Deprecation of `webclient` listeners and non-HTTP(S) `web_client_location`
|
||||
|
||||
3
mypy.ini
3
mypy.ini
@@ -77,9 +77,6 @@ exclude = (?x)
|
||||
|tests/push/test_http.py
|
||||
|tests/push/test_presentable_names.py
|
||||
|tests/push/test_push_rule_evaluator.py
|
||||
|tests/rest/admin/test_admin.py
|
||||
|tests/rest/admin/test_user.py
|
||||
|tests/rest/admin/test_username_available.py
|
||||
|tests/rest/client/test_account.py
|
||||
|tests/rest/client/test_events.py
|
||||
|tests/rest/client/test_filter.py
|
||||
|
||||
@@ -47,7 +47,7 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
__version__ = "1.51.0"
|
||||
__version__ = "1.52.0"
|
||||
|
||||
if bool(os.environ.get("SYNAPSE_TEST_PATCH_LOG_CONTEXTS", False)):
|
||||
# We import here so that we don't have to install a bunch of deps when
|
||||
|
||||
@@ -24,8 +24,6 @@ class ExperimentalConfig(Config):
|
||||
def read_config(self, config: JsonDict, **kwargs):
|
||||
experimental = config.get("experimental_features") or {}
|
||||
|
||||
# Whether to enable experimental MSC1849 (aka relations) support
|
||||
self.msc1849_enabled = config.get("experimental_msc1849_support_enabled", True)
|
||||
# MSC3440 (thread relation)
|
||||
self.msc3440_enabled: bool = experimental.get("msc3440_enabled", False)
|
||||
|
||||
|
||||
@@ -489,6 +489,19 @@ class ServerConfig(Config):
|
||||
# events with profile information that differ from the target's global profile.
|
||||
self.allow_per_room_profiles = config.get("allow_per_room_profiles", True)
|
||||
|
||||
# The maximum size an avatar can have, in bytes.
|
||||
self.max_avatar_size = config.get("max_avatar_size")
|
||||
if self.max_avatar_size is not None:
|
||||
self.max_avatar_size = self.parse_size(self.max_avatar_size)
|
||||
|
||||
# The MIME types allowed for an avatar.
|
||||
self.allowed_avatar_mimetypes = config.get("allowed_avatar_mimetypes")
|
||||
if self.allowed_avatar_mimetypes and not isinstance(
|
||||
self.allowed_avatar_mimetypes,
|
||||
list,
|
||||
):
|
||||
raise ConfigError("allowed_avatar_mimetypes must be a list")
|
||||
|
||||
self.listeners = [parse_listener_def(x) for x in config.get("listeners", [])]
|
||||
|
||||
# no_tls is not really supported any more, but let's grandfather it in
|
||||
@@ -1168,6 +1181,20 @@ class ServerConfig(Config):
|
||||
#
|
||||
#allow_per_room_profiles: false
|
||||
|
||||
# The largest allowed file size for a user avatar. Defaults to no restriction.
|
||||
#
|
||||
# Note that user avatar changes will not work if this is set without
|
||||
# using Synapse's media repository.
|
||||
#
|
||||
#max_avatar_size: 10M
|
||||
|
||||
# The MIME types allowed for user avatars. Defaults to no restriction.
|
||||
#
|
||||
# Note that user avatar changes will not work if this is set without
|
||||
# using Synapse's media repository.
|
||||
#
|
||||
#allowed_avatar_mimetypes: ["image/png", "image/jpeg", "image/gif"]
|
||||
|
||||
# How long to keep redacted events in unredacted form in the database. After
|
||||
# this period redacted events get replaced with their redacted form in the DB.
|
||||
#
|
||||
|
||||
@@ -31,6 +31,8 @@ from synapse.types import (
|
||||
create_requester,
|
||||
get_domain_from_id,
|
||||
)
|
||||
from synapse.util.caches.descriptors import cached
|
||||
from synapse.util.stringutils import parse_and_validate_mxc_uri
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from synapse.server import HomeServer
|
||||
@@ -64,6 +66,11 @@ class ProfileHandler:
|
||||
self.user_directory_handler = hs.get_user_directory_handler()
|
||||
self.request_ratelimiter = hs.get_request_ratelimiter()
|
||||
|
||||
self.max_avatar_size = hs.config.server.max_avatar_size
|
||||
self.allowed_avatar_mimetypes = hs.config.server.allowed_avatar_mimetypes
|
||||
|
||||
self.server_name = hs.config.server.server_name
|
||||
|
||||
if hs.config.worker.run_background_tasks:
|
||||
self.clock.looping_call(
|
||||
self._update_remote_profile_cache, self.PROFILE_UPDATE_MS
|
||||
@@ -286,6 +293,9 @@ class ProfileHandler:
|
||||
400, "Avatar URL is too long (max %i)" % (MAX_AVATAR_URL_LEN,)
|
||||
)
|
||||
|
||||
if not await self.check_avatar_size_and_mime_type(new_avatar_url):
|
||||
raise SynapseError(403, "This avatar is not allowed", Codes.FORBIDDEN)
|
||||
|
||||
avatar_url_to_set: Optional[str] = new_avatar_url
|
||||
if new_avatar_url == "":
|
||||
avatar_url_to_set = None
|
||||
@@ -307,6 +317,63 @@ class ProfileHandler:
|
||||
|
||||
await self._update_join_states(requester, target_user)
|
||||
|
||||
@cached()
|
||||
async def check_avatar_size_and_mime_type(self, mxc: str) -> bool:
|
||||
"""Check that the size and content type of the avatar at the given MXC URI are
|
||||
within the configured limits.
|
||||
|
||||
Args:
|
||||
mxc: The MXC URI at which the avatar can be found.
|
||||
|
||||
Returns:
|
||||
A boolean indicating whether the file can be allowed to be set as an avatar.
|
||||
"""
|
||||
if not self.max_avatar_size and not self.allowed_avatar_mimetypes:
|
||||
return True
|
||||
|
||||
server_name, _, media_id = parse_and_validate_mxc_uri(mxc)
|
||||
|
||||
if server_name == self.server_name:
|
||||
media_info = await self.store.get_local_media(media_id)
|
||||
else:
|
||||
media_info = await self.store.get_cached_remote_media(server_name, media_id)
|
||||
|
||||
if media_info is None:
|
||||
# Both configuration options need to access the file's metadata, and
|
||||
# retrieving remote avatars just for this becomes a bit of a faff, especially
|
||||
# if e.g. the file is too big. It's also generally safe to assume most files
|
||||
# used as avatar are uploaded locally, or if the upload didn't happen as part
|
||||
# of a PUT request on /avatar_url that the file was at least previewed by the
|
||||
# user locally (and therefore downloaded to the remote media cache).
|
||||
logger.warning("Forbidding avatar change to %s: avatar not on server", mxc)
|
||||
return False
|
||||
|
||||
if self.max_avatar_size:
|
||||
# Ensure avatar does not exceed max allowed avatar size
|
||||
if media_info["media_length"] > self.max_avatar_size:
|
||||
logger.warning(
|
||||
"Forbidding avatar change to %s: %d bytes is above the allowed size "
|
||||
"limit",
|
||||
mxc,
|
||||
media_info["media_length"],
|
||||
)
|
||||
return False
|
||||
|
||||
if self.allowed_avatar_mimetypes:
|
||||
# Ensure the avatar's file type is allowed
|
||||
if (
|
||||
self.allowed_avatar_mimetypes
|
||||
and media_info["media_type"] not in self.allowed_avatar_mimetypes
|
||||
):
|
||||
logger.warning(
|
||||
"Forbidding avatar change to %s: mimetype %s not allowed",
|
||||
mxc,
|
||||
media_info["media_type"],
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
async def on_profile_query(self, args: JsonDict) -> JsonDict:
|
||||
"""Handles federation profile query requests."""
|
||||
|
||||
|
||||
@@ -590,6 +590,12 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
||||
errcode=Codes.BAD_JSON,
|
||||
)
|
||||
|
||||
if "avatar_url" in content:
|
||||
if not await self.profile_handler.check_avatar_size_and_mime_type(
|
||||
content["avatar_url"],
|
||||
):
|
||||
raise SynapseError(403, "This avatar is not allowed", Codes.FORBIDDEN)
|
||||
|
||||
# The event content should *not* include the authorising user as
|
||||
# it won't be properly signed. Strip it out since it might come
|
||||
# back from a client updating a display name / avatar.
|
||||
|
||||
@@ -75,7 +75,6 @@ class RelationsWorkerStore(SQLBaseStore):
|
||||
):
|
||||
super().__init__(database, db_conn, hs)
|
||||
|
||||
self._msc1849_enabled = hs.config.experimental.msc1849_enabled
|
||||
self._msc3440_enabled = hs.config.experimental.msc3440_enabled
|
||||
|
||||
@cached(tree=True)
|
||||
@@ -683,9 +682,6 @@ class RelationsWorkerStore(SQLBaseStore):
|
||||
A map of event ID to the bundled aggregation for the event. Not all
|
||||
events may have bundled aggregations in the results.
|
||||
"""
|
||||
# If bundled aggregations are disabled, nothing to do.
|
||||
if not self._msc1849_enabled:
|
||||
return {}
|
||||
|
||||
# TODO Parallelize.
|
||||
results = {}
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from typing import Any, Dict
|
||||
from unittest.mock import Mock
|
||||
|
||||
import synapse.types
|
||||
from synapse.api.errors import AuthError, SynapseError
|
||||
from synapse.rest import admin
|
||||
from synapse.server import HomeServer
|
||||
from synapse.types import UserID
|
||||
|
||||
from tests import unittest
|
||||
@@ -46,7 +47,7 @@ class ProfileTestCase(unittest.HomeserverTestCase):
|
||||
)
|
||||
return hs
|
||||
|
||||
def prepare(self, reactor, clock, hs):
|
||||
def prepare(self, reactor, clock, hs: HomeServer):
|
||||
self.store = hs.get_datastore()
|
||||
|
||||
self.frank = UserID.from_string("@1234abcd:test")
|
||||
@@ -248,3 +249,92 @@ class ProfileTestCase(unittest.HomeserverTestCase):
|
||||
),
|
||||
SynapseError,
|
||||
)
|
||||
|
||||
def test_avatar_constraints_no_config(self):
|
||||
"""Tests that the method to check an avatar against configured constraints skips
|
||||
all of its check if no constraint is configured.
|
||||
"""
|
||||
# The first check that's done by this method is whether the file exists; if we
|
||||
# don't get an error on a non-existing file then it means all of the checks were
|
||||
# successfully skipped.
|
||||
res = self.get_success(
|
||||
self.handler.check_avatar_size_and_mime_type("mxc://test/unknown_file")
|
||||
)
|
||||
self.assertTrue(res)
|
||||
|
||||
@unittest.override_config({"max_avatar_size": 50})
|
||||
def test_avatar_constraints_missing(self):
|
||||
"""Tests that an avatar isn't allowed if the file at the given MXC URI couldn't
|
||||
be found.
|
||||
"""
|
||||
res = self.get_success(
|
||||
self.handler.check_avatar_size_and_mime_type("mxc://test/unknown_file")
|
||||
)
|
||||
self.assertFalse(res)
|
||||
|
||||
@unittest.override_config({"max_avatar_size": 50})
|
||||
def test_avatar_constraints_file_size(self):
|
||||
"""Tests that a file that's above the allowed file size is forbidden but one
|
||||
that's below it is allowed.
|
||||
"""
|
||||
self._setup_local_files(
|
||||
{
|
||||
"small": {"size": 40},
|
||||
"big": {"size": 60},
|
||||
}
|
||||
)
|
||||
|
||||
res = self.get_success(
|
||||
self.handler.check_avatar_size_and_mime_type("mxc://test/small")
|
||||
)
|
||||
self.assertTrue(res)
|
||||
|
||||
res = self.get_success(
|
||||
self.handler.check_avatar_size_and_mime_type("mxc://test/big")
|
||||
)
|
||||
self.assertFalse(res)
|
||||
|
||||
@unittest.override_config({"allowed_avatar_mimetypes": ["image/png"]})
|
||||
def test_avatar_constraint_mime_type(self):
|
||||
"""Tests that a file with an unauthorised MIME type is forbidden but one with
|
||||
an authorised content type is allowed.
|
||||
"""
|
||||
self._setup_local_files(
|
||||
{
|
||||
"good": {"mimetype": "image/png"},
|
||||
"bad": {"mimetype": "application/octet-stream"},
|
||||
}
|
||||
)
|
||||
|
||||
res = self.get_success(
|
||||
self.handler.check_avatar_size_and_mime_type("mxc://test/good")
|
||||
)
|
||||
self.assertTrue(res)
|
||||
|
||||
res = self.get_success(
|
||||
self.handler.check_avatar_size_and_mime_type("mxc://test/bad")
|
||||
)
|
||||
self.assertFalse(res)
|
||||
|
||||
def _setup_local_files(self, names_and_props: Dict[str, Dict[str, Any]]):
|
||||
"""Stores metadata about files in the database.
|
||||
|
||||
Args:
|
||||
names_and_props: A dictionary with one entry per file, with the key being the
|
||||
file's name, and the value being a dictionary of properties. Supported
|
||||
properties are "mimetype" (for the file's type) and "size" (for the
|
||||
file's size).
|
||||
"""
|
||||
store = self.hs.get_datastore()
|
||||
|
||||
for name, props in names_and_props.items():
|
||||
self.get_success(
|
||||
store.store_local_media(
|
||||
media_id=name,
|
||||
media_type=props.get("mimetype", "image/png"),
|
||||
time_now_ms=self.clock.time_msec(),
|
||||
upload_name=None,
|
||||
media_length=props.get("size", 50),
|
||||
user_id=UserID.from_string("@rin:test"),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -12,18 +12,20 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import urllib.parse
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import Mock
|
||||
from typing import List
|
||||
|
||||
from twisted.internet.defer import Deferred
|
||||
from parameterized import parameterized
|
||||
|
||||
from twisted.test.proto_helpers import MemoryReactor
|
||||
|
||||
import synapse.rest.admin
|
||||
from synapse.http.server import JsonResource
|
||||
from synapse.logging.context import make_deferred_yieldable
|
||||
from synapse.rest.admin import VersionServlet
|
||||
from synapse.rest.client import groups, login, room
|
||||
from synapse.server import HomeServer
|
||||
from synapse.util import Clock
|
||||
|
||||
from tests import unittest
|
||||
from tests.server import FakeSite, make_request
|
||||
@@ -33,12 +35,12 @@ from tests.test_utils import SMALL_PNG
|
||||
class VersionTestCase(unittest.HomeserverTestCase):
|
||||
url = "/_synapse/admin/v1/server_version"
|
||||
|
||||
def create_test_resource(self):
|
||||
def create_test_resource(self) -> JsonResource:
|
||||
resource = JsonResource(self.hs)
|
||||
VersionServlet(self.hs).register(resource)
|
||||
return resource
|
||||
|
||||
def test_version_string(self):
|
||||
def test_version_string(self) -> None:
|
||||
channel = self.make_request("GET", self.url, shorthand=False)
|
||||
|
||||
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
||||
@@ -54,14 +56,14 @@ class DeleteGroupTestCase(unittest.HomeserverTestCase):
|
||||
groups.register_servlets,
|
||||
]
|
||||
|
||||
def prepare(self, reactor, clock, hs):
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||
self.admin_user = self.register_user("admin", "pass", admin=True)
|
||||
self.admin_user_tok = self.login("admin", "pass")
|
||||
|
||||
self.other_user = self.register_user("user", "pass")
|
||||
self.other_user_token = self.login("user", "pass")
|
||||
|
||||
def test_delete_group(self):
|
||||
def test_delete_group(self) -> None:
|
||||
# Create a new group
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
@@ -112,7 +114,7 @@ class DeleteGroupTestCase(unittest.HomeserverTestCase):
|
||||
self.assertNotIn(group_id, self._get_groups_user_is_in(self.admin_user_tok))
|
||||
self.assertNotIn(group_id, self._get_groups_user_is_in(self.other_user_token))
|
||||
|
||||
def _check_group(self, group_id, expect_code):
|
||||
def _check_group(self, group_id: str, expect_code: int) -> None:
|
||||
"""Assert that trying to fetch the given group results in the given
|
||||
HTTP status code
|
||||
"""
|
||||
@@ -124,7 +126,7 @@ class DeleteGroupTestCase(unittest.HomeserverTestCase):
|
||||
|
||||
self.assertEqual(expect_code, channel.code, msg=channel.json_body)
|
||||
|
||||
def _get_groups_user_is_in(self, access_token):
|
||||
def _get_groups_user_is_in(self, access_token: str) -> List[str]:
|
||||
"""Returns the list of groups the user is in (given their access token)"""
|
||||
channel = self.make_request("GET", b"/joined_groups", access_token=access_token)
|
||||
|
||||
@@ -143,59 +145,15 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
|
||||
room.register_servlets,
|
||||
]
|
||||
|
||||
def prepare(self, reactor, clock, hs):
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||
# Allow for uploading and downloading to/from the media repo
|
||||
self.media_repo = hs.get_media_repository_resource()
|
||||
self.download_resource = self.media_repo.children[b"download"]
|
||||
self.upload_resource = self.media_repo.children[b"upload"]
|
||||
|
||||
def make_homeserver(self, reactor, clock):
|
||||
|
||||
self.fetches = []
|
||||
|
||||
async def get_file(destination, path, output_stream, args=None, max_size=None):
|
||||
"""
|
||||
Returns tuple[int,dict,str,int] of file length, response headers,
|
||||
absolute URI, and response code.
|
||||
"""
|
||||
|
||||
def write_to(r):
|
||||
data, response = r
|
||||
output_stream.write(data)
|
||||
return response
|
||||
|
||||
d = Deferred()
|
||||
d.addCallback(write_to)
|
||||
self.fetches.append((d, destination, path, args))
|
||||
return await make_deferred_yieldable(d)
|
||||
|
||||
client = Mock()
|
||||
client.get_file = get_file
|
||||
|
||||
self.storage_path = self.mktemp()
|
||||
self.media_store_path = self.mktemp()
|
||||
os.mkdir(self.storage_path)
|
||||
os.mkdir(self.media_store_path)
|
||||
|
||||
config = self.default_config()
|
||||
config["media_store_path"] = self.media_store_path
|
||||
config["thumbnail_requirements"] = {}
|
||||
config["max_image_pixels"] = 2000000
|
||||
|
||||
provider_config = {
|
||||
"module": "synapse.rest.media.v1.storage_provider.FileStorageProviderBackend",
|
||||
"store_local": True,
|
||||
"store_synchronous": False,
|
||||
"store_remote": True,
|
||||
"config": {"directory": self.storage_path},
|
||||
}
|
||||
config["media_storage_providers"] = [provider_config]
|
||||
|
||||
hs = self.setup_test_homeserver(config=config, federation_http_client=client)
|
||||
|
||||
return hs
|
||||
|
||||
def _ensure_quarantined(self, admin_user_tok, server_and_media_id):
|
||||
def _ensure_quarantined(
|
||||
self, admin_user_tok: str, server_and_media_id: str
|
||||
) -> None:
|
||||
"""Ensure a piece of media is quarantined when trying to access it."""
|
||||
channel = make_request(
|
||||
self.reactor,
|
||||
@@ -216,12 +174,18 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
|
||||
),
|
||||
)
|
||||
|
||||
def test_quarantine_media_requires_admin(self):
|
||||
@parameterized.expand(
|
||||
[
|
||||
# Attempt quarantine media APIs as non-admin
|
||||
"/_synapse/admin/v1/media/quarantine/example.org/abcde12345",
|
||||
# And the roomID/userID endpoint
|
||||
"/_synapse/admin/v1/room/!room%3Aexample.com/media/quarantine",
|
||||
]
|
||||
)
|
||||
def test_quarantine_media_requires_admin(self, url: str) -> None:
|
||||
self.register_user("nonadmin", "pass", admin=False)
|
||||
non_admin_user_tok = self.login("nonadmin", "pass")
|
||||
|
||||
# Attempt quarantine media APIs as non-admin
|
||||
url = "/_synapse/admin/v1/media/quarantine/example.org/abcde12345"
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
url.encode("ascii"),
|
||||
@@ -235,22 +199,7 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
|
||||
msg="Expected forbidden on quarantining media as a non-admin",
|
||||
)
|
||||
|
||||
# And the roomID/userID endpoint
|
||||
url = "/_synapse/admin/v1/room/!room%3Aexample.com/media/quarantine"
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
url.encode("ascii"),
|
||||
access_token=non_admin_user_tok,
|
||||
)
|
||||
|
||||
# Expect a forbidden error
|
||||
self.assertEqual(
|
||||
HTTPStatus.FORBIDDEN,
|
||||
channel.code,
|
||||
msg="Expected forbidden on quarantining media as a non-admin",
|
||||
)
|
||||
|
||||
def test_quarantine_media_by_id(self):
|
||||
def test_quarantine_media_by_id(self) -> None:
|
||||
self.register_user("id_admin", "pass", admin=True)
|
||||
admin_user_tok = self.login("id_admin", "pass")
|
||||
|
||||
@@ -295,7 +244,15 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
|
||||
# Attempt to access the media
|
||||
self._ensure_quarantined(admin_user_tok, server_name_and_media_id)
|
||||
|
||||
def test_quarantine_all_media_in_room(self, override_url_template=None):
|
||||
@parameterized.expand(
|
||||
[
|
||||
# regular API path
|
||||
"/_synapse/admin/v1/room/%s/media/quarantine",
|
||||
# deprecated API path
|
||||
"/_synapse/admin/v1/quarantine_media/%s",
|
||||
]
|
||||
)
|
||||
def test_quarantine_all_media_in_room(self, url: str) -> None:
|
||||
self.register_user("room_admin", "pass", admin=True)
|
||||
admin_user_tok = self.login("room_admin", "pass")
|
||||
|
||||
@@ -333,16 +290,9 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
|
||||
tok=non_admin_user_tok,
|
||||
)
|
||||
|
||||
# Quarantine all media in the room
|
||||
if override_url_template:
|
||||
url = override_url_template % urllib.parse.quote(room_id)
|
||||
else:
|
||||
url = "/_synapse/admin/v1/room/%s/media/quarantine" % urllib.parse.quote(
|
||||
room_id
|
||||
)
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
url,
|
||||
url % urllib.parse.quote(room_id),
|
||||
access_token=admin_user_tok,
|
||||
)
|
||||
self.pump(1.0)
|
||||
@@ -359,11 +309,7 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
|
||||
self._ensure_quarantined(admin_user_tok, server_and_media_id_1)
|
||||
self._ensure_quarantined(admin_user_tok, server_and_media_id_2)
|
||||
|
||||
def test_quarantine_all_media_in_room_deprecated_api_path(self):
|
||||
# Perform the above test with the deprecated API path
|
||||
self.test_quarantine_all_media_in_room("/_synapse/admin/v1/quarantine_media/%s")
|
||||
|
||||
def test_quarantine_all_media_by_user(self):
|
||||
def test_quarantine_all_media_by_user(self) -> None:
|
||||
self.register_user("user_admin", "pass", admin=True)
|
||||
admin_user_tok = self.login("user_admin", "pass")
|
||||
|
||||
@@ -401,7 +347,7 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
|
||||
self._ensure_quarantined(admin_user_tok, server_and_media_id_1)
|
||||
self._ensure_quarantined(admin_user_tok, server_and_media_id_2)
|
||||
|
||||
def test_cannot_quarantine_safe_media(self):
|
||||
def test_cannot_quarantine_safe_media(self) -> None:
|
||||
self.register_user("user_admin", "pass", admin=True)
|
||||
admin_user_tok = self.login("user_admin", "pass")
|
||||
|
||||
@@ -475,7 +421,7 @@ class PurgeHistoryTestCase(unittest.HomeserverTestCase):
|
||||
room.register_servlets,
|
||||
]
|
||||
|
||||
def prepare(self, reactor, clock, hs):
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||
self.admin_user = self.register_user("admin", "pass", admin=True)
|
||||
self.admin_user_tok = self.login("admin", "pass")
|
||||
|
||||
@@ -488,7 +434,7 @@ class PurgeHistoryTestCase(unittest.HomeserverTestCase):
|
||||
self.url = f"/_synapse/admin/v1/purge_history/{self.room_id}"
|
||||
self.url_status = "/_synapse/admin/v1/purge_history_status/"
|
||||
|
||||
def test_purge_history(self):
|
||||
def test_purge_history(self) -> None:
|
||||
"""
|
||||
Simple test of purge history API.
|
||||
Test only that is is possible to call, get status HTTPStatus.OK and purge_id.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,9 +14,13 @@
|
||||
|
||||
from http import HTTPStatus
|
||||
|
||||
from twisted.test.proto_helpers import MemoryReactor
|
||||
|
||||
import synapse.rest.admin
|
||||
from synapse.api.errors import Codes, SynapseError
|
||||
from synapse.rest.client import login
|
||||
from synapse.server import HomeServer
|
||||
from synapse.util import Clock
|
||||
|
||||
from tests import unittest
|
||||
|
||||
@@ -28,11 +32,11 @@ class UsernameAvailableTestCase(unittest.HomeserverTestCase):
|
||||
]
|
||||
url = "/_synapse/admin/v1/username_available"
|
||||
|
||||
def prepare(self, reactor, clock, hs):
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||
self.register_user("admin", "pass", admin=True)
|
||||
self.admin_user_tok = self.login("admin", "pass")
|
||||
|
||||
async def check_username(username):
|
||||
async def check_username(username: str) -> bool:
|
||||
if username == "allowed":
|
||||
return True
|
||||
raise SynapseError(
|
||||
@@ -44,24 +48,24 @@ class UsernameAvailableTestCase(unittest.HomeserverTestCase):
|
||||
handler = self.hs.get_registration_handler()
|
||||
handler.check_username = check_username
|
||||
|
||||
def test_username_available(self):
|
||||
def test_username_available(self) -> None:
|
||||
"""
|
||||
The endpoint should return a HTTPStatus.OK response if the username does not exist
|
||||
"""
|
||||
|
||||
url = "%s?username=%s" % (self.url, "allowed")
|
||||
channel = self.make_request("GET", url, None, self.admin_user_tok)
|
||||
channel = self.make_request("GET", url, access_token=self.admin_user_tok)
|
||||
|
||||
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.json_body)
|
||||
self.assertTrue(channel.json_body["available"])
|
||||
|
||||
def test_username_unavailable(self):
|
||||
def test_username_unavailable(self) -> None:
|
||||
"""
|
||||
The endpoint should return a HTTPStatus.OK response if the username does not exist
|
||||
"""
|
||||
|
||||
url = "%s?username=%s" % (self.url, "disallowed")
|
||||
channel = self.make_request("GET", url, None, self.admin_user_tok)
|
||||
channel = self.make_request("GET", url, access_token=self.admin_user_tok)
|
||||
|
||||
self.assertEqual(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
|
||||
@@ -13,8 +13,12 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Tests REST events for /profile paths."""
|
||||
from typing import Any, Dict
|
||||
|
||||
from synapse.api.errors import Codes
|
||||
from synapse.rest import admin
|
||||
from synapse.rest.client import login, profile, room
|
||||
from synapse.types import UserID
|
||||
|
||||
from tests import unittest
|
||||
|
||||
@@ -25,6 +29,7 @@ class ProfileTestCase(unittest.HomeserverTestCase):
|
||||
admin.register_servlets_for_client_rest_resource,
|
||||
login.register_servlets,
|
||||
profile.register_servlets,
|
||||
room.register_servlets,
|
||||
]
|
||||
|
||||
def make_homeserver(self, reactor, clock):
|
||||
@@ -150,6 +155,157 @@ class ProfileTestCase(unittest.HomeserverTestCase):
|
||||
self.assertEqual(channel.code, 200, channel.result)
|
||||
return channel.json_body.get("avatar_url")
|
||||
|
||||
@unittest.override_config({"max_avatar_size": 50})
|
||||
def test_avatar_size_limit_global(self):
|
||||
"""Tests that the maximum size limit for avatars is enforced when updating a
|
||||
global profile.
|
||||
"""
|
||||
self._setup_local_files(
|
||||
{
|
||||
"small": {"size": 40},
|
||||
"big": {"size": 60},
|
||||
}
|
||||
)
|
||||
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/profile/{self.owner}/avatar_url",
|
||||
content={"avatar_url": "mxc://test/big"},
|
||||
access_token=self.owner_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 403, channel.result)
|
||||
self.assertEqual(
|
||||
channel.json_body["errcode"], Codes.FORBIDDEN, channel.json_body
|
||||
)
|
||||
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/profile/{self.owner}/avatar_url",
|
||||
content={"avatar_url": "mxc://test/small"},
|
||||
access_token=self.owner_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.result)
|
||||
|
||||
@unittest.override_config({"max_avatar_size": 50})
|
||||
def test_avatar_size_limit_per_room(self):
|
||||
"""Tests that the maximum size limit for avatars is enforced when updating a
|
||||
per-room profile.
|
||||
"""
|
||||
self._setup_local_files(
|
||||
{
|
||||
"small": {"size": 40},
|
||||
"big": {"size": 60},
|
||||
}
|
||||
)
|
||||
|
||||
room_id = self.helper.create_room_as(tok=self.owner_tok)
|
||||
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/rooms/{room_id}/state/m.room.member/{self.owner}",
|
||||
content={"membership": "join", "avatar_url": "mxc://test/big"},
|
||||
access_token=self.owner_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 403, channel.result)
|
||||
self.assertEqual(
|
||||
channel.json_body["errcode"], Codes.FORBIDDEN, channel.json_body
|
||||
)
|
||||
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/rooms/{room_id}/state/m.room.member/{self.owner}",
|
||||
content={"membership": "join", "avatar_url": "mxc://test/small"},
|
||||
access_token=self.owner_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.result)
|
||||
|
||||
@unittest.override_config({"allowed_avatar_mimetypes": ["image/png"]})
|
||||
def test_avatar_allowed_mime_type_global(self):
|
||||
"""Tests that the MIME type whitelist for avatars is enforced when updating a
|
||||
global profile.
|
||||
"""
|
||||
self._setup_local_files(
|
||||
{
|
||||
"good": {"mimetype": "image/png"},
|
||||
"bad": {"mimetype": "application/octet-stream"},
|
||||
}
|
||||
)
|
||||
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/profile/{self.owner}/avatar_url",
|
||||
content={"avatar_url": "mxc://test/bad"},
|
||||
access_token=self.owner_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 403, channel.result)
|
||||
self.assertEqual(
|
||||
channel.json_body["errcode"], Codes.FORBIDDEN, channel.json_body
|
||||
)
|
||||
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/profile/{self.owner}/avatar_url",
|
||||
content={"avatar_url": "mxc://test/good"},
|
||||
access_token=self.owner_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.result)
|
||||
|
||||
@unittest.override_config({"allowed_avatar_mimetypes": ["image/png"]})
|
||||
def test_avatar_allowed_mime_type_per_room(self):
|
||||
"""Tests that the MIME type whitelist for avatars is enforced when updating a
|
||||
per-room profile.
|
||||
"""
|
||||
self._setup_local_files(
|
||||
{
|
||||
"good": {"mimetype": "image/png"},
|
||||
"bad": {"mimetype": "application/octet-stream"},
|
||||
}
|
||||
)
|
||||
|
||||
room_id = self.helper.create_room_as(tok=self.owner_tok)
|
||||
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/rooms/{room_id}/state/m.room.member/{self.owner}",
|
||||
content={"membership": "join", "avatar_url": "mxc://test/bad"},
|
||||
access_token=self.owner_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 403, channel.result)
|
||||
self.assertEqual(
|
||||
channel.json_body["errcode"], Codes.FORBIDDEN, channel.json_body
|
||||
)
|
||||
|
||||
channel = self.make_request(
|
||||
"PUT",
|
||||
f"/rooms/{room_id}/state/m.room.member/{self.owner}",
|
||||
content={"membership": "join", "avatar_url": "mxc://test/good"},
|
||||
access_token=self.owner_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200, channel.result)
|
||||
|
||||
def _setup_local_files(self, names_and_props: Dict[str, Dict[str, Any]]):
|
||||
"""Stores metadata about files in the database.
|
||||
|
||||
Args:
|
||||
names_and_props: A dictionary with one entry per file, with the key being the
|
||||
file's name, and the value being a dictionary of properties. Supported
|
||||
properties are "mimetype" (for the file's type) and "size" (for the
|
||||
file's size).
|
||||
"""
|
||||
store = self.hs.get_datastore()
|
||||
|
||||
for name, props in names_and_props.items():
|
||||
self.get_success(
|
||||
store.store_local_media(
|
||||
media_id=name,
|
||||
media_type=props.get("mimetype", "image/png"),
|
||||
time_now_ms=self.clock.time_msec(),
|
||||
upload_name=None,
|
||||
media_length=props.get("size", 50),
|
||||
user_id=UserID.from_string("@rin:test"),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ProfilesRestrictedTestCase(unittest.HomeserverTestCase):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user