1
0

Compare commits

..

107 Commits

Author SHA1 Message Date
Quentin Gliech 4580e12275 WIP: MAS in complement 2024-08-05 17:30:40 +02:00
Eric Eastwood 2b620e0a15 Sliding Sync: Add typing notification extension (MSC3961) (#17505)
[MSC3961](https://github.com/matrix-org/matrix-spec-proposals/pull/3961): Sliding Sync Extension: Typing Notifications

Based on
[MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575):
Sliding Sync
2024-07-31 13:20:23 -05:00
Eric Eastwood 39731bb205 Sliding Sync: Split and move tests (#17504)
Split and move Sliding Sync tests so we have some more sane test file
sizes
2024-07-31 12:20:46 -05:00
Eric Eastwood 1d6186265a Sliding Sync: Fix limited response description (make accurate) (#17507) 2024-07-31 11:47:26 -05:00
Eric Eastwood 46de0ee16b Sliding Sync: Update filters to be robust against remote invite rooms (#17450)
Update `filters.is_encrypted` and `filters.types`/`filters.not_types` to
be robust when dealing with remote invite rooms in Sliding Sync.

Part of
[MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575):
Sliding Sync

Follow-up to https://github.com/element-hq/synapse/pull/17434

We now take into account current state, fallback to stripped state
for invite/knock rooms, then historical state. If we can't determine
the info needed to filter a room (either from state or stripped state),
it is filtered out.
2024-07-30 13:20:29 -05:00
Eric Eastwood b221f0b84b Sliding Sync: Add receipts extension (MSC3960) (#17489)
[MSC3960](https://github.com/matrix-org/matrix-spec-proposals/pull/3960): Receipts extension

Based on
[MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575):
Sliding Sync
2024-07-30 12:49:55 -05:00
Olivier 'reivilibre b2c55bd049 Merge branch 'master' into develop 2024-07-30 18:09:05 +01:00
Olivier 'reivilibre ed583d9c81 Merge branch 'release-v1.112' 2024-07-30 18:07:35 +01:00
dependabot[bot] f76dc9923c Bump types-setuptools from 70.1.0.20240627 to 71.1.0.20240726 (#17497)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-30 17:33:43 +01:00
dependabot[bot] 7e997fb8b1 Bump types-pyopenssl from 24.1.0.20240425 to 24.1.0.20240722 (#17496)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-30 17:33:18 +01:00
dependabot[bot] dbc2290cbe Bump bcrypt from 4.1.3 to 4.2.0 (#17495)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-30 17:32:49 +01:00
dependabot[bot] 2f6b86e79a Bump serde_json from 1.0.120 to 1.0.121 (#17493)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-30 17:32:16 +01:00
Olivier 'reivilibre 37f9876ccf 1.112.0 2024-07-30 17:24:09 +01:00
reivilibre 8b449a8ce6 Upgrade locked dependency on Twisted to 24.7.0rc1. (#17502)
I also update the tests and HTTP Proxy code to fix it for this new
Twisted release.

Pulls in fix for
https://github.com/twisted/twisted/security/advisories/GHSA-c8m8-j448-xjx7


Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
2024-07-30 17:14:14 +01:00
Olivier 'reivilibre 53db8a914e Merge branch 'master' into develop 2024-07-30 17:10:46 +01:00
Olivier 'reivilibre e4868f8a1e Add bold emphasis to some parts of the changelog 2024-07-30 16:23:58 +01:00
Olivier 'reivilibre dcad81082c 1.111.1 2024-07-30 16:16:35 +01:00
reivilibre c56b070e6f Upgrade locked dependency on Twisted to 24.7.0rc1. (#17502)
I also update the tests and HTTP Proxy code to fix it for this new
Twisted release.

Pulls in fix for
https://github.com/twisted/twisted/security/advisories/GHSA-c8m8-j448-xjx7


Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
2024-07-30 15:23:23 +01:00
dependabot[bot] be726724a8 Bump ruff from 0.5.4 to 0.5.5 (#17494)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-30 11:44:54 +01:00
Erik Johnston 62ae56a4ac Add some more opentracing to sliding sync (#17501)
This will make it easier to see what it is doing in jaeger.
2024-07-30 10:54:11 +01:00
Richard van der Hoff 808dab0699 Fix failures property in /keys/query (#17499)
Fixes: https://github.com/element-hq/synapse/issues/17498
Fixes: https://github.com/element-hq/element-web/issues/27867
2024-07-30 09:51:24 +01:00
Erik Johnston 34306be5aa Only send rooms with updates down sliding sync (#17479)
Rather than always including all rooms in range.

Also adds a pre-filter to rooms that checks the stream change cache to
see if anything might have happened.

Based on #17447

---------

Co-authored-by: Eric Eastwood <eric.eastwood@beta.gouv.fr>
2024-07-30 09:30:44 +01:00
Erik Johnston be4a16ff44 Sliding Sync: Track whether we have sent rooms down to clients (#17447)
The basic idea is that we introduce a new token for a sliding sync
connection, which stores the mapping of room to room "status" (i.e. have
we sent the room down?). This token allows us to handle duplicate
requests properly. In future it can be used to store more
"per-connection" information safely.

In future this should be migrated into the DB, so its important that we
try to reduce the number of syncs where we need to update the
per-connection information. In this PoC this only happens when we: a)
send down a set of room for the first time, or b) we have previously
sent down a room and there are updates but we are not sending the room
down the sync (due to not falling in a list range)

Co-authored-by: Eric Eastwood <eric.eastwood@beta.gouv.fr>
2024-07-29 22:45:48 +01:00
Eric Eastwood 568051c0f0 Refactor Sliding Sync tests to better utilize the SlidingSyncBase.do_sync(...) (pt. 2) (#17482)
`SlidingSyncBase.do_sync()` for tests was first introduced in
https://github.com/element-hq/synapse/pull/17452

Part 1: https://github.com/element-hq/synapse/pull/17481
2024-07-25 11:01:47 -05:00
Eric Eastwood ebbabfe782 Refactor Sliding Sync tests to better utilize the SlidingSyncBase (pt. 1) (#17481)
`SlidingSyncBase` for tests was first introduced in
https://github.com/element-hq/synapse/pull/17452

Part 2: https://github.com/element-hq/synapse/pull/17482
2024-07-25 10:43:35 -05:00
YLong Shi 69ac4b6a6e Update config_documentation - Change example of msisdn in allowed_local_3pids (#17476)
Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
2024-07-25 11:07:44 +00:00
Eric Eastwood 729026e604 Sliding Sync: Add Account Data extension (MSC3959) (#17477)
Extensions based on
[MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575):
Sliding Sync
2024-07-24 17:10:38 -05:00
Erik Johnston bdf37ad4c4 Sliding Sync: ensure bump stamp ignores backfilled events (#17478)
Backfill events have a negative stream ordering, and so its not useful
to use to compare with other (positive) stream orderings.

Plus, the Rust SDK currently assumes `bump_stamp` is positive.
2024-07-24 15:21:56 +01:00
Erik Johnston 8bbc98e66d Use a new token format for sliding sync (#17452)
This is in preparation for adding per-connection state.

---------

Co-authored-by: Eric Eastwood <eric.eastwood@beta.gouv.fr>
2024-07-24 11:47:25 +01:00
Maciej Laskowski 4b9f4c2abf Update debian template - new link to the delegation docs (#17475)
Update debian template - new link to the delegation docs
2024-07-24 10:32:56 +01:00
Devon Hudson e8ee784c75 Address changelog review comments 2024-07-23 09:14:45 -06:00
Devon Hudson 48c1307911 1.112.0rc1 2024-07-23 09:01:43 -06:00
Erik Johnston d225b6b3eb Speed up SS room sorting (#17468)
We do this by bulk fetching the latest stream ordering.

---------

Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
2024-07-23 14:03:14 +01:00
reivilibre 1daae43f3a Reduce volume of 'Waiting for current token' logs, which were introduced in v1.109.0. (#17428)
Introduced in: #17215

This caused us a minor bit of grief as the volume of logs produced was
much higher than normal

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
2024-07-23 11:51:34 +01:00
Michael Hollister a9ee832e48 Fixed presence results not returning offline users on initial sync (#17231)
This is to address an issue in which `m.presence` results on initial
sync are not returning entries of users who are currently offline.

The original behaviour was from
https://github.com/element-hq/synapse/issues/1535

This change is useful for applications that use the
presence system for tracking user profile information/updates (e.g.
https://github.com/element-hq/synapse/pull/16992 or for profile status
messages).

This is gated behind a new configuration option to avoid performance
impact for applications that don't need this, as a pragmatic solution
for now.
2024-07-23 09:59:24 +00:00
dependabot[bot] 13a99fba1b Bump hiredis from 2.3.2 to 3.0.0 (#17464)
Bumps [hiredis](https://github.com/redis/hiredis-py) from 2.3.2 to
3.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/redis/hiredis-py/releases">hiredis's
releases</a>.</em></p>
<blockquote>
<h2>3.0.0</h2>
<h1>Changes</h1>
<h2>Breaking Changes</h2>
<ul>
<li>Return Redis sets as Python lists (<a
href="https://redirect.github.com/redis/hiredis-py/issues/189">#189</a>)</li>
</ul>
<h2>🐛 Bug Fixes</h2>
<ul>
<li>Return Redis sets as Python lists (<a
href="https://redirect.github.com/redis/hiredis-py/issues/189">#189</a>)</li>
</ul>
<h2>Contributors</h2>
<p>We'd like to thank all the contributors who worked on this
release!</p>
<p><a href="https://github.com/gerzse"><code>@​gerzse</code></a></p>
<h2>2.4.0</h2>
<h1>Changes</h1>
<h2>🧰 Maintenance</h2>
<ul>
<li>Fix small typo (<a
href="https://redirect.github.com/redis/hiredis-py/issues/192">#192</a>)</li>
<li>Quote version for Python setup action in CI (<a
href="https://redirect.github.com/redis/hiredis-py/issues/191">#191</a>)</li>
<li>Fix building the wheel for windows (<a
href="https://redirect.github.com/redis/hiredis-py/issues/190">#190</a>)</li>
<li>pack: Replace sdsalloc.h with alloc.h (<a
href="https://redirect.github.com/redis/hiredis-py/issues/159">#159</a>)</li>
<li>Bump black from 22.3.0 to 24.3.0 (<a
href="https://redirect.github.com/redis/hiredis-py/issues/185">#185</a>)</li>
<li>Removing python 3.7 trove (<a
href="https://redirect.github.com/redis/hiredis-py/issues/181">#181</a>)</li>
<li>Badge for latest released on Pypi (<a
href="https://redirect.github.com/redis/hiredis-py/issues/182">#182</a>)</li>
<li>Sync license in metadata with LICENSE file (<a
href="https://redirect.github.com/redis/hiredis-py/issues/183">#183</a>)</li>
</ul>
<h2>Contributors</h2>
<p>We'd like to thank all the contributors who worked on this
release!</p>
<p><a href="https://github.com/Apteryks"><code>@​Apteryks</code></a>, <a
href="https://github.com/ArtemIsmagilov"><code>@​ArtemIsmagilov</code></a>,
<a href="https://github.com/chayim"><code>@​chayim</code></a>, <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>, <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot],
<a href="https://github.com/gerzse"><code>@​gerzse</code></a> and <a
href="https://github.com/shadchin"><code>@​shadchin</code></a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/redis/hiredis-py/commit/c1eefbdb76614435f7433207bf385ba8cb930b60"><code>c1eefbd</code></a>
Return Redis sets as Python lists (<a
href="https://redirect.github.com/redis/hiredis-py/issues/189">#189</a>)</li>
<li><a
href="https://github.com/redis/hiredis-py/commit/a94bb447173c60b90709a9ed117b3a5b699ff795"><code>a94bb44</code></a>
Version 2.4.0 (<a
href="https://redirect.github.com/redis/hiredis-py/issues/193">#193</a>)</li>
<li><a
href="https://github.com/redis/hiredis-py/commit/7792dd23384aa7b00761df51d8011fe24fa33143"><code>7792dd2</code></a>
Fix a typo in the README file (<a
href="https://redirect.github.com/redis/hiredis-py/issues/192">#192</a>)</li>
<li><a
href="https://github.com/redis/hiredis-py/commit/01fa2fd6f123e3424ffa00a647f2bf83d48543be"><code>01fa2fd</code></a>
Quote version for Python setup action in CI (<a
href="https://redirect.github.com/redis/hiredis-py/issues/191">#191</a>)</li>
<li><a
href="https://github.com/redis/hiredis-py/commit/4c970a336567223573c700a44e405a0c263a85fa"><code>4c970a3</code></a>
Fix building the wheel for windows (<a
href="https://redirect.github.com/redis/hiredis-py/issues/190">#190</a>)</li>
<li><a
href="https://github.com/redis/hiredis-py/commit/f4dd0814c16dc9a8efa72434101d49c97778c830"><code>f4dd081</code></a>
pack: Replace sdsalloc.h with alloc.h (<a
href="https://redirect.github.com/redis/hiredis-py/issues/159">#159</a>)</li>
<li><a
href="https://github.com/redis/hiredis-py/commit/e70af5b94f0881f23f2fb35417bb2e0d0b792f53"><code>e70af5b</code></a>
Bump black from 22.3.0 to 24.3.0 (<a
href="https://redirect.github.com/redis/hiredis-py/issues/185">#185</a>)</li>
<li><a
href="https://github.com/redis/hiredis-py/commit/cc239705fb64f92c6ac3aff36679a300662e0ca7"><code>cc23970</code></a>
Removing Python 3.7 trove (<a
href="https://redirect.github.com/redis/hiredis-py/issues/181">#181</a>)</li>
<li><a
href="https://github.com/redis/hiredis-py/commit/64e3394548fe670e7853a2407799e13daa4bf2cb"><code>64e3394</code></a>
Badge for latest released on Pypi (<a
href="https://redirect.github.com/redis/hiredis-py/issues/182">#182</a>)</li>
<li><a
href="https://github.com/redis/hiredis-py/commit/ba18089d74f991b9b9e7ac9c17f85432f50d0048"><code>ba18089</code></a>
Sync license in metadata with LICENSE file (<a
href="https://redirect.github.com/redis/hiredis-py/issues/183">#183</a>)</li>
<li>See full diff in <a
href="https://github.com/redis/hiredis-py/compare/v2.3.2...v3.0.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hiredis&package-manager=pip&previous-version=2.3.2&new-version=3.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 23:58:19 +00:00
dependabot[bot] e3a0681ecf Bump pyopenssl from 24.1.0 to 24.2.1 (#17465)
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 24.1.0 to
24.2.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst">pyopenssl's
changelog</a>.</em></p>
<blockquote>
<h2>24.2.1 (2024-07-20)</h2>
<p>Backward-incompatible changes:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</p>
<p>Deprecations:
^^^^^^^^^^^^^</p>
<p>Changes:
^^^^^^^^</p>
<ul>
<li>Fixed changelog to remove sphinx specific restructured text
strings.</li>
</ul>
<h2>24.2.0 (2024-07-20)</h2>
<p>Backward-incompatible changes:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</p>
<p>Deprecations:
^^^^^^^^^^^^^</p>
<ul>
<li>Deprecated <code>OpenSSL.crypto.X509Req</code>,
<code>OpenSSL.crypto.load_certificate_request</code>,
<code>OpenSSL.crypto.dump_certificate_request</code>. Instead,
<code>cryptography.x509.CertificateSigningRequest</code>,
<code>cryptography.x509.CertificateSigningRequestBuilder</code>,
<code>cryptography.x509.load_der_x509_csr</code>, or
<code>cryptography.x509.load_pem_x509_csr</code> should be used.</li>
</ul>
<p>Changes:
^^^^^^^^</p>
<ul>
<li>Added type hints for the <code>SSL</code> module.
<code>[#1308](https://github.com/pyca/pyopenssl/issues/1308)
&lt;https://github.com/pyca/pyopenssl/pull/1308&gt;</code>_.</li>
<li>Changed <code>OpenSSL.crypto.PKey.from_cryptography_key</code> to
accept public and private EC, ED25519, ED448 keys.
<code>[#1310](https://github.com/pyca/pyopenssl/issues/1310)
&lt;https://github.com/pyca/pyopenssl/pull/1310&gt;</code>_.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pyca/pyopenssl/commit/8dd94578656d47f854c087783b72431aaeb73d8e"><code>8dd9457</code></a>
24.2.1 (<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1320">#1320</a>)</li>
<li><a
href="https://github.com/pyca/pyopenssl/commit/19f093e0c3db5d7354df0a5256344d9ebc5d931f"><code>19f093e</code></a>
make changelog vanilla rst (<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1319">#1319</a>)</li>
<li><a
href="https://github.com/pyca/pyopenssl/commit/e265b2867bbc4f45a14b31affe4a0be710d2b293"><code>e265b28</code></a>
Prepare for 24.2.0 release (<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1318">#1318</a>)</li>
<li><a
href="https://github.com/pyca/pyopenssl/commit/6943ee524e511138dd78fde6000e6f8e2bdd4b0c"><code>6943ee5</code></a>
Deprecate CSR support in pyOpenSSL (<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1316">#1316</a>)</li>
<li><a
href="https://github.com/pyca/pyopenssl/commit/01b9b5637318bde2490e3f8bb243723546dbcc62"><code>01b9b56</code></a>
Add more type definitions for <code>SSL</code> module, check with mypy
(<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1313">#1313</a>)</li>
<li><a
href="https://github.com/pyca/pyopenssl/commit/cdcb48baf7d5dd0f040f244934c2967b04b9f4bc"><code>cdcb48b</code></a>
Prune redundant <code>:rtype:</code> from SSL module (<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1315">#1315</a>)</li>
<li><a
href="https://github.com/pyca/pyopenssl/commit/b86914d37fc6c0e138e2a0f733dd65dec2efc96c"><code>b86914d</code></a>
Fix <code>ruff</code> invocation (<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1314">#1314</a>)</li>
<li><a
href="https://github.com/pyca/pyopenssl/commit/caa1ab3ac5c5096b20b69fac2e42cb67c6f716d1"><code>caa1ab3</code></a>
Update changelog for PR <a
href="https://redirect.github.com/pyca/pyopenssl/issues/1308">#1308</a>
and <a
href="https://redirect.github.com/pyca/pyopenssl/issues/1310">#1310</a>
(<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1311">#1311</a>)</li>
<li><a
href="https://github.com/pyca/pyopenssl/commit/9a2105501f601798bb2fb1928cee5ed8b21e32e4"><code>9a21055</code></a>
Allow loading EC, ED25519, ED448 public keys from cryptography (<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1310">#1310</a>)</li>
<li><a
href="https://github.com/pyca/pyopenssl/commit/9eaa1073621dd46b3e721183efc318c95aba0d74"><code>9eaa107</code></a>
Add type annotations for the <code>SSL</code> module (<a
href="https://redirect.github.com/pyca/pyopenssl/issues/1308">#1308</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pyca/pyopenssl/compare/24.1.0...24.2.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pyopenssl&package-manager=pip&previous-version=24.1.0&new-version=24.2.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 23:42:36 +00:00
Eric Eastwood de05a64246 Sliding Sync: Add E2EE extension (MSC3884) (#17454)
Spec: [MSC3884](https://github.com/matrix-org/matrix-spec-proposals/pull/3884)

Based on [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575): Sliding Sync
2024-07-22 15:40:06 -05:00
Erik Johnston d221512498 SS: Implement $ME support (#17469)
`$ME` can be used as a substitute for the requester's user ID.
2024-07-22 17:48:09 +01:00
Erik Johnston ed0face8ad Speed up room keys query by using read/write lock (#17461)
Linaerizing all access slows things down when devices try and fetch lots
of keys on login
2024-07-22 14:51:17 +01:00
dependabot[bot] 73529d3732 Bump ruff from 0.5.0 to 0.5.4 (#17466) 2024-07-22 14:29:06 +01:00
dependabot[bot] 1648337775 Bump sentry-sdk from 2.8.0 to 2.10.0 (#17467) 2024-07-22 14:28:54 +01:00
Shay dc8ddc6472 Prepare for authenticated media freeze (#17433)
As part of the rollout of
[MSC3916](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3916-authentication-for-media.md)
this PR adds support for designating authenticated media and ensuring
that authenticated media is not served over unauthenticated endpoints.
2024-07-22 10:33:17 +01:00
Erik Johnston d3f9afd8d9 Add a cache on get_rooms_for_local_user_where_membership_is (#17460)
As it gets used in sliding sync.

We basically invalidate it in all the same places as
`get_rooms_for_user`. Most of the changes are due to needing the
arguments you pass in to be hashable (which lists aren't)
2024-07-19 16:19:15 +01:00
Erik Johnston 43c865f7c9 Generate room sync data concurrently (#17458)
This is also what we do for standard `/sync`.
2024-07-19 12:09:39 +01:00
dependabot[bot] 71d83477cb Bump sentry-sdk from 2.6.0 to 2.8.0 (#17456) 2024-07-19 11:02:38 +01:00
Ben Banfield-Zanin 6a01af59e1 Improve default_power_level_content_override documentation (#17451)
Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
2024-07-18 13:32:32 +01:00
Erik Johnston f583f1dce4 Revert "Bump setuptools from 67.6.0 to 70.0.0" (#17455)
Reverts element-hq/synapse#17448

We hit a bug when deploying with synctl:

```
Traceback (most recent call last):
  File "/home/synapse/env-python311/bin/synctl", line 33, in <module>
    sys.exit(load_entry_point('matrix-synapse', 'console_scripts', 'synctl')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/synapse/env-python311/bin/synctl", line 25, in importlib_load_entry_point
    return next(matches).load()
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/importlib/metadata/__init__.py", line 202, in load
    module = import_module(match.group('module'))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/synapse/src/synapse/_scripts/synctl.py", line 37, in <module>
    from synapse.config import find_config_files
  File "/home/synapse/src/synapse/config/__init__.py", line 22, in <module>
    from ._base import ConfigError, find_config_files
  File "/home/synapse/src/synapse/config/_base.py", line 49, in <module>
    import pkg_resources
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 3282, in <module>
    @_call_aside
     ^^^^^^^^^^^
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 3266, in _call_aside
    f(*args, **kwargs)
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 3295, in _initialize_master_working_set
    working_set = _declare_state('object', 'working_set', WorkingSet._build_master())
                                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 589, in _build_master
    ws.require(__requires__)
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 926, in require
    needed = self.resolve(parse_requirements(requirements))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 787, in resolve
    dist = self._resolve_dist(
           ^^^^^^^^^^^^^^^^^^^
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 816, in _resolve_dist
    env = Environment(self.entries)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 1014, in __init__
    self.scan(search_path)
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 1046, in scan
    for dist in find_distributions(item):
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 2091, in find_on_path
    yield from factory(fullpath)
               ^^^^^^^^^^^^^^^^^
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 2183, in resolve_egg_link
    return next(dist_groups, ())
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 2179, in <genexpr>
    resolved_paths = (
                     ^
  File "/home/synapse/env-python311/lib/python3.11/site-packages/pkg_resources/__init__.py", line 2167, in non_empty_lines
    for line in _read_utf8_with_fallback(path).splitlines():
                ^^^^^^^^^^^^^^^^^^^^^^^^
NameError: name '_read_utf8_with_fallback' is not defined
```
2024-07-18 12:59:53 +01:00
Eric Eastwood a574de0062 Add m.room.create to default bump event types (#17453)
Add `m.room.create` to default bump event types

This probably helps when no messages have been sent in the room and it
was just created.
2024-07-18 12:49:53 +01:00
Eric Eastwood 3fee32ed6b Order heroes by stream_ordering (as spec'ed) (#17435)
The spec specifically mentions `stream_ordering` but that's a Synapse specific concept. In any case, the essence of the spec is basically the first 5 members of the room which `stream_ordering` accomplishes.

Split off from https://github.com/element-hq/synapse/pull/17419#discussion_r1671342794

## Spec compliance

> This should be the first 5 members of the room, **ordered by stream ordering**, which are joined or invited. The list must never include the client’s own user ID. When no joined or invited members are available, this should consist of the banned and left users.
>
> *-- https://spec.matrix.org/v1.10/client-server-api/#_matrixclientv3sync_roomsummary*

Related to https://github.com/matrix-org/matrix-spec/issues/1334
2024-07-17 13:10:15 -05:00
Till Faelligen 5884f0a956 Merge branch 'master' into develop 2024-07-16 14:33:58 +02:00
dependabot[bot] 79924aebef Bump mypy from 1.9.0 to 1.10.1 (#17445) 2024-07-16 12:08:06 +01:00
Till Faelligen 574aa53126 1.111.0 2024-07-16 12:55:26 +02:00
dependabot[bot] 83894180b2 Bump matrix-org/done-action from 2 to 3 (#17440) 2024-07-16 11:35:47 +01:00
Shay 429ecb7564 Handle remote download responses with UNKNOWN_LENGTH more gracefully (#17439)
Prior to this PR, remote downloads which did not provide a
`content-length` were decremented from the remote download ratelimiter
at the max allowable size, leading to excessive ratelimiting - see
https://github.com/element-hq/synapse/issues/17394.

This PR adds a linearizer to limit concurrent remote downloads to 6 per
IP address, and decrements remote downloads without a `content-length`
from the ratelimiter *after* the download is complete and the response
length is known.

Also adds logic to ensure that responses with a known length respect the
`max_download_size`.
2024-07-16 11:13:55 +01:00
dependabot[bot] 9e1acea051 Bump setuptools from 67.6.0 to 70.0.0 (#17448) 2024-07-16 10:06:05 +01:00
Shay 899d33f2ba Remove unnecessary call to resume producing in fake channel (#17449)
This fell out of the authenticated media work - this bit of code masked
a bug but does not break anything when removed, so probably should be
removed.
2024-07-16 09:52:39 +01:00
Erik Johnston df11af14db Fix bug where sync could get stuck when using workers (#17438)
This is because we serialized the token wrong if the instance map
contained entries from before the minimum token.
2024-07-15 16:13:04 +01:00
dependabot[bot] d88ba45db9 Bump types-jsonschema from 4.22.0.20240610 to 4.23.0.20240712 (#17446) 2024-07-15 13:58:28 +01:00
dependabot[bot] 14f2b1eb00 Bump bytes from 1.6.0 to 1.6.1 (#17441) 2024-07-15 13:58:12 +01:00
dependabot[bot] 2af729a193 Bump ulid from 1.1.2 to 1.1.3 (#17442) 2024-07-15 13:57:45 +01:00
dependabot[bot] 0de0689ae8 Bump jsonschema from 4.22.0 to 4.23.0 (#17444) 2024-07-15 13:57:30 +01:00
dependabot[bot] 4c44020838 Bump twine from 5.1.0 to 5.1.1 (#17443) 2024-07-15 13:57:13 +01:00
Quentin Gliech 4f6194492a Make sure we use the right logic for enabling the media repo. (#17424)
This removes the `enable_media_repo` attribute on the server config in
favour of always using the `can_load_media_repo` in the media config.
This should avoid issues like in #17420 in the future
2024-07-15 11:42:59 +02:00
Eric Eastwood ab62aa09da Add room subscriptions to Sliding Sync /sync (#17432)
Add room subscriptions to Sliding Sync `/sync`

Based on
[MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575):
Sliding Sync

Currently, you can only subscribe to rooms you have had *any* membership
in before.

In the future, we will allow `world_readable` rooms to be subscribed to
without joining.
2024-07-15 10:37:10 +01:00
Eric Eastwood fb66e938b2 Add is_dm room field to Sliding Sync /sync (#17429)
Based on
[MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575):
Sliding Sync
2024-07-11 18:19:26 -05:00
Eric Eastwood 5a97bbd895 Add heroes and room summary fields to Sliding Sync /sync (#17419)
Additional room summary fields: `joined_count`, `invited_count`

Based on
[MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575):
Sliding Sync
2024-07-11 14:05:38 -05:00
Erik Johnston 606da398fc Fix filtering room types on remote rooms (#17434)
We can only fetch room types for rooms the server is in, so we need to
only filter rooms that we're joined to.

Also includes a perf fix to bulk fetch room types.
2024-07-11 16:00:44 +01:00
Travis Ralston 677142b6a9 Fix docs on record_action to clarify the actions are applied (#17426)
This looks like a copy/paste error: the function doesn't reject
anything, but instead allows the action count to go through regardless.
The remainder of the function's documentation appears correct.
2024-07-11 14:03:13 +01:00
villepeh 342f0c35b7 Add Red Hat Enterprise Linux and Rocky Linux installation instructions (#17423)
Added RHEL/Rocky install instructions (PyPI). Instructions cover
versions 8 and 9 which are the only supported ones - except for RHEL7
which is now on extended life cycle support phase.

Large part of the guide is for installing Python 3.11 or 3.12. RHEL8
ships with Python 3.6 and RHEL9 ships with 3.9. Newer Python versions
can be installed easily as they don't interfere with OS software that
still relies on the default Python version.

I was first planning to add prerequisites part to the prerequisites
section and then install instructions on the top of the page but that
section is for pre-built packages so it just didn't sound right. So I
just dumped everything to the PyPI section of the page. But suggestions
to change are welcome.

I also didn't combine these with Fedora section. I haven't tested those
packages on RHEL and Fedora ships with Python 3.12 out-of-box.
2024-07-11 14:02:19 +01:00
Joe Groocock 5871daf877 Use consistent casing between FROM and AS (#17431)
Signed-off-by: Joe Groocock <me@frebib.net>
2024-07-11 13:56:25 +01:00
dependabot[bot] 30e14c8510 Bump zipp from 3.15.0 to 3.19.1 (#17427) 2024-07-11 11:54:21 +01:00
Will Lewis 3232bc2982 Upload new logo with white bg and update readme to use it (#17387) 2024-07-10 14:59:24 +01:00
Erik Johnston 4ca13ce0dd Handle to-device extensions to Sliding Sync (#17416)
Implements MSC3885

---------

Co-authored-by: Eric Eastwood <eric.eastwood@beta.gouv.fr>
2024-07-10 11:58:42 +01:00
Quentin Gliech 8e229535fa Merge branch 'release-v1.111' into develop 2024-07-10 11:36:07 +02:00
Quentin Gliech e0ff850cb7 1.111.0rc2 2024-07-10 10:47:35 +02:00
Erik Johnston 22fbc5be54 Fix new media APIs when using synapse.app.media_repository (#17420)
Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
2024-07-09 18:29:32 +01:00
Eric Eastwood 1cf3ff6b40 Add rooms name and avatar to Sliding Sync /sync (#17418)
Based on [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575): Sliding Sync
2024-07-09 12:26:45 -05:00
Andrew Morgan 62d8b0361b Note the new federated media worker endpoints in the worker docs & upgrade notes (#17421) 2024-07-09 16:41:51 +01:00
Andrew Morgan 4d6f7c0fc9 Route auth'd fed media requests to media repo in Complement tests (#17422) 2024-07-09 15:12:11 +00:00
Quentin Gliech d48061b7e6 Fix up the changelog 2024-07-09 12:36:24 +02:00
Quentin Gliech 963a60c7e7 Merge remote-tracking branch 'origin/develop' into release-v1.111 2024-07-09 12:36:15 +02:00
Quentin Gliech 8e7da35402 Tweak the changelog for v1.111.0rc1
Co-authored-by: Andrew Morgan <andrewm@element.io>
2024-07-09 12:04:52 +02:00
Erik Johnston 028b103ae0 Fix exception when failing to talk to remote server (#17411)
Broke in #17381
2024-07-09 10:04:40 +00:00
Quentin Gliech abb1384502 1.111.0rc1 2024-07-09 11:51:51 +02:00
Erik Johnston 0ed1c64c83 Fix /versions requests (#17410)
We need it to work on workers and allow guest access.

Broke by #17392
2024-07-09 10:25:44 +01:00
dependabot[bot] 1353fb3347 Bump ijson from 3.2.3 to 3.3.0 (#17413) 2024-07-09 09:31:57 +01:00
dependabot[bot] b15e17ce6e Bump pillow from 10.3.0 to 10.4.0 (#17412) 2024-07-09 09:31:42 +01:00
Erik Johnston 8cdd2d214e Fix bug in sliding sync when using old DB. (#17398)
We don't necessarily have `instance_name` for old events (before we
support multiple event persisters). We treat those as if the
`instance_name` was "master".

---------

Co-authored-by: Eric Eastwood <eric.eastwood@beta.gouv.fr>
2024-07-08 20:30:23 +01:00
Eric Eastwood 3fef535ff2 Add rooms.bump_stamp to Sliding Sync /sync for easier client-side sorting (#17395)
`bump_stamp` corresponds to the `stream_ordering` of the latest `DEFAULT_BUMP_EVENT_TYPES` in the room. This helps clients sort more readily without them needing to pull in a bunch of the timeline to determine the last activity. `bump_event_types` is a thing because for example, we don't want display name changes to mark the room as unread and bump it to the top. For encrypted rooms, we just have to consider any activity as a bump because we can't see the content and the client has to figure it out for themselves.

Outside of Synapse, `bump_stamp` is just a free-form counter so other implementations could use `received_ts`or `origin_server_ts` (see the [*Security considerations* section in MSC3575 about the potential pitfalls of using `origin_server_ts`](https://github.com/matrix-org/matrix-spec-proposals/blob/kegan/sync-v3/proposals/3575-sync.md#security-considerations)). It doesn't have any guarantee about always going up. In the Synapse case, it could go down if an event was redacted/removed (or purged in cases of retention policies).

In the future, we could add `bump_event_types` as [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) mentions if people need to customize the event types.

---

In the Sliding Sync proxy, a similar [`timestamp` field was added](https://github.com/matrix-org/sliding-sync/pull/247) for the same purpose but the name is not obvious what it pertains to or what it's for.

The `timestamp` field was also added to Ruma in https://github.com/ruma/ruma/pull/1622
2024-07-08 13:17:08 -05:00
dependabot[bot] 62134dcc77 Bump serde_json from 1.0.119 to 1.0.120 (#17408) 2024-07-08 15:24:30 +01:00
dependabot[bot] 23eed4f72a Bump serde from 1.0.203 to 1.0.204 (#17409) 2024-07-08 15:24:21 +01:00
dependabot[bot] 4721177241 Bump mypy-zope from 1.0.4 to 1.0.5 (#17414) 2024-07-08 15:24:11 +01:00
dependabot[bot] 7879f288df Bump pydantic from 2.7.1 to 2.8.2 (#17415) 2024-07-08 15:24:01 +01:00
Travis Ralston ccbd619b43 Declare support for Matrix 1.11 (#17403)
Previous: https://github.com/element-hq/synapse/pull/17082
Fixes https://github.com/element-hq/synapse/issues/17402
See https://github.com/element-hq/synapse/issues/17402 for context

**Blocked on https://github.com/element-hq/synapse/pull/17388**
(required for spec compliance)
2024-07-08 07:37:28 -06:00
Quentin Gliech c896030f67 MSC3861: allow overriding the introspection endpoint (#17406)
This makes it easier to go through an internal endpoint instead of the
public facing URL when introspecting tokens, reducing latency.
2024-07-08 14:08:11 +02:00
dependabot[bot] 4d7e53707c Bump certifi from 2023.7.22 to 2024.7.4 (#17404) 2024-07-08 10:32:09 +01:00
Shay cf69f8d59b Support MSC3916 by adding a federation /thumbnail endpoint and authenticated _matrix/client/v1/media/thumbnail endpoint (#17388)
[MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916)
added the endpoints `_matrix/federation/v1/media/thumbnail` and the
authenticated `_matrix/client/v1/media/thumbnail`.

This PR implements those endpoints, along with stabilizing
`_matrix/client/v1/media/config` and
`_matrix/client/v1/media/preview_url`.

Complement tests are at
https://github.com/matrix-org/complement/pull/728
2024-07-08 10:11:20 +01:00
dependabot[bot] 20de685a4b Bump ruff from 0.3.7 to 0.5.0 (#17381) 2024-07-05 12:35:57 +00:00
Erik Johnston 8e9e6f1a0a Allow enabling sliding sync per-user (#17393)
Based on #17392
2024-07-05 13:04:27 +01:00
Erik Johnston 57538eb4d9 Finish up work to allow per-user feature flags (#17392)
Follows on from @H-Shay's great work at
https://github.com/matrix-org/synapse/pull/15344 and MSC4026.

Also enables its use for MSC3881, mainly as an easy but concrete example
of how to use it.
2024-07-05 13:02:35 +01:00
Sandro 45b35f8eae Fix links to MSC3916 (#17397) 2024-07-05 12:00:19 +01:00
Travis Ralston 2ec257d608 Upon deactivation, forget all of the user's rooms (#17400)
This can help ensure that the rooms are eventually purged if the other
local users also forget them. Synapse already clears some of the room
information as part of the `_background_remove_left_rooms` background
task, but this doesn't catch `events`, `event_json`, etc.
2024-07-05 11:56:52 +01:00
Hugh Nimmo-Smith daeaeb2a7b Fix links in README (#17379)
I don't think this warrants a changelog?

### Pull Request Checklist

<!-- Please read
https://element-hq.github.io/synapse/latest/development/contributing_guide.html
before submitting your pull request -->

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
- Be a short description of your change which makes sense to users.
"Fixed a bug that prevented receiving messages from other servers."
instead of "Moved X method from `EventStore` to `EventWorkerStore`.".
  - Use markdown where necessary, mostly for `code blocks`.
  - End with either a period (.) or an exclamation mark (!).
  - Start with a capital letter.
- Feel free to credit yourself, by adding a sentence "Contributed by
@github_username." or "Contributed by [Your Name]." to the end of the
entry.
* [ ] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct
(run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))
2024-07-05 10:34:02 +00:00
Quentin Gliech 7786ae7e1c Make the release script create a release branch for Complement as well (#17318) 2024-07-05 09:07:45 +02:00
Eric Eastwood 22aeb78b77 Add rooms.required_state to Sliding Sync /sync (#17342)
Also handles excluding rooms with partial state when people are asking for room membership events unless it's `$LAZY` room membership.
2024-07-04 12:25:36 -05:00
Eric Eastwood a9d2e40ea4 Changelog entries only get merged if they have the same content and extension (#17399)
Changelog entries only get merged if they have the same content and
extension

See
https://github.com/element-hq/synapse/pull/17301#discussion_r1665387218
2024-07-04 17:21:25 +01:00
149 changed files with 15837 additions and 3246 deletions
+3 -3
View File
@@ -139,7 +139,7 @@ jobs:
- name: Semantic checks (ruff) - name: Semantic checks (ruff)
# --quiet suppresses the update check. # --quiet suppresses the update check.
run: poetry run ruff --quiet . run: poetry run ruff check --quiet .
lint-mypy: lint-mypy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -305,7 +305,7 @@ jobs:
- lint-readme - lint-readme
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: matrix-org/done-action@v2 - uses: matrix-org/done-action@v3
with: with:
needs: ${{ toJSON(needs) }} needs: ${{ toJSON(needs) }}
@@ -737,7 +737,7 @@ jobs:
- linting-done - linting-done
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: matrix-org/done-action@v2 - uses: matrix-org/done-action@v3
with: with:
needs: ${{ toJSON(needs) }} needs: ${{ toJSON(needs) }}
+182 -1
View File
@@ -1,3 +1,184 @@
# Synapse 1.112.0 (2024-07-30)
This security release is to update our locked dependency on Twisted to 24.7.0rc1, which includes a security fix for [CVE-2024-41671 / GHSA-c8m8-j448-xjx7: Disordered HTTP pipeline response in twisted.web, again](https://github.com/twisted/twisted/security/advisories/GHSA-c8m8-j448-xjx7).
Note that this security fix is also available as **Synapse 1.111.1**, which does not include the rest of the changes in Synapse 1.112.0.
This issue means that, if multiple HTTP requests are pipelined in the same TCP connection, Synapse can send responses to the wrong HTTP request.
If a reverse proxy was configured to use HTTP pipelining, this could result in responses being sent to the wrong user, severely harming confidentiality.
With that said, despite being a high severity issue, **we consider it unlikely that Synapse installations will be affected**.
The use of HTTP pipelining in this fashion would cause worse performance for clients (request-response latencies would be increased as users' responses would be artificially blocked behind other users' slow requests). Further, Nginx and Haproxy, two common reverse proxies, do not appear to support configuring their upstreams to use HTTP pipelining and thus would not be affected. For both of these reasons, we consider it unlikely that a Synapse deployment would be set up in such a configuration.
Despite that, we cannot rule out that some installations may exist with this unusual setup and so we are releasing this security update today.
**pip users:** Note that by default, upgrading Synapse using pip will not automatically upgrade Twisted. **Please manually install the new version of Twisted** using `pip install Twisted==24.7.0rc1`. Note also that even the `--upgrade-strategy=eager` flag to `pip install -U matrix-synapse` will not upgrade Twisted to a patched version because it is only a release candidate at this time.
### Internal Changes
- Upgrade locked dependency on Twisted to 24.7.0rc1. ([\#17502](https://github.com/element-hq/synapse/issues/17502))
# Synapse 1.112.0rc1 (2024-07-23)
Please note that this release candidate does not include the security dependency update
included in version 1.111.1 as this version was released before 1.111.1.
The same security fix can be found in the full release of 1.112.0.
### Features
- Add to-device extension support to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17416](https://github.com/element-hq/synapse/issues/17416))
- Populate `name`/`avatar` fields in experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17418](https://github.com/element-hq/synapse/issues/17418))
- Populate `heroes` and room summary fields (`joined_count`, `invited_count`) in experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17419](https://github.com/element-hq/synapse/issues/17419))
- Populate `is_dm` room field in experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17429](https://github.com/element-hq/synapse/issues/17429))
- Add room subscriptions to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17432](https://github.com/element-hq/synapse/issues/17432))
- Prepare for authenticated media freeze. ([\#17433](https://github.com/element-hq/synapse/issues/17433))
- Add E2EE extension support to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17454](https://github.com/element-hq/synapse/issues/17454))
### Bugfixes
- Add configurable option to always include offline users in presence sync results. Contributed by @Michael-Hollister. ([\#17231](https://github.com/element-hq/synapse/issues/17231))
- Fix bug in experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint when using room type filters and the user has one or more remote invites. ([\#17434](https://github.com/element-hq/synapse/issues/17434))
- Order `heroes` by `stream_ordering` as the Matrix specification states (applies to `/sync`). ([\#17435](https://github.com/element-hq/synapse/issues/17435))
- Fix rare bug where `/sync` would break for a user when using workers with multiple stream writers. ([\#17438](https://github.com/element-hq/synapse/issues/17438))
### Improved Documentation
- Update the readme image to have a white background, so that it is readable in dark mode. ([\#17387](https://github.com/element-hq/synapse/issues/17387))
- Add Red Hat Enterprise Linux and Rocky Linux 8 and 9 installation instructions. ([\#17423](https://github.com/element-hq/synapse/issues/17423))
- Improve documentation for the [`default_power_level_content_override`](https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#default_power_level_content_override) config option. ([\#17451](https://github.com/element-hq/synapse/issues/17451))
### Internal Changes
- Make sure we always use the right logic for enabling the media repo. ([\#17424](https://github.com/element-hq/synapse/issues/17424))
- Fix argument documentation for method `RateLimiter.record_action`. ([\#17426](https://github.com/element-hq/synapse/issues/17426))
- Reduce volume of 'Waiting for current token' logs, which were introduced in v1.109.0. ([\#17428](https://github.com/element-hq/synapse/issues/17428))
- Limit concurrent remote downloads to 6 per IP address, and decrement remote downloads without a content-length from the ratelimiter after the download is complete. ([\#17439](https://github.com/element-hq/synapse/issues/17439))
- Remove unnecessary call to resume producing in fake channel. ([\#17449](https://github.com/element-hq/synapse/issues/17449))
- Update experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint to bump room when it is created. ([\#17453](https://github.com/element-hq/synapse/issues/17453))
- Speed up generating sliding sync responses. ([\#17458](https://github.com/element-hq/synapse/issues/17458))
- Add cache to `get_rooms_for_local_user_where_membership_is` to speed up sliding sync. ([\#17460](https://github.com/element-hq/synapse/issues/17460))
- Speed up fetching room keys from backup. ([\#17461](https://github.com/element-hq/synapse/issues/17461))
- Speed up sorting of the room list in sliding sync. ([\#17468](https://github.com/element-hq/synapse/issues/17468))
- Implement handling of `$ME` as a state key in sliding sync. ([\#17469](https://github.com/element-hq/synapse/issues/17469))
### Updates to locked dependencies
* Bump bytes from 1.6.0 to 1.6.1. ([\#17441](https://github.com/element-hq/synapse/issues/17441))
* Bump hiredis from 2.3.2 to 3.0.0. ([\#17464](https://github.com/element-hq/synapse/issues/17464))
* Bump jsonschema from 4.22.0 to 4.23.0. ([\#17444](https://github.com/element-hq/synapse/issues/17444))
* Bump matrix-org/done-action from 2 to 3. ([\#17440](https://github.com/element-hq/synapse/issues/17440))
* Bump mypy from 1.9.0 to 1.10.1. ([\#17445](https://github.com/element-hq/synapse/issues/17445))
* Bump pyopenssl from 24.1.0 to 24.2.1. ([\#17465](https://github.com/element-hq/synapse/issues/17465))
* Bump ruff from 0.5.0 to 0.5.4. ([\#17466](https://github.com/element-hq/synapse/issues/17466))
* Bump sentry-sdk from 2.6.0 to 2.8.0. ([\#17456](https://github.com/element-hq/synapse/issues/17456))
* Bump sentry-sdk from 2.8.0 to 2.10.0. ([\#17467](https://github.com/element-hq/synapse/issues/17467))
* Bump setuptools from 67.6.0 to 70.0.0. ([\#17448](https://github.com/element-hq/synapse/issues/17448))
* Bump twine from 5.1.0 to 5.1.1. ([\#17443](https://github.com/element-hq/synapse/issues/17443))
* Bump types-jsonschema from 4.22.0.20240610 to 4.23.0.20240712. ([\#17446](https://github.com/element-hq/synapse/issues/17446))
* Bump ulid from 1.1.2 to 1.1.3. ([\#17442](https://github.com/element-hq/synapse/issues/17442))
* Bump zipp from 3.15.0 to 3.19.1. ([\#17427](https://github.com/element-hq/synapse/issues/17427))
# Synapse 1.111.1 (2024-07-30)
This security release is to update our locked dependency on Twisted to 24.7.0rc1, which includes a security fix for [CVE-2024-41671 / GHSA-c8m8-j448-xjx7: Disordered HTTP pipeline response in twisted.web, again](https://github.com/twisted/twisted/security/advisories/GHSA-c8m8-j448-xjx7).
This issue means that, if multiple HTTP requests are pipelined in the same TCP connection, Synapse can send responses to the wrong HTTP request.
If a reverse proxy was configured to use HTTP pipelining, this could result in responses being sent to the wrong user, severely harming confidentiality.
With that said, despite being a high severity issue, **we consider it unlikely that Synapse installations will be affected**.
The use of HTTP pipelining in this fashion would cause worse performance for clients (request-response latencies would be increased as users' responses would be artificially blocked behind other users' slow requests). Further, Nginx and Haproxy, two common reverse proxies, do not appear to support configuring their upstreams to use HTTP pipelining and thus would not be affected. For both of these reasons, we consider it unlikely that a Synapse deployment would be set up in such a configuration.
Despite that, we cannot rule out that some installations may exist with this unusual setup and so we are releasing this security update today.
**pip users:** Note that by default, upgrading Synapse using pip will not automatically upgrade Twisted. **Please manually install the new version of Twisted** using `pip install Twisted==24.7.0rc1`. Note also that even the `--upgrade-strategy=eager` flag to `pip install -U matrix-synapse` will not upgrade Twisted to a patched version because it is only a release candidate at this time.
### Internal Changes
- Upgrade locked dependency on Twisted to 24.7.0rc1. ([\#17502](https://github.com/element-hq/synapse/issues/17502))
# Synapse 1.111.0 (2024-07-16)
No significant changes since 1.111.0rc2.
# Synapse 1.111.0rc2 (2024-07-10)
### Bugfixes
- Fix bug where using `synapse.app.media_repository` worker configuration would break the new media endpoints. ([\#17420](https://github.com/element-hq/synapse/issues/17420))
### Improved Documentation
- Document the new federation media worker endpoints in the [upgrade notes](https://element-hq.github.io/synapse/v1.111/upgrade.html) and [worker docs](https://element-hq.github.io/synapse/v1.111/workers.html). ([\#17421](https://github.com/element-hq/synapse/issues/17421))
### Internal Changes
- Route authenticated federation media requests to media repository workers in Complement tests. ([\#17422](https://github.com/element-hq/synapse/issues/17422))
# Synapse 1.111.0rc1 (2024-07-09)
### Features
- Add `rooms` data to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17320](https://github.com/element-hq/synapse/issues/17320))
- Add `room_types`/`not_room_types` filtering to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17337](https://github.com/element-hq/synapse/issues/17337))
- Return "required state" in experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17342](https://github.com/element-hq/synapse/issues/17342))
- Support [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3916-authentication-for-media.md) by adding [`_matrix/client/v1/media/download`](https://spec.matrix.org/v1.11/client-server-api/#get_matrixclientv1mediadownloadservernamemediaid) endpoint. ([\#17365](https://github.com/element-hq/synapse/issues/17365))
- Support [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/blob/rav/authentication-for-media/proposals/3916-authentication-for-media.md)
by adding [`_matrix/client/v1/media/thumbnail`](https://spec.matrix.org/v1.11/client-server-api/#get_matrixclientv1mediathumbnailservernamemediaid), [`_matrix/federation/v1/media/thumbnail`](https://spec.matrix.org/v1.11/server-server-api/#get_matrixfederationv1mediathumbnailmediaid) endpoints and stabilizing the
remaining [`_matrix/client/v1/media`](https://spec.matrix.org/v1.11/client-server-api/#get_matrixclientv1mediaconfig) endpoints. ([\#17388](https://github.com/element-hq/synapse/issues/17388))
- Add `rooms.bump_stamp` for easier client-side sorting in experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17395](https://github.com/element-hq/synapse/issues/17395))
- Forget all of a user's rooms upon deactivation, preventing local room purges from being blocked on deactivated users. ([\#17400](https://github.com/element-hq/synapse/issues/17400))
- Declare support for [Matrix 1.11](https://matrix.org/blog/2024/06/20/matrix-v1.11-release/). ([\#17403](https://github.com/element-hq/synapse/issues/17403))
- [MSC3861](https://github.com/matrix-org/matrix-spec-proposals/pull/3861): allow overriding the introspection endpoint. ([\#17406](https://github.com/element-hq/synapse/issues/17406))
### Bugfixes
- Fix rare race which caused no new to-device messages to be received from remote server. ([\#17362](https://github.com/element-hq/synapse/issues/17362))
- Fix bug in experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint when using an old database. ([\#17398](https://github.com/element-hq/synapse/issues/17398))
### Improved Documentation
- Clarify that `url_preview_url_blacklist` is a usability feature. ([\#17356](https://github.com/element-hq/synapse/issues/17356))
- Fix broken links in README. ([\#17379](https://github.com/element-hq/synapse/issues/17379))
- Clarify that changelog content *and file extension* need to match in order for entries to merge. ([\#17399](https://github.com/element-hq/synapse/issues/17399))
### Internal Changes
- Make the release script create a release branch for Complement as well. ([\#17318](https://github.com/element-hq/synapse/issues/17318))
- Fix uploading packages to PyPi. ([\#17363](https://github.com/element-hq/synapse/issues/17363))
- Add CI check for the README. ([\#17367](https://github.com/element-hq/synapse/issues/17367))
- Fix linting errors from new `ruff` version. ([\#17381](https://github.com/element-hq/synapse/issues/17381), [\#17411](https://github.com/element-hq/synapse/issues/17411))
- Fix building debian packages on non-clean checkouts. ([\#17390](https://github.com/element-hq/synapse/issues/17390))
- Finish up work to allow per-user feature flags. ([\#17392](https://github.com/element-hq/synapse/issues/17392), [\#17410](https://github.com/element-hq/synapse/issues/17410))
- Allow enabling sliding sync per-user. ([\#17393](https://github.com/element-hq/synapse/issues/17393))
### Updates to locked dependencies
* Bump certifi from 2023.7.22 to 2024.7.4. ([\#17404](https://github.com/element-hq/synapse/issues/17404))
* Bump cryptography from 42.0.7 to 42.0.8. ([\#17382](https://github.com/element-hq/synapse/issues/17382))
* Bump ijson from 3.2.3 to 3.3.0. ([\#17413](https://github.com/element-hq/synapse/issues/17413))
* Bump log from 0.4.21 to 0.4.22. ([\#17384](https://github.com/element-hq/synapse/issues/17384))
* Bump mypy-zope from 1.0.4 to 1.0.5. ([\#17414](https://github.com/element-hq/synapse/issues/17414))
* Bump pillow from 10.3.0 to 10.4.0. ([\#17412](https://github.com/element-hq/synapse/issues/17412))
* Bump pydantic from 2.7.1 to 2.8.2. ([\#17415](https://github.com/element-hq/synapse/issues/17415))
* Bump ruff from 0.3.7 to 0.5.0. ([\#17381](https://github.com/element-hq/synapse/issues/17381))
* Bump serde from 1.0.203 to 1.0.204. ([\#17409](https://github.com/element-hq/synapse/issues/17409))
* Bump serde_json from 1.0.117 to 1.0.120. ([\#17385](https://github.com/element-hq/synapse/issues/17385), [\#17408](https://github.com/element-hq/synapse/issues/17408))
* Bump types-setuptools from 69.5.0.20240423 to 70.1.0.20240627. ([\#17380](https://github.com/element-hq/synapse/issues/17380))
# Synapse 1.110.0 (2024-07-03) # Synapse 1.110.0 (2024-07-03)
No significant changes since 1.110.0rc3. No significant changes since 1.110.0rc3.
@@ -48,7 +229,7 @@ No significant changes since 1.110.0rc3.
This is useful for scripts that bootstrap user accounts with initial passwords. ([\#17304](https://github.com/element-hq/synapse/issues/17304)) This is useful for scripts that bootstrap user accounts with initial passwords. ([\#17304](https://github.com/element-hq/synapse/issues/17304))
- Add support for via query parameter from [MSC4156](https://github.com/matrix-org/matrix-spec-proposals/pull/4156). ([\#17322](https://github.com/element-hq/synapse/issues/17322)) - Add support for via query parameter from [MSC4156](https://github.com/matrix-org/matrix-spec-proposals/pull/4156). ([\#17322](https://github.com/element-hq/synapse/issues/17322))
- Add `is_invite` filtering to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17335](https://github.com/element-hq/synapse/issues/17335)) - Add `is_invite` filtering to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17335](https://github.com/element-hq/synapse/issues/17335))
- Support [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/blob/rav/authentication-for-media/proposals/3916-authentication-for-media.md) by adding a federation /download endpoint. ([\#17350](https://github.com/element-hq/synapse/issues/17350)) - Support [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3916-authentication-for-media.md) by adding a federation /download endpoint. ([\#17350](https://github.com/element-hq/synapse/issues/17350))
### Bugfixes ### Bugfixes
Generated
+11 -10
View File
@@ -67,9 +67,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.6.0" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@@ -485,18 +485,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -505,11 +505,12 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.119" version = "1.0.121"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr",
"ryu", "ryu",
"serde", "serde",
] ]
@@ -597,9 +598,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]] [[package]]
name = "ulid" name = "ulid"
version = "1.1.2" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259" checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"rand", "rand",
+4 -4
View File
@@ -1,4 +1,4 @@
.. image:: https://github.com/element-hq/product/assets/87339233/7abf477a-5277-47f3-be44-ea44917d8ed7 .. image:: ./docs/element_logo_white_bg.svg
:height: 60px :height: 60px
**Element Synapse - Matrix homeserver implementation** **Element Synapse - Matrix homeserver implementation**
@@ -179,10 +179,10 @@ desired ``localpart`` in the 'User name' box.
----------------------- -----------------------
Enterprise quality support for Synapse including SLAs is available as part of an Enterprise quality support for Synapse including SLAs is available as part of an
`Element Server Suite (ESS) <https://element.io/pricing>` subscription. `Element Server Suite (ESS) <https://element.io/pricing>`_ subscription.
If you are an existing ESS subscriber then you can raise a `support request <https://ems.element.io/support>` If you are an existing ESS subscriber then you can raise a `support request <https://ems.element.io/support>`_
and access the `knowledge base <https://ems-docs.element.io>`. and access the `knowledge base <https://ems-docs.element.io>`_.
🤝 Community support 🤝 Community support
-------------------- --------------------
-1
View File
@@ -1 +0,0 @@
Add `rooms` data to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.
-1
View File
@@ -1 +0,0 @@
Add `room_types`/`not_room_types` filtering to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.
-1
View File
@@ -1 +0,0 @@
Clarify `url_preview_url_blacklist` is a usability feature.
-1
View File
@@ -1 +0,0 @@
Fix rare race which causes no new to-device messages to be received from remote server.
-1
View File
@@ -1 +0,0 @@
Fix uploading packages to PyPi.
-1
View File
@@ -1 +0,0 @@
Support [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/blob/rav/authentication-for-media/proposals/3916-authentication-for-media.md) by adding _matrix/client/v1/media/download endpoint.
-1
View File
@@ -1 +0,0 @@
Add CI check for the README.
-1
View File
@@ -1 +0,0 @@
Fix building debian packages on non-clean checkouts.
-1
View File
@@ -1 +0,0 @@
Pin CI to complement release branch for releases.
+1
View File
@@ -0,0 +1 @@
Track which rooms have been sent to clients in the experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.
+1
View File
@@ -0,0 +1 @@
Update experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint to handle invite/knock rooms when filtering.
+1
View File
@@ -0,0 +1 @@
Change sliding sync to use their own token format in preparation for storing per-connection state.
+1
View File
@@ -0,0 +1 @@
Update the [`allowed_local_3pids`](https://element-hq.github.io/synapse/v1.112/usage/configuration/config_documentation.html#allowed_local_3pids) config option's msisdn address to a working example.
+1
View File
@@ -0,0 +1 @@
Add Account Data extension support to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.
+1
View File
@@ -0,0 +1 @@
Ensure we don't send down negative `bump_stamp` in experimental sliding sync endpoint.
+1
View File
@@ -0,0 +1 @@
Do not send down empty room entries down experimental sliding sync endpoint.
+1
View File
@@ -0,0 +1 @@
Refactor Sliding Sync tests to better utilize the `SlidingSyncBase`.
+1
View File
@@ -0,0 +1 @@
Refactor Sliding Sync tests to better utilize the `SlidingSyncBase`.
+1
View File
@@ -0,0 +1 @@
Add receipts extension support to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.
+1
View File
@@ -0,0 +1 @@
Fix a bug introduced in v1.110.0 which caused `/keys/query` to return incomplete results, leading to high network activity and CPU usage on Matrix clients.
+1
View File
@@ -0,0 +1 @@
Add some opentracing tags and logging to the experimental sliding sync implementation.
+1
View File
@@ -0,0 +1 @@
Split and move Sliding Sync tests so we have some more sane test file sizes.
+1
View File
@@ -0,0 +1 @@
Add typing notification extension support to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.
+1
View File
@@ -0,0 +1 @@
Update the `limited` field description in the Sliding Sync response to accurately describe what it actually represents.
+36
View File
@@ -1,3 +1,39 @@
matrix-synapse-py3 (1.112.0) stable; urgency=medium
* New Synapse release 1.112.0.
-- Synapse Packaging team <packages@matrix.org> Tue, 30 Jul 2024 17:15:48 +0100
matrix-synapse-py3 (1.112.0~rc1) stable; urgency=medium
* New Synapse release 1.112.0rc1.
-- Synapse Packaging team <packages@matrix.org> Tue, 23 Jul 2024 08:58:55 -0600
matrix-synapse-py3 (1.111.1) stable; urgency=medium
* New Synapse release 1.111.1.
-- Synapse Packaging team <packages@matrix.org> Tue, 30 Jul 2024 16:13:52 +0100
matrix-synapse-py3 (1.111.0) stable; urgency=medium
* New Synapse release 1.111.0.
-- Synapse Packaging team <packages@matrix.org> Tue, 16 Jul 2024 12:42:46 +0200
matrix-synapse-py3 (1.111.0~rc2) stable; urgency=medium
* New synapse release 1.111.0rc2.
-- Synapse Packaging team <packages@matrix.org> Wed, 10 Jul 2024 08:46:54 +0000
matrix-synapse-py3 (1.111.0~rc1) stable; urgency=medium
* New synapse release 1.111.0rc1.
-- Synapse Packaging team <packages@matrix.org> Tue, 09 Jul 2024 09:49:25 +0000
matrix-synapse-py3 (1.110.0) stable; urgency=medium matrix-synapse-py3 (1.110.0) stable; urgency=medium
* New Synapse release 1.110.0. * New Synapse release 1.110.0.
+1 -1
View File
@@ -5,7 +5,7 @@ _Description: Name of the server:
servers via federation. This is normally the public hostname of the servers via federation. This is normally the public hostname of the
server running synapse, but can be different if you set up delegation. server running synapse, but can be different if you set up delegation.
Please refer to the delegation documentation in this case: Please refer to the delegation documentation in this case:
https://github.com/element-hq/synapse/blob/master/docs/delegate.md. https://element-hq.github.io/synapse/latest/delegate.html.
Template: matrix-synapse/report-stats Template: matrix-synapse/report-stats
Type: boolean Type: boolean
+2 -2
View File
@@ -27,7 +27,7 @@ ARG PYTHON_VERSION=3.11
### ###
# We hardcode the use of Debian bookworm here because this could change upstream # We hardcode the use of Debian bookworm here because this could change upstream
# and other Dockerfiles used for testing are expecting bookworm. # and other Dockerfiles used for testing are expecting bookworm.
FROM docker.io/library/python:${PYTHON_VERSION}-slim-bookworm as requirements FROM docker.io/library/python:${PYTHON_VERSION}-slim-bookworm AS requirements
# RUN --mount is specific to buildkit and is documented at # RUN --mount is specific to buildkit and is documented at
# https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#build-mounts-run---mount. # https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#build-mounts-run---mount.
@@ -87,7 +87,7 @@ RUN if [ -z "$TEST_ONLY_IGNORE_POETRY_LOCKFILE" ]; then \
### ###
### Stage 1: builder ### Stage 1: builder
### ###
FROM docker.io/library/python:${PYTHON_VERSION}-slim-bookworm as builder FROM docker.io/library/python:${PYTHON_VERSION}-slim-bookworm AS builder
# install the OS build deps # install the OS build deps
RUN \ RUN \
+1 -1
View File
@@ -24,7 +24,7 @@ ARG distro=""
# https://launchpad.net/~jyrki-pulliainen/+archive/ubuntu/dh-virtualenv, but # https://launchpad.net/~jyrki-pulliainen/+archive/ubuntu/dh-virtualenv, but
# it's not obviously easier to use that than to build our own.) # it's not obviously easier to use that than to build our own.)
FROM docker.io/library/${distro} as builder FROM docker.io/library/${distro} AS builder
RUN apt-get update -qq -o Acquire::Languages=none RUN apt-get update -qq -o Acquire::Languages=none
RUN env DEBIAN_FRONTEND=noninteractive apt-get install \ RUN env DEBIAN_FRONTEND=noninteractive apt-get install \
+45 -50
View File
@@ -1,67 +1,62 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
ARG SYNAPSE_VERSION=latest ARG SYNAPSE_VERSION=latest
ARG FROM=matrixdotorg/synapse:$SYNAPSE_VERSION ARG SYNAPSE_IMAGE=docker.io/matrixdotorg/synapse:$SYNAPSE_VERSION
# first of all, we create a base image with an nginx which we can copy into the ARG MAS_VERSION=latest
# target image. For repeated rebuilds, this is much faster than apt installing ARG MAS_IMAGE=ghcr.io/matrix-org/matrix-authentication-service:$MAS_VERSION
# each time.
FROM docker.io/library/debian:bookworm-slim AS deps_base ARG REDIS_VERSION=7.4.0
RUN \ ARG REDIS_IMAGE=docker.io/library/redis:$REDIS_VERSION-bookworm
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update -qq && \
DEBIAN_FRONTEND=noninteractive apt-get install -yqq --no-install-recommends \
redis-server nginx-light
# Similarly, a base to copy the redis server from. ARG NGINX_VERSION=1.26.1
# ARG NGINX_IMAGE=docker.io/library/nginx:$NGINX_VERSION-bookworm
# The redis docker image has fewer dynamic libraries than the debian package,
# which makes it much easier to copy (but we need to make sure we use an image FROM $NGINX_IMAGE AS nginx
# based on the same debian version as the synapse image, to make sure we get FROM $REDIS_IMAGE AS redis
# the expected version of libc. FROM $MAS_IMAGE AS mas
FROM docker.io/library/redis:7-bookworm AS redis_base
# now build the final image, based on the the regular Synapse docker image # now build the final image, based on the the regular Synapse docker image
FROM $FROM FROM $SYNAPSE_IMAGE
# Install supervisord with pip instead of apt, to avoid installing a second # Install supervisord with pip instead of apt, to avoid installing a second
# copy of python. # copy of python.
RUN --mount=type=cache,target=/root/.cache/pip \ RUN --mount=type=cache,target=/root/.cache/pip \
pip install supervisor~=4.2 pip install supervisor~=4.2
RUN mkdir -p /etc/supervisor/conf.d RUN mkdir -p /etc/supervisor/conf.d
# Copy over redis and nginx # Copy over redis, nginx and matrix-authentication-service
COPY --from=redis_base /usr/local/bin/redis-server /usr/local/bin COPY --from=redis /usr/local/bin/redis-server /usr/local/bin
COPY --from=deps_base /usr/sbin/nginx /usr/sbin COPY --from=nginx /usr/sbin/nginx /usr/sbin
COPY --from=deps_base /usr/share/nginx /usr/share/nginx COPY --from=nginx /usr/share/nginx /usr/share/nginx
COPY --from=deps_base /usr/lib/nginx /usr/lib/nginx COPY --from=nginx /usr/lib/nginx /usr/lib/nginx
COPY --from=deps_base /etc/nginx /etc/nginx COPY --from=nginx /etc/nginx /etc/nginx
RUN rm /etc/nginx/sites-enabled/default RUN mkdir /var/log/nginx /var/lib/nginx
RUN mkdir /var/log/nginx /var/lib/nginx RUN chown www-data /var/lib/nginx
RUN chown www-data /var/lib/nginx
# have nginx log to stderr/out # have nginx log to stderr/out
RUN ln -sf /dev/stdout /var/log/nginx/access.log RUN ln -sf /dev/stdout /var/log/nginx/access.log
RUN ln -sf /dev/stderr /var/log/nginx/error.log RUN ln -sf /dev/stderr /var/log/nginx/error.log
# Copy Synapse worker, nginx and supervisord configuration template files COPY --from=mas /usr/local/bin/mas-cli /usr/local/bin
COPY ./docker/conf-workers/* /conf/ COPY --from=mas /usr/local/share/mas-cli /usr/local/share
# Copy a script to prefix log lines with the supervisor program name # Copy Synapse worker, nginx and supervisord configuration template files
COPY ./docker/prefix-log /usr/local/bin/ COPY ./docker/conf-workers/* /conf/
# Expose nginx listener port # Copy a script to prefix log lines with the supervisor program name
EXPOSE 8080/tcp COPY ./docker/prefix-log /usr/local/bin/
# A script to read environment variables and create the necessary # Expose nginx listener port
# files to run the desired worker configuration. Will start supervisord. EXPOSE 8080/tcp
COPY ./docker/configure_workers_and_start.py /configure_workers_and_start.py
ENTRYPOINT ["/configure_workers_and_start.py"]
# Replace the healthcheck with one which checks *all* the workers. The script # A script to read environment variables and create the necessary
# is generated by configure_workers_and_start.py. # files to run the desired worker configuration. Will start supervisord.
HEALTHCHECK --start-period=5s --interval=15s --timeout=5s \ COPY ./docker/configure_workers_and_start.py /configure_workers_and_start.py
CMD /bin/sh /healthcheck.sh ENTRYPOINT ["/configure_workers_and_start.py"]
# Replace the healthcheck with one which checks *all* the workers. The script
# is generated by configure_workers_and_start.py.
HEALTHCHECK --start-period=5s --interval=15s --timeout=5s \
CMD /bin/sh /healthcheck.sh
+13 -6
View File
@@ -6,11 +6,17 @@
# Instructions for building this image from those it depends on is detailed in this guide: # Instructions for building this image from those it depends on is detailed in this guide:
# https://github.com/element-hq/synapse/blob/develop/docker/README-testing.md#testing-with-postgresql-and-single-or-multi-process-synapse # https://github.com/element-hq/synapse/blob/develop/docker/README-testing.md#testing-with-postgresql-and-single-or-multi-process-synapse
ARG SYNAPSE_VERSION=latest
# This is an intermediate image, to be built locally (not pulled from a registry). # This is an intermediate image, to be built locally (not pulled from a registry).
ARG FROM=matrixdotorg/synapse-workers:$SYNAPSE_VERSION ARG SYNAPSE_WORKERS_IMAGE=synapse-workers
ARG POSTGRES_VERSION=13
ARG POSTGRES_IMAGE=docker.io/library/postgres:$POSTGRES_VERSION-bookworm
# Save the Postgres image for later
FROM $POSTGRES_IMAGE AS postgres
FROM $SYNAPSE_WORKERS_IMAGE
FROM $FROM
# First of all, we copy postgres server from the official postgres image, # First of all, we copy postgres server from the official postgres image,
# since for repeated rebuilds, this is much faster than apt installing # since for repeated rebuilds, this is much faster than apt installing
# postgres each time. # postgres each time.
@@ -20,8 +26,8 @@ FROM $FROM
# the same debian version as Synapse's docker image (so the versions of the # the same debian version as Synapse's docker image (so the versions of the
# shared libraries match). # shared libraries match).
RUN adduser --system --uid 999 postgres --home /var/lib/postgresql RUN adduser --system --uid 999 postgres --home /var/lib/postgresql
COPY --from=docker.io/library/postgres:13-bookworm /usr/lib/postgresql /usr/lib/postgresql COPY --from=postgres /usr/lib/postgresql /usr/lib/postgresql
COPY --from=docker.io/library/postgres:13-bookworm /usr/share/postgresql /usr/share/postgresql COPY --from=postgres /usr/share/postgresql /usr/share/postgresql
RUN mkdir /var/run/postgresql && chown postgres /var/run/postgresql RUN mkdir /var/run/postgresql && chown postgres /var/run/postgresql
ENV PATH="${PATH}:/usr/lib/postgresql/13/bin" ENV PATH="${PATH}:/usr/lib/postgresql/13/bin"
ENV PGDATA=/var/lib/postgresql/data ENV PGDATA=/var/lib/postgresql/data
@@ -29,9 +35,10 @@ ENV PGDATA=/var/lib/postgresql/data
# We also initialize the database at build time, rather than runtime, so that it's faster to spin up the image. # We also initialize the database at build time, rather than runtime, so that it's faster to spin up the image.
RUN gosu postgres initdb --locale=C --encoding=UTF-8 --auth-host password RUN gosu postgres initdb --locale=C --encoding=UTF-8 --auth-host password
# Configure a password and create a database for Synapse # Configure a password and create a database for Synapse and MAS
RUN echo "ALTER USER postgres PASSWORD 'somesecret'" | gosu postgres postgres --single RUN echo "ALTER USER postgres PASSWORD 'somesecret'" | gosu postgres postgres --single
RUN echo "CREATE DATABASE synapse" | gosu postgres postgres --single RUN echo "CREATE DATABASE synapse" | gosu postgres postgres --single
RUN echo "CREATE DATABASE mas" | gosu postgres postgres --single
# Extend the shared homeserver config to disable rate-limiting, # Extend the shared homeserver config to disable rate-limiting,
# set Complement's static shared secret, enable registration, amongst other # set Complement's static shared secret, enable registration, amongst other
+11
View File
@@ -20,4 +20,15 @@ app_service_config_files:
{%- endfor %} {%- endfor %}
{%- endif %} {%- endif %}
{% if enable_mas %}
experimental_features:
msc3861:
enabled: true
issuer: "http://localhost:8008/"
client_id: "0000000000000000000SYNAPSE"
client_auth_method: client_secret_basic
client_secret: choozia3ThiefahZaofeiveish1kahr0
admin_token: eeShoo4ceebae4Lo4Che1hoofoophaiz
{% endif %}
{{ shared_worker_config }} {{ shared_worker_config }}
+9
View File
@@ -35,3 +35,12 @@ autorestart=true
# Redis can be disabled if the image is being used without workers # Redis can be disabled if the image is being used without workers
autostart={{ enable_redis }} autostart={{ enable_redis }}
[program:mas]
comamnd=/usr/local/bin/prefix-log /usr/local/bin/mas-cli --config /conf/mas.yaml
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=unexpected
autostart={{ enable_mas }}
+3
View File
@@ -126,6 +126,7 @@ WORKERS_CONFIG: Dict[str, Dict[str, Any]] = {
"^/_synapse/admin/v1/media/.*$", "^/_synapse/admin/v1/media/.*$",
"^/_synapse/admin/v1/quarantine_media/.*$", "^/_synapse/admin/v1/quarantine_media/.*$",
"^/_matrix/client/v1/media/.*$", "^/_matrix/client/v1/media/.*$",
"^/_matrix/federation/v1/media/.*$",
], ],
# The first configured media worker will run the media background jobs # The first configured media worker will run the media background jobs
"shared_extra_conf": { "shared_extra_conf": {
@@ -958,6 +959,7 @@ def generate_worker_files(
shared_worker_config=yaml.dump(shared_config), shared_worker_config=yaml.dump(shared_config),
appservice_registrations=appservice_registrations, appservice_registrations=appservice_registrations,
enable_redis=workers_in_use, enable_redis=workers_in_use,
enable_mas=False,
workers_in_use=workers_in_use, workers_in_use=workers_in_use,
using_unix_sockets=using_unix_sockets, using_unix_sockets=using_unix_sockets,
) )
@@ -980,6 +982,7 @@ def generate_worker_files(
"/etc/supervisor/supervisord.conf", "/etc/supervisor/supervisord.conf",
main_config_path=config_path, main_config_path=config_path,
enable_redis=workers_in_use, enable_redis=workers_in_use,
enable_mas=False,
using_unix_sockets=using_unix_sockets, using_unix_sockets=using_unix_sockets,
) )
+1 -5
View File
@@ -2,13 +2,9 @@
This API allows a server administrator to enable or disable some experimental features on a per-user This API allows a server administrator to enable or disable some experimental features on a per-user
basis. The currently supported features are: basis. The currently supported features are:
- [MSC3026](https://github.com/matrix-org/matrix-spec-proposals/pull/3026): busy
presence state enabled
- [MSC3881](https://github.com/matrix-org/matrix-spec-proposals/pull/3881): enable remotely toggling push notifications - [MSC3881](https://github.com/matrix-org/matrix-spec-proposals/pull/3881): enable remotely toggling push notifications
for another client for another client
- [MSC3967](https://github.com/matrix-org/matrix-spec-proposals/pull/3967): do not require - [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575): enable experimental sliding sync support
UIA when first uploading cross-signing keys.
To use it, you will need to authenticate by providing an `access_token` To use it, you will need to authenticate by providing an `access_token`
for a server admin: see [Admin API](../usage/administration/admin_api/). for a server admin: see [Admin API](../usage/administration/admin_api/).
+3 -3
View File
@@ -449,9 +449,9 @@ For example, a fix in PR #1234 would have its changelog entry in
> The security levels of Florbs are now validated when received > The security levels of Florbs are now validated when received
> via the `/federation/florb` endpoint. Contributed by Jane Matrix. > via the `/federation/florb` endpoint. Contributed by Jane Matrix.
If there are multiple pull requests involved in a single bugfix/feature/etc, If there are multiple pull requests involved in a single bugfix/feature/etc, then the
then the content for each `changelog.d` file should be the same. Towncrier will content for each `changelog.d` file and file extension should be the same. Towncrier
merge the matching files together into a single changelog entry when we come to will merge the matching files together into a single changelog entry when we come to
release. release.
### How do I know what to call the changelog file before I create the PR? ### How do I know what to call the changelog file before I create the PR?
+94
View File
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="41.440346mm"
height="10.383124mm"
viewBox="0 0 41.440346 10.383125"
version="1.1"
id="svg1"
xml:space="preserve"
sodipodi:docname="element_logo_white_bg.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:export-bgcolor="#ffffffff" /><defs
id="defs1" /><g
id="layer1"
transform="translate(-84.803844,-143.2075)"
inkscape:export-filename="element_logo_white_bg.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"><g
style="fill:none"
id="g1"
transform="matrix(0.26458333,0,0,0.26458333,85.841658,144.26667)"><rect
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.31041;stroke-dasharray:none;stroke-opacity:1"
id="rect20"
width="155.31451"
height="37.932892"
x="-3.2672384"
y="-3.3479743"
rx="3.3718522"
ry="3.7915266"
transform="translate(-2.1259843e-6)"
inkscape:label="rect20"
inkscape:export-filename="rect20.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" /><path
fill-rule="evenodd"
clip-rule="evenodd"
d="M 16,32 C 24.8366,32 32,24.8366 32,16 32,7.16344 24.8366,0 16,0 7.16344,0 0,7.16344 0,16 0,24.8366 7.16344,32 16,32 Z"
fill="#0dbd8b"
id="path1" /><path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 13.0756,7.455 c 0,-0.64584 0.5247,-1.1694 1.1719,-1.1694 4.3864,0 7.9423,3.54853 7.9423,7.9259 0,0.6458 -0.5246,1.1694 -1.1718,1.1694 -0.6472,0 -1.1719,-0.5236 -1.1719,-1.1694 0,-3.0857 -2.5066,-5.58711 -5.5986,-5.58711 -0.6472,0 -1.1719,-0.52355 -1.1719,-1.16939 z"
fill="#ffffff"
id="path2" /><path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 24.5424,13.042 c 0.6472,0 1.1719,0.5235 1.1719,1.1694 0,4.3773 -3.5559,7.9258 -7.9424,7.9258 -0.6472,0 -1.1718,-0.5235 -1.1718,-1.1693 0,-0.6459 0.5246,-1.1694 1.1718,-1.1694 3.0921,0 5.5987,-2.5015 5.5987,-5.5871 0,-0.6459 0.5247,-1.1694 1.1718,-1.1694 z"
fill="#ffffff"
id="path3" /><path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 18.9446,24.5446 c 0,0.6459 -0.5247,1.1694 -1.1718,1.1694 -4.3865,0 -7.94239,-3.5485 -7.94239,-7.9258 0,-0.6459 0.52469,-1.1694 1.17179,-1.1694 0.6472,0 1.1719,0.5235 1.1719,1.1694 0,3.0856 2.5066,5.587 5.5987,5.587 0.6471,0 1.1718,0.5236 1.1718,1.1694 z"
fill="#ffffff"
id="path4" /><path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 7.45823,18.9576 c -0.64718,0 -1.17183,-0.5235 -1.17183,-1.1694 0,-4.3773 3.55591,-7.92581 7.9423,-7.92581 0.6472,0 1.1719,0.52351 1.1719,1.16941 0,0.6458 -0.5247,1.1694 -1.1719,1.1694 -3.092,0 -5.59864,2.5014 -5.59864,5.587 0,0.6459 -0.52465,1.1694 -1.17183,1.1694 z"
fill="#ffffff"
id="path5" /><path
d="M 56.2856,18.1428 H 44.9998 c 0.1334,1.181 0.5619,2.1238 1.2858,2.8286 0.7238,0.6857 1.6761,1.0286 2.8571,1.0286 0.7809,0 1.4857,-0.1905 2.1143,-0.5715 0.6286,-0.3809 1.0762,-0.8952 1.3428,-1.5428 h 3.4286 c -0.4571,1.5047 -1.3143,2.7238 -2.5714,3.6571 -1.2381,0.9143 -2.7048,1.3715 -4.4,1.3715 -2.2095,0 -4,-0.7334 -5.3714,-2.2 -1.3524,-1.4667 -2.0286,-3.3239 -2.0286,-5.5715 0,-2.1905 0.6857,-4.0285 2.0571,-5.5143 1.3715,-1.4857 3.1429,-2.22853 5.3143,-2.22853 2.1714,0 3.9238,0.73333 5.2572,2.20003 1.3523,1.4476 2.0285,3.2762 2.0285,5.4857 z m -7.2572,-5.9714 c -1.0667,0 -1.9524,0.3143 -2.6571,0.9429 -0.7048,0.6285 -1.1429,1.4666 -1.3143,2.5142 h 7.8857 c -0.1524,-1.0476 -0.5714,-1.8857 -1.2571,-2.5142 -0.6858,-0.6286 -1.5715,-0.9429 -2.6572,-0.9429 z"
fill="#000000"
id="path6" /><path
d="M 58.6539,20.1428 V 3.14282 h 3.4 V 20.2 c 0,0.7619 0.419,1.1428 1.2571,1.1428 l 0.6,-0.0285 v 3.2285 c -0.3238,0.0572 -0.6667,0.0857 -1.0286,0.0857 -1.4666,0 -2.5428,-0.3714 -3.2285,-1.1142 -0.6667,-0.7429 -1,-1.8667 -1,-3.3715 z"
fill="#000000"
id="path7" /><path
d="M 79.7454,18.1428 H 68.4597 c 0.1333,1.181 0.5619,2.1238 1.2857,2.8286 0.7238,0.6857 1.6762,1.0286 2.8571,1.0286 0.781,0 1.4857,-0.1905 2.1143,-0.5715 0.6286,-0.3809 1.0762,-0.8952 1.3429,-1.5428 h 3.4285 c -0.4571,1.5047 -1.3143,2.7238 -2.5714,3.6571 -1.2381,0.9143 -2.7048,1.3715 -4.4,1.3715 -2.2095,0 -4,-0.7334 -5.3714,-2.2 -1.3524,-1.4667 -2.0286,-3.3239 -2.0286,-5.5715 0,-2.1905 0.6857,-4.0285 2.0571,-5.5143 1.3715,-1.4857 3.1429,-2.22853 5.3143,-2.22853 2.1715,0 3.9238,0.73333 5.2572,2.20003 1.3524,1.4476 2.0285,3.2762 2.0285,5.4857 z m -7.2572,-5.9714 c -1.0666,0 -1.9524,0.3143 -2.6571,0.9429 -0.7048,0.6285 -1.1429,1.4666 -1.3143,2.5142 h 7.8857 c -0.1524,-1.0476 -0.5714,-1.8857 -1.2571,-2.5142 -0.6857,-0.6286 -1.5715,-0.9429 -2.6572,-0.9429 z"
fill="#000000"
id="path8" /><path
d="m 95.0851,16.0571 v 8.5143 h -3.4 v -8.8857 c 0,-2.2476 -0.9333,-3.3714 -2.8,-3.3714 -1.0095,0 -1.819,0.3238 -2.4286,0.9714 -0.5904,0.6476 -0.8857,1.5333 -0.8857,2.6571 v 8.6286 h -3.4 V 9.74282 h 3.1429 v 1.97148 c 0.3619,-0.6667 0.9143,-1.2191 1.6571,-1.6572 0.7429,-0.43809 1.6667,-0.65713 2.7714,-0.65713 2.0572,0 3.5429,0.78093 4.4572,2.34283 1.2571,-1.5619 2.9333,-2.34283 5.0286,-2.34283 1.733,0 3.067,0.54285 4,1.62853 0.933,1.0667 1.4,2.4762 1.4,4.2286 v 9.3143 h -3.4 v -8.8857 c 0,-2.2476 -0.933,-3.3714 -2.8,-3.3714 -1.0286,0 -1.8477,0.3333 -2.4572,1 -0.5905,0.6476 -0.8857,1.5619 -0.8857,2.7428 z"
fill="#000000"
id="path9" /><path
d="m 121.537,18.1428 h -11.286 c 0.133,1.181 0.562,2.1238 1.286,2.8286 0.723,0.6857 1.676,1.0286 2.857,1.0286 0.781,0 1.486,-0.1905 2.114,-0.5715 0.629,-0.3809 1.076,-0.8952 1.343,-1.5428 h 3.429 c -0.458,1.5047 -1.315,2.7238 -2.572,3.6571 -1.238,0.9143 -2.705,1.3715 -4.4,1.3715 -2.209,0 -4,-0.7334 -5.371,-2.2 -1.353,-1.4667 -2.029,-3.3239 -2.029,-5.5715 0,-2.1905 0.686,-4.0285 2.057,-5.5143 1.372,-1.4857 3.143,-2.22853 5.315,-2.22853 2.171,0 3.923,0.73333 5.257,2.20003 1.352,1.4476 2.028,3.2762 2.028,5.4857 z m -7.257,-5.9714 c -1.067,0 -1.953,0.3143 -2.658,0.9429 -0.704,0.6285 -1.142,1.4666 -1.314,2.5142 h 7.886 c -0.153,-1.0476 -0.572,-1.8857 -1.257,-2.5142 -0.686,-0.6286 -1.572,-0.9429 -2.657,-0.9429 z"
fill="#000000"
id="path10" /><path
d="m 127.105,9.74282 v 1.97148 c 0.343,-0.6477 0.905,-1.1905 1.686,-1.6286 0.8,-0.45716 1.762,-0.68573 2.885,-0.68573 1.753,0 3.105,0.53333 4.058,1.60003 0.971,1.0666 1.457,2.4857 1.457,4.2571 v 9.3143 h -3.4 v -8.8857 c 0,-1.0476 -0.248,-1.8667 -0.743,-2.4572 -0.476,-0.6095 -1.21,-0.9142 -2.2,-0.9142 -1.086,0 -1.943,0.3238 -2.572,0.9714 -0.609,0.6476 -0.914,1.5428 -0.914,2.6857 v 8.6 h -3.4 V 9.74282 Z"
fill="#000000"
id="path11" /><path
d="m 147.12,21.5428 v 2.9429 c -0.419,0.1143 -1.009,0.1714 -1.771,0.1714 -2.895,0 -4.343,-1.4571 -4.343,-4.3714 v -7.8286 h -2.257 V 9.74282 h 2.257 V 5.88568 h 3.4 v 3.85714 h 2.772 v 2.71428 h -2.772 v 7.4857 c 0,1.1619 0.552,1.7429 1.657,1.7429 z"
fill="#000000"
id="path12" /></g></g></svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

+1 -1
View File
@@ -67,7 +67,7 @@ in Synapse can be deactivated.
**NOTE**: This has an impact on security and is for testing purposes only! **NOTE**: This has an impact on security and is for testing purposes only!
To deactivate the certificate validation, the following setting must be added to To deactivate the certificate validation, the following setting must be added to
your [homserver.yaml](../usage/configuration/homeserver_sample_config.md). your [homeserver.yaml](../usage/configuration/homeserver_sample_config.md).
```yaml ```yaml
use_insecure_ssl_client_just_for_testing_do_not_use: true use_insecure_ssl_client_just_for_testing_do_not_use: true
+56 -1
View File
@@ -309,7 +309,62 @@ sudo dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
libwebp-devel libxml2-devel libxslt-devel libpq-devel \ libwebp-devel libxml2-devel libxslt-devel libpq-devel \
python3-virtualenv libffi-devel openssl-devel python3-devel \ python3-virtualenv libffi-devel openssl-devel python3-devel \
libicu-devel libicu-devel
sudo dnf groupinstall "Development Tools" sudo dnf group install "Development Tools"
```
##### Red Hat Enterprise Linux / Rocky Linux
*Note: The term "RHEL" below refers to both Red Hat Enterprise Linux and Rocky Linux. The distributions are 1:1 binary compatible.*
It's recommended to use the latest Python versions.
RHEL 8 in particular ships with Python 3.6 by default which is EOL and therefore no longer supported by Synapse. RHEL 9 ship with Python 3.9 which is still supported by the Python core team as of this writing. However, newer Python versions provide significant performance improvements and they're available in official distributions' repositories. Therefore it's recommended to use them.
Python 3.11 and 3.12 are available for both RHEL 8 and 9.
These commands should be run as root user.
RHEL 8
```bash
# Enable PowerTools repository
dnf config-manager --set-enabled powertools
```
RHEL 9
```bash
# Enable CodeReady Linux Builder repository
crb enable
```
Install new version of Python. You only need one of these:
```bash
# Python 3.11
dnf install python3.11 python3.11-devel
```
```bash
# Python 3.12
dnf install python3.12 python3.12-devel
```
Finally, install common prerequisites
```bash
dnf install libicu libicu-devel libpq5 libpq5-devel lz4 pkgconf
dnf group install "Development Tools"
```
###### Using venv module instead of virtualenv command
It's recommended to use Python venv module directly rather than the virtualenv command.
* On RHEL 9, virtualenv is only available on [EPEL](https://docs.fedoraproject.org/en-US/epel/).
* On RHEL 8, virtualenv is based on Python 3.6. It does not support creating 3.11/3.12 virtual environments.
Here's an example of creating Python 3.12 virtual environment and installing Synapse from PyPI.
```bash
mkdir -p ~/synapse
# To use Python 3.11, simply use the command "python3.11" instead.
python3.12 -m venv ~/synapse/env
source ~/synapse/env/bin/activate
pip install --upgrade pip
pip install --upgrade setuptools
pip install matrix-synapse
``` ```
##### macOS ##### macOS
+3 -2
View File
@@ -119,13 +119,14 @@ stacking them up. You can monitor the currently running background updates with
# Upgrading to v1.111.0 # Upgrading to v1.111.0
## New worker endpoints for authenticated client media ## New worker endpoints for authenticated client and federation media
[Media repository workers](./workers.md#synapseappmedia_repository) handling [Media repository workers](./workers.md#synapseappmedia_repository) handling
Media APIs can now handle the following endpoint pattern: Media APIs can now handle the following endpoint patterns:
``` ```
^/_matrix/client/v1/media/.*$ ^/_matrix/client/v1/media/.*$
^/_matrix/federation/v1/media/.*$
``` ```
Please update your reverse proxy configuration. Please update your reverse proxy configuration.
@@ -246,6 +246,7 @@ Example configuration:
```yaml ```yaml
presence: presence:
enabled: false enabled: false
include_offline_users_on_sync: false
``` ```
`enabled` can also be set to a special value of "untracked" which ignores updates `enabled` can also be set to a special value of "untracked" which ignores updates
@@ -254,6 +255,10 @@ received via clients and federation, while still accepting updates from the
*The "untracked" option was added in Synapse 1.96.0.* *The "untracked" option was added in Synapse 1.96.0.*
When clients perform an initial or `full_state` sync, presence results for offline users are
not included by default. Setting `include_offline_users_on_sync` to `true` will always include
offline users in the results. Defaults to false.
--- ---
### `require_auth_for_profile_requests` ### `require_auth_for_profile_requests`
@@ -1863,6 +1868,18 @@ federation_rr_transactions_per_room_per_second: 40
## Media Store ## Media Store
Config options related to Synapse's media store. Config options related to Synapse's media store.
---
### `enable_authenticated_media`
When set to true, all subsequent media uploads will be marked as authenticated, and will not be available over legacy
unauthenticated media endpoints (`/_matrix/media/(r0|v3|v1)/download` and `/_matrix/media/(r0|v3|v1)/thumbnail`) - requests for authenticated media over these endpoints will result in a 404. All media, including authenticated media, will be available over the authenticated media endpoints `_matrix/client/v1/media/download` and `_matrix/client/v1/media/thumbnail`. Media uploaded prior to setting this option to true will still be available over the legacy endpoints. Note if the setting is switched to false
after enabling, media marked as authenticated will be available over legacy endpoints. Defaults to false, but
this will change to true in a future Synapse release.
Example configuration:
```yaml
enable_authenticated_media: true
```
--- ---
### `enable_media_repo` ### `enable_media_repo`
@@ -2369,7 +2386,7 @@ enable_registration_without_verification: true
--- ---
### `registrations_require_3pid` ### `registrations_require_3pid`
If this is set, users must provide all of the specified types of 3PID when registering an account. If this is set, users must provide all of the specified types of [3PID](https://spec.matrix.org/latest/appendices/#3pid-types) when registering an account.
Note that [`enable_registration`](#enable_registration) must also be set to allow account registration. Note that [`enable_registration`](#enable_registration) must also be set to allow account registration.
@@ -2394,6 +2411,9 @@ disable_msisdn_registration: true
Mandate that users are only allowed to associate certain formats of Mandate that users are only allowed to associate certain formats of
3PIDs with accounts on this server, as specified by the `medium` and `pattern` sub-options. 3PIDs with accounts on this server, as specified by the `medium` and `pattern` sub-options.
`pattern` is a [Perl-like regular expression](https://docs.python.org/3/library/re.html#module-re).
More information about 3PIDs, allowed `medium` types and their `address` syntax can be found [in the Matrix spec](https://spec.matrix.org/latest/appendices/#3pid-types).
Example configuration: Example configuration:
```yaml ```yaml
@@ -2403,7 +2423,7 @@ allowed_local_3pids:
- medium: email - medium: email
pattern: '^[^@]+@vector\.im$' pattern: '^[^@]+@vector\.im$'
- medium: msisdn - medium: msisdn
pattern: '\+44' pattern: '^44\d{10}$'
``` ```
--- ---
### `enable_3pid_lookup` ### `enable_3pid_lookup`
@@ -4134,6 +4154,38 @@ default_power_level_content_override:
trusted_private_chat: null trusted_private_chat: null
public_chat: null public_chat: null
``` ```
The default power levels for each preset are:
```yaml
"m.room.name": 50
"m.room.power_levels": 100
"m.room.history_visibility": 100
"m.room.canonical_alias": 50
"m.room.avatar": 50
"m.room.tombstone": 100
"m.room.server_acl": 100
"m.room.encryption": 100
```
So a complete example where the default power-levels for a preset are maintained
but the power level for a new key is set is:
```yaml
default_power_level_content_override:
private_chat:
events:
"com.example.foo": 0
"m.room.name": 50
"m.room.power_levels": 100
"m.room.history_visibility": 100
"m.room.canonical_alias": 50
"m.room.avatar": 50
"m.room.tombstone": 100
"m.room.server_acl": 100
"m.room.encryption": 100
trusted_private_chat: null
public_chat: null
```
--- ---
### `forget_rooms_on_leave` ### `forget_rooms_on_leave`
+1
View File
@@ -740,6 +740,7 @@ Handles the media repository. It can handle all endpoints starting with:
/_matrix/media/ /_matrix/media/
/_matrix/client/v1/media/ /_matrix/client/v1/media/
/_matrix/federation/v1/media/
... and the following regular expressions matching media-specific administration APIs: ... and the following regular expressions matching media-specific administration APIs:
Generated
+506 -511
View File
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -43,6 +43,7 @@ target-version = ['py38', 'py39', 'py310', 'py311']
[tool.ruff] [tool.ruff]
line-length = 88 line-length = 88
[tool.ruff.lint]
# See https://beta.ruff.rs/docs/rules/#error-e # See https://beta.ruff.rs/docs/rules/#error-e
# for error codes. The ones we ignore are: # for error codes. The ones we ignore are:
# E501: Line too long (black enforces this for us) # E501: Line too long (black enforces this for us)
@@ -96,7 +97,7 @@ module-name = "synapse.synapse_rust"
[tool.poetry] [tool.poetry]
name = "matrix-synapse" name = "matrix-synapse"
version = "1.110.0" version = "1.112.0"
description = "Homeserver for the Matrix decentralised comms protocol" description = "Homeserver for the Matrix decentralised comms protocol"
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"] authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
@@ -200,8 +201,8 @@ netaddr = ">=0.7.18"
# add a lower bound to the Jinja2 dependency. # add a lower bound to the Jinja2 dependency.
Jinja2 = ">=3.0" Jinja2 = ">=3.0"
bleach = ">=1.4.3" bleach = ">=1.4.3"
# We use `Self`, which were added in `typing-extensions` 4.0. # We use `assert_never`, which were added in `typing-extensions` 4.1.
typing-extensions = ">=4.0" typing-extensions = ">=4.1"
# We enforce that we have a `cryptography` version that bundles an `openssl` # We enforce that we have a `cryptography` version that bundles an `openssl`
# with the latest security patches. # with the latest security patches.
cryptography = ">=3.4.7" cryptography = ">=3.4.7"
@@ -321,7 +322,7 @@ all = [
# This helps prevents merge conflicts when running a batch of dependabot updates. # This helps prevents merge conflicts when running a batch of dependabot updates.
isort = ">=5.10.1" isort = ">=5.10.1"
black = ">=22.7.0" black = ">=22.7.0"
ruff = "0.3.7" ruff = "0.5.5"
# Type checking only works with the pydantic.v1 compat module from pydantic v2 # Type checking only works with the pydantic.v1 compat module from pydantic v2
pydantic = "^2" pydantic = "^2"
+13 -10
View File
@@ -167,11 +167,11 @@ if [ -z "$skip_docker_build" ]; then
-f "docker/editable.Dockerfile" . -f "docker/editable.Dockerfile" .
$CONTAINER_RUNTIME build -t synapse-workers-editable \ $CONTAINER_RUNTIME build -t synapse-workers-editable \
--build-arg FROM=synapse-editable \ --build-arg SYNAPSE_IMAGE=synapse-editable \
-f "docker/Dockerfile-workers" . -f "docker/Dockerfile-workers" .
$CONTAINER_RUNTIME build -t complement-synapse-editable \ $CONTAINER_RUNTIME build -t complement-synapse-editable \
--build-arg FROM=synapse-workers-editable \ --build-arg SUNAPSE_WORKERS_IMAGE=synapse-workers-editable \
-f "docker/complement/Dockerfile" "docker/complement" -f "docker/complement/Dockerfile" "docker/complement"
# Prepare the Rust module # Prepare the Rust module
@@ -180,21 +180,24 @@ if [ -z "$skip_docker_build" ]; then
else else
# Build the base Synapse image from the local checkout # Build the base Synapse image from the local checkout
echo_if_github "::group::Build Docker image: matrixdotorg/synapse" echo_if_github "::group::Build Docker image: synapse"
$CONTAINER_RUNTIME build -t matrixdotorg/synapse \ $CONTAINER_RUNTIME build -t synapse \
--build-arg TEST_ONLY_SKIP_DEP_HASH_VERIFICATION \ --build-arg TEST_ONLY_SKIP_DEP_HASH_VERIFICATION \
--build-arg TEST_ONLY_IGNORE_POETRY_LOCKFILE \ --build-arg TEST_ONLY_IGNORE_POETRY_LOCKFILE \
-f "docker/Dockerfile" . -f "docker/Dockerfile" .
echo_if_github "::endgroup::" echo_if_github "::endgroup::"
# Build the workers docker image (from the base Synapse image we just built). # Build the workers docker image (from the base Synapse image we just built).
echo_if_github "::group::Build Docker image: matrixdotorg/synapse-workers" echo_if_github "::group::Build Docker image: synapse-workers"
$CONTAINER_RUNTIME build -t matrixdotorg/synapse-workers -f "docker/Dockerfile-workers" . $CONTAINER_RUNTIME build -t synapse-workers \
--build-arg SYNAPSE_IMAGE=synapse \
-f "docker/Dockerfile-workers" .
echo_if_github "::endgroup::" echo_if_github "::endgroup::"
# Build the unified Complement image (from the worker Synapse image we just built). # Build the unified Complement image (from the worker Synapse image we just built).
echo_if_github "::group::Build Docker image: complement/Dockerfile" echo_if_github "::group::Build Docker image: complement-synapse"
$CONTAINER_RUNTIME build -t complement-synapse \ $CONTAINER_RUNTIME build -t complement-synapse \
--build-arg SYNAPSE_WORKERS_IMAGE=synapse-workers \
-f "docker/complement/Dockerfile" "docker/complement" -f "docker/complement/Dockerfile" "docker/complement"
echo_if_github "::endgroup::" echo_if_github "::endgroup::"
+1 -1
View File
@@ -112,7 +112,7 @@ python3 -m black "${files[@]}"
# Catch any common programming mistakes in Python code. # Catch any common programming mistakes in Python code.
# --quiet suppresses the update check. # --quiet suppresses the update check.
ruff --quiet --fix "${files[@]}" ruff check --quiet --fix "${files[@]}"
# Catch any common programming mistakes in Rust code. # Catch any common programming mistakes in Rust code.
# #
+12 -16
View File
@@ -70,7 +70,6 @@ def cli() -> None:
pip install -e .[dev] pip install -e .[dev]
- A checkout of the sytest repository at ../sytest - A checkout of the sytest repository at ../sytest
- A checkout of the complement repository at ../complement - A checkout of the complement repository at ../complement
Then to use: Then to use:
@@ -116,7 +115,7 @@ def _prepare() -> None:
sytest_repo = get_repo_and_check_clean_checkout("../sytest", "sytest") sytest_repo = get_repo_and_check_clean_checkout("../sytest", "sytest")
complement_repo = get_repo_and_check_clean_checkout("../complement", "complement") complement_repo = get_repo_and_check_clean_checkout("../complement", "complement")
click.secho("Updating Synapse, Sytest and Complement git repos...") click.secho("Updating Synapse and Sytest git repos...")
synapse_repo.remote().fetch() synapse_repo.remote().fetch()
sytest_repo.remote().fetch() sytest_repo.remote().fetch()
complement_repo.remote().fetch() complement_repo.remote().fetch()
@@ -203,28 +202,24 @@ def _prepare() -> None:
# release type. # release type.
if current_version.is_prerelease: if current_version.is_prerelease:
default = release_branch_name default = release_branch_name
complement_default = release_branch_name
elif release_type == "minor": elif release_type == "minor":
default = "develop" default = "develop"
complement_default = "main"
else: else:
default = "master" default = "master"
complement_default = "main"
sy_branch_name = click.prompt( branch_name = click.prompt(
"Which branch should the release be based on?", default=default "Which branch should the release be based on?", default=default
) )
complement_branch = click.prompt( for repo_name, repo in {
"Which Complement branch should the release be based on?", "synapse": synapse_repo,
default=complement_default, "sytest": sytest_repo,
) "complement": complement_repo,
for repo_name, (repo, branch_name) in {
"synapse": (synapse_repo, sy_branch_name),
"sytest": (sytest_repo, sy_branch_name),
"complement": (complement_repo, complement_branch),
}.items(): }.items():
# Special case for Complement: `develop` maps to `main`
if repo_name == "complement" and branch_name == "develop":
branch_name = "main"
base_branch = find_ref(repo, branch_name) base_branch = find_ref(repo, branch_name)
if not base_branch: if not base_branch:
print(f"Could not find base branch {branch_name} for {repo_name}!") print(f"Could not find base branch {branch_name} for {repo_name}!")
@@ -246,7 +241,8 @@ def _prepare() -> None:
# not on subsequent RCs or full releases). # not on subsequent RCs or full releases).
if click.confirm("Push new SyTest branch?", default=True): if click.confirm("Push new SyTest branch?", default=True):
sytest_repo.git.push("-u", sytest_repo.remote().name, release_branch_name) sytest_repo.git.push("-u", sytest_repo.remote().name, release_branch_name)
# The same special case rules apply to Complement.
# Same for Complement
if click.confirm("Push new Complement branch?", default=True): if click.confirm("Push new Complement branch?", default=True):
complement_repo.git.push( complement_repo.git.push(
"-u", complement_repo.remote().name, release_branch_name "-u", complement_repo.remote().name, release_branch_name
+1 -1
View File
@@ -44,7 +44,7 @@ logger = logging.getLogger("generate_workers_map")
class MockHomeserver(HomeServer): class MockHomeserver(HomeServer):
DATASTORE_CLASS = DataStore # type: ignore DATASTORE_CLASS = DataStore
def __init__(self, config: HomeServerConfig, worker_app: Optional[str]) -> None: def __init__(self, config: HomeServerConfig, worker_app: Optional[str]) -> None:
super().__init__(config.server.server_name, config=config) super().__init__(config.server.server_name, config=config)
+3 -2
View File
@@ -119,18 +119,19 @@ BOOLEAN_COLUMNS = {
"e2e_room_keys": ["is_verified"], "e2e_room_keys": ["is_verified"],
"event_edges": ["is_state"], "event_edges": ["is_state"],
"events": ["processed", "outlier", "contains_url"], "events": ["processed", "outlier", "contains_url"],
"local_media_repository": ["safe_from_quarantine"], "local_media_repository": ["safe_from_quarantine", "authenticated"],
"per_user_experimental_features": ["enabled"],
"presence_list": ["accepted"], "presence_list": ["accepted"],
"presence_stream": ["currently_active"], "presence_stream": ["currently_active"],
"public_room_list_stream": ["visibility"], "public_room_list_stream": ["visibility"],
"pushers": ["enabled"], "pushers": ["enabled"],
"redactions": ["have_censored"], "redactions": ["have_censored"],
"remote_media_cache": ["authenticated"],
"room_stats_state": ["is_federatable"], "room_stats_state": ["is_federatable"],
"rooms": ["is_public", "has_auth_chain_index"], "rooms": ["is_public", "has_auth_chain_index"],
"users": ["shadow_banned", "approved", "locked", "suspended"], "users": ["shadow_banned", "approved", "locked", "suspended"],
"un_partial_stated_event_stream": ["rejection_status_changed"], "un_partial_stated_event_stream": ["rejection_status_changed"],
"users_who_share_rooms": ["share_private"], "users_who_share_rooms": ["share_private"],
"per_user_experimental_features": ["enabled"],
} }
+1 -1
View File
@@ -41,7 +41,7 @@ logger = logging.getLogger("update_database")
class MockHomeserver(HomeServer): class MockHomeserver(HomeServer):
DATASTORE_CLASS = DataStore # type: ignore [assignment] DATASTORE_CLASS = DataStore
def __init__(self, config: HomeServerConfig): def __init__(self, config: HomeServerConfig):
super().__init__( super().__init__(
+17 -1
View File
@@ -18,7 +18,7 @@
# [This file includes modifications made by New Vector Limited] # [This file includes modifications made by New Vector Limited]
# #
# #
from typing import Optional, Tuple from typing import TYPE_CHECKING, Optional, Tuple
from typing_extensions import Protocol from typing_extensions import Protocol
@@ -28,6 +28,9 @@ from synapse.appservice import ApplicationService
from synapse.http.site import SynapseRequest from synapse.http.site import SynapseRequest
from synapse.types import Requester from synapse.types import Requester
if TYPE_CHECKING:
from synapse.rest.admin.experimental_features import ExperimentalFeature
# guests always get this device id. # guests always get this device id.
GUEST_DEVICE_ID = "guest_device" GUEST_DEVICE_ID = "guest_device"
@@ -87,6 +90,19 @@ class Auth(Protocol):
AuthError if access is denied for the user in the access token AuthError if access is denied for the user in the access token
""" """
async def get_user_by_req_experimental_feature(
self,
request: SynapseRequest,
feature: "ExperimentalFeature",
allow_guest: bool = False,
allow_expired: bool = False,
allow_locked: bool = False,
) -> Requester:
"""Like `get_user_by_req`, except also checks if the user has access to
the experimental feature. If they don't returns a 404 unrecognized
request.
"""
async def validate_appservice_can_control_user_id( async def validate_appservice_can_control_user_id(
self, app_service: ApplicationService, user_id: str self, app_service: ApplicationService, user_id: str
) -> None: ) -> None:
+29
View File
@@ -28,6 +28,7 @@ from synapse.api.errors import (
Codes, Codes,
InvalidClientTokenError, InvalidClientTokenError,
MissingClientTokenError, MissingClientTokenError,
UnrecognizedRequestError,
) )
from synapse.http.site import SynapseRequest from synapse.http.site import SynapseRequest
from synapse.logging.opentracing import active_span, force_tracing, start_active_span from synapse.logging.opentracing import active_span, force_tracing, start_active_span
@@ -38,8 +39,10 @@ from . import GUEST_DEVICE_ID
from .base import BaseAuth from .base import BaseAuth
if TYPE_CHECKING: if TYPE_CHECKING:
from synapse.rest.admin.experimental_features import ExperimentalFeature
from synapse.server import HomeServer from synapse.server import HomeServer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -106,6 +109,32 @@ class InternalAuth(BaseAuth):
parent_span.set_tag("appservice_id", requester.app_service.id) parent_span.set_tag("appservice_id", requester.app_service.id)
return requester return requester
async def get_user_by_req_experimental_feature(
self,
request: SynapseRequest,
feature: "ExperimentalFeature",
allow_guest: bool = False,
allow_expired: bool = False,
allow_locked: bool = False,
) -> Requester:
try:
requester = await self.get_user_by_req(
request,
allow_guest=allow_guest,
allow_expired=allow_expired,
allow_locked=allow_locked,
)
if await self.store.is_feature_enabled(requester.user.to_string(), feature):
return requester
raise UnrecognizedRequestError(code=404)
except (AuthError, InvalidClientTokenError):
if feature.is_globally_enabled(self.hs.config):
# If its globally enabled then return the auth error
raise
raise UnrecognizedRequestError(code=404)
@cancellable @cancellable
async def _wrapped_get_user_by_req( async def _wrapped_get_user_by_req(
self, self,
+41 -2
View File
@@ -40,6 +40,7 @@ from synapse.api.errors import (
OAuthInsufficientScopeError, OAuthInsufficientScopeError,
StoreError, StoreError,
SynapseError, SynapseError,
UnrecognizedRequestError,
) )
from synapse.http.site import SynapseRequest from synapse.http.site import SynapseRequest
from synapse.logging.context import make_deferred_yieldable from synapse.logging.context import make_deferred_yieldable
@@ -48,6 +49,7 @@ from synapse.util import json_decoder
from synapse.util.caches.cached_call import RetryOnExceptionCachedCall from synapse.util.caches.cached_call import RetryOnExceptionCachedCall
if TYPE_CHECKING: if TYPE_CHECKING:
from synapse.rest.admin.experimental_features import ExperimentalFeature
from synapse.server import HomeServer from synapse.server import HomeServer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -143,6 +145,18 @@ class MSC3861DelegatedAuth(BaseAuth):
# metadata.validate_introspection_endpoint() # metadata.validate_introspection_endpoint()
return metadata return metadata
async def _introspection_endpoint(self) -> str:
"""
Returns the introspection endpoint of the issuer
It uses the config option if set, otherwise it will use OIDC discovery to get it
"""
if self._config.introspection_endpoint is not None:
return self._config.introspection_endpoint
metadata = await self._load_metadata()
return metadata.get("introspection_endpoint")
async def _introspect_token(self, token: str) -> IntrospectionToken: async def _introspect_token(self, token: str) -> IntrospectionToken:
""" """
Send a token to the introspection endpoint and returns the introspection response Send a token to the introspection endpoint and returns the introspection response
@@ -159,8 +173,7 @@ class MSC3861DelegatedAuth(BaseAuth):
Returns: Returns:
The introspection response The introspection response
""" """
metadata = await self._issuer_metadata.get() introspection_endpoint = await self._introspection_endpoint()
introspection_endpoint = metadata.get("introspection_endpoint")
raw_headers: Dict[str, str] = { raw_headers: Dict[str, str] = {
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
"User-Agent": str(self._http_client.user_agent, "utf-8"), "User-Agent": str(self._http_client.user_agent, "utf-8"),
@@ -245,6 +258,32 @@ class MSC3861DelegatedAuth(BaseAuth):
return requester return requester
async def get_user_by_req_experimental_feature(
self,
request: SynapseRequest,
feature: "ExperimentalFeature",
allow_guest: bool = False,
allow_expired: bool = False,
allow_locked: bool = False,
) -> Requester:
try:
requester = await self.get_user_by_req(
request,
allow_guest=allow_guest,
allow_expired=allow_expired,
allow_locked=allow_locked,
)
if await self.store.is_feature_enabled(requester.user.to_string(), feature):
return requester
raise UnrecognizedRequestError(code=404)
except (AuthError, InvalidClientTokenError):
if feature.is_globally_enabled(self.hs.config):
# If its globally enabled then return the auth error
raise
raise UnrecognizedRequestError(code=404)
async def get_user_by_access_token( async def get_user_by_access_token(
self, self,
token: str, token: str,
+13 -1
View File
@@ -50,7 +50,7 @@ class Membership:
KNOCK: Final = "knock" KNOCK: Final = "knock"
LEAVE: Final = "leave" LEAVE: Final = "leave"
BAN: Final = "ban" BAN: Final = "ban"
LIST: Final = {INVITE, JOIN, KNOCK, LEAVE, BAN} LIST: Final = frozenset((INVITE, JOIN, KNOCK, LEAVE, BAN))
class PresenceState: class PresenceState:
@@ -128,9 +128,13 @@ class EventTypes:
SpaceParent: Final = "m.space.parent" SpaceParent: Final = "m.space.parent"
Reaction: Final = "m.reaction" Reaction: Final = "m.reaction"
Sticker: Final = "m.sticker"
LiveLocationShareStart: Final = "m.beacon_info"
CallInvite: Final = "m.call.invite" CallInvite: Final = "m.call.invite"
PollStart: Final = "m.poll.start"
class ToDeviceEventTypes: class ToDeviceEventTypes:
RoomKeyRequest: Final = "m.room_key_request" RoomKeyRequest: Final = "m.room_key_request"
@@ -221,6 +225,11 @@ class EventContentFields:
# This is deprecated in MSC2175. # This is deprecated in MSC2175.
ROOM_CREATOR: Final = "creator" ROOM_CREATOR: Final = "creator"
# The version of the room for `m.room.create` events.
ROOM_VERSION: Final = "room_version"
ROOM_NAME: Final = "name"
# Used in m.room.guest_access events. # Used in m.room.guest_access events.
GUEST_ACCESS: Final = "guest_access" GUEST_ACCESS: Final = "guest_access"
@@ -233,6 +242,9 @@ class EventContentFields:
# an unspecced field added to to-device messages to identify them uniquely-ish # an unspecced field added to to-device messages to identify them uniquely-ish
TO_DEVICE_MSGID: Final = "org.matrix.msgid" TO_DEVICE_MSGID: Final = "org.matrix.msgid"
# `m.room.encryption`` algorithm field
ENCRYPTION_ALGORITHM: Final = "algorithm"
class EventUnsignedContentFields: class EventUnsignedContentFields:
"""Fields found inside the 'unsigned' data on events""" """Fields found inside the 'unsigned' data on events"""
+2 -3
View File
@@ -236,9 +236,8 @@ class Ratelimiter:
requester: The requester that is doing the action, if any. requester: The requester that is doing the action, if any.
key: An arbitrary key used to classify an action. Defaults to the key: An arbitrary key used to classify an action. Defaults to the
requester's user ID. requester's user ID.
n_actions: The number of times the user wants to do this action. If the user n_actions: The number of times the user performed the action. May be negative
cannot do all of the actions, the user's action count is not incremented to "refund" the rate limit.
at all.
_time_now_s: The current time. Optional, defaults to the current time according _time_now_s: The current time. Optional, defaults to the current time according
to self.clock. Only used by tests. to self.clock. Only used by tests.
""" """
+1 -1
View File
@@ -110,7 +110,7 @@ class AdminCmdStore(
class AdminCmdServer(HomeServer): class AdminCmdServer(HomeServer):
DATASTORE_CLASS = AdminCmdStore # type: ignore DATASTORE_CLASS = AdminCmdStore
async def export_data_command(hs: HomeServer, args: argparse.Namespace) -> None: async def export_data_command(hs: HomeServer, args: argparse.Namespace) -> None:
+5 -1
View File
@@ -74,6 +74,9 @@ from synapse.storage.databases.main.event_push_actions import (
EventPushActionsWorkerStore, EventPushActionsWorkerStore,
) )
from synapse.storage.databases.main.events_worker import EventsWorkerStore from synapse.storage.databases.main.events_worker import EventsWorkerStore
from synapse.storage.databases.main.experimental_features import (
ExperimentalFeaturesStore,
)
from synapse.storage.databases.main.filtering import FilteringWorkerStore from synapse.storage.databases.main.filtering import FilteringWorkerStore
from synapse.storage.databases.main.keys import KeyStore from synapse.storage.databases.main.keys import KeyStore
from synapse.storage.databases.main.lock import LockStore from synapse.storage.databases.main.lock import LockStore
@@ -155,6 +158,7 @@ class GenericWorkerStore(
LockStore, LockStore,
SessionStore, SessionStore,
TaskSchedulerWorkerStore, TaskSchedulerWorkerStore,
ExperimentalFeaturesStore,
): ):
# Properties that multiple storage classes define. Tell mypy what the # Properties that multiple storage classes define. Tell mypy what the
# expected type is. # expected type is.
@@ -163,7 +167,7 @@ class GenericWorkerStore(
class GenericWorkerServer(HomeServer): class GenericWorkerServer(HomeServer):
DATASTORE_CLASS = GenericWorkerStore # type: ignore DATASTORE_CLASS = GenericWorkerStore
def _listen_http(self, listener_config: ListenerConfig) -> None: def _listen_http(self, listener_config: ListenerConfig) -> None:
assert listener_config.http_options is not None assert listener_config.http_options is not None
+2 -2
View File
@@ -81,7 +81,7 @@ def gz_wrap(r: Resource) -> Resource:
class SynapseHomeServer(HomeServer): class SynapseHomeServer(HomeServer):
DATASTORE_CLASS = DataStore # type: ignore DATASTORE_CLASS = DataStore
def _listener_http( def _listener_http(
self, self,
@@ -217,7 +217,7 @@ class SynapseHomeServer(HomeServer):
) )
if name in ["media", "federation", "client"]: if name in ["media", "federation", "client"]:
if self.config.server.enable_media_repo: if self.config.media.can_load_media_repo:
media_repo = self.get_media_repository_resource() media_repo = self.get_media_repository_resource()
resources.update( resources.update(
{ {
+6 -4
View File
@@ -140,6 +140,12 @@ class MSC3861:
("experimental", "msc3861", "client_auth_method"), ("experimental", "msc3861", "client_auth_method"),
) )
introspection_endpoint: Optional[str] = attr.ib(
default=None,
validator=attr.validators.optional(attr.validators.instance_of(str)),
)
"""The URL of the introspection endpoint used to validate access tokens."""
account_management_url: Optional[str] = attr.ib( account_management_url: Optional[str] = attr.ib(
default=None, default=None,
validator=attr.validators.optional(attr.validators.instance_of(str)), validator=attr.validators.optional(attr.validators.instance_of(str)),
@@ -437,10 +443,6 @@ class ExperimentalConfig(Config):
"msc3823_account_suspension", False "msc3823_account_suspension", False
) )
self.msc3916_authenticated_media_enabled = experimental.get(
"msc3916_authenticated_media_enabled", False
)
# MSC4151: Report room API (Client-Server API) # MSC4151: Report room API (Client-Server API)
self.msc4151_enabled: bool = experimental.get("msc4151_enabled", False) self.msc4151_enabled: bool = experimental.get("msc4151_enabled", False)
+5 -1
View File
@@ -126,7 +126,7 @@ class ContentRepositoryConfig(Config):
# Only enable the media repo if either the media repo is enabled or the # Only enable the media repo if either the media repo is enabled or the
# current worker app is the media repo. # current worker app is the media repo.
if ( if (
self.root.server.enable_media_repo is False config.get("enable_media_repo", True) is False
and config.get("worker_app") != "synapse.app.media_repository" and config.get("worker_app") != "synapse.app.media_repository"
): ):
self.can_load_media_repo = False self.can_load_media_repo = False
@@ -272,6 +272,10 @@ class ContentRepositoryConfig(Config):
remote_media_lifetime remote_media_lifetime
) )
self.enable_authenticated_media = config.get(
"enable_authenticated_media", False
)
def generate_config_section(self, data_dir_path: str, **kwargs: Any) -> str: def generate_config_section(self, data_dir_path: str, **kwargs: Any) -> str:
assert data_dir_path is not None assert data_dir_path is not None
media_store = os.path.join(data_dir_path, "media_store") media_store = os.path.join(data_dir_path, "media_store")
+5 -6
View File
@@ -384,6 +384,11 @@ class ServerConfig(Config):
# Whether to internally track presence, requires that presence is enabled, # Whether to internally track presence, requires that presence is enabled,
self.track_presence = self.presence_enabled and presence_enabled != "untracked" self.track_presence = self.presence_enabled and presence_enabled != "untracked"
# Determines if presence results for offline users are included on initial/full sync
self.presence_include_offline_users_on_sync = presence_config.get(
"include_offline_users_on_sync", False
)
# Custom presence router module # Custom presence router module
# This is the legacy way of configuring it (the config should now be put in the modules section) # This is the legacy way of configuring it (the config should now be put in the modules section)
self.presence_router_module_class = None self.presence_router_module_class = None
@@ -395,12 +400,6 @@ class ServerConfig(Config):
self.presence_router_config, self.presence_router_config,
) = load_module(presence_router_config, ("presence", "presence_router")) ) = load_module(presence_router_config, ("presence", "presence_router"))
# whether to enable the media repository endpoints. This should be set
# to false if the media repository is running as a separate endpoint;
# doing so ensures that we will not run cache cleanup jobs on the
# master, potentially causing inconsistency.
self.enable_media_repo = config.get("enable_media_repo", True)
# Whether to require authentication to retrieve profile data (avatars, # Whether to require authentication to retrieve profile data (avatars,
# display names) of other users through the client API. # display names) of other users through the client API.
self.require_auth_for_profile_requests = config.get( self.require_auth_for_profile_requests = config.get(
+19
View File
@@ -554,3 +554,22 @@ def relation_from_event(event: EventBase) -> Optional[_EventRelation]:
aggregation_key = None aggregation_key = None
return _EventRelation(parent_id, rel_type, aggregation_key) return _EventRelation(parent_id, rel_type, aggregation_key)
@attr.s(slots=True, frozen=True, auto_attribs=True)
class StrippedStateEvent:
"""
A stripped down state event. Usually used for remote invite/knocks so the user can
make an informed decision on whether they want to join.
Attributes:
type: Event `type`
state_key: Event `state_key`
sender: Event `sender`
content: Event `content`
"""
type: str
state_key: str
sender: str
content: Dict[str, Any]
+28 -1
View File
@@ -49,7 +49,7 @@ from synapse.api.errors import Codes, SynapseError
from synapse.api.room_versions import RoomVersion from synapse.api.room_versions import RoomVersion
from synapse.types import JsonDict, Requester from synapse.types import JsonDict, Requester
from . import EventBase, make_event_from_dict from . import EventBase, StrippedStateEvent, make_event_from_dict
if TYPE_CHECKING: if TYPE_CHECKING:
from synapse.handlers.relations import BundledAggregations from synapse.handlers.relations import BundledAggregations
@@ -854,3 +854,30 @@ def strip_event(event: EventBase) -> JsonDict:
"content": event.content, "content": event.content,
"sender": event.sender, "sender": event.sender,
} }
def parse_stripped_state_event(raw_stripped_event: Any) -> Optional[StrippedStateEvent]:
"""
Given a raw value from an event's `unsigned` field, attempt to parse it into a
`StrippedStateEvent`.
"""
if isinstance(raw_stripped_event, dict):
# All of these fields are required
type = raw_stripped_event.get("type")
state_key = raw_stripped_event.get("state_key")
sender = raw_stripped_event.get("sender")
content = raw_stripped_event.get("content")
if (
isinstance(type, str)
and isinstance(state_key, str)
and isinstance(sender, str)
and isinstance(content, dict)
):
return StrippedStateEvent(
type=type,
state_key=state_key,
sender=sender,
content=content,
)
return None
@@ -338,12 +338,11 @@ class PerDestinationQueue:
# not caught up yet # not caught up yet
return return
pending_pdus = []
while True: while True:
self._new_data_to_send = False self._new_data_to_send = False
async with _TransactionQueueManager(self) as ( async with _TransactionQueueManager(self) as (
pending_pdus, pending_pdus, # noqa: F811
pending_edus, pending_edus,
): ):
if not pending_pdus and not pending_edus: if not pending_pdus and not pending_edus:
@@ -33,6 +33,7 @@ from synapse.federation.transport.server.federation import (
FEDERATION_SERVLET_CLASSES, FEDERATION_SERVLET_CLASSES,
FederationAccountStatusServlet, FederationAccountStatusServlet,
FederationMediaDownloadServlet, FederationMediaDownloadServlet,
FederationMediaThumbnailServlet,
FederationUnstableClientKeysClaimServlet, FederationUnstableClientKeysClaimServlet,
) )
from synapse.http.server import HttpServer, JsonResource from synapse.http.server import HttpServer, JsonResource
@@ -316,8 +317,11 @@ def register_servlets(
): ):
continue continue
if servletclass == FederationMediaDownloadServlet: if (
if not hs.config.server.enable_media_repo: servletclass == FederationMediaDownloadServlet
or servletclass == FederationMediaThumbnailServlet
):
if not hs.config.media.can_load_media_repo:
continue continue
servletclass( servletclass(
@@ -363,6 +363,8 @@ class BaseFederationServlet:
if ( if (
func.__self__.__class__.__name__ # type: ignore func.__self__.__class__.__name__ # type: ignore
== "FederationMediaDownloadServlet" == "FederationMediaDownloadServlet"
or func.__self__.__class__.__name__ # type: ignore
== "FederationMediaThumbnailServlet"
): ):
response = await func( response = await func(
origin, content, request, *args, **kwargs origin, content, request, *args, **kwargs
@@ -375,6 +377,8 @@ class BaseFederationServlet:
if ( if (
func.__self__.__class__.__name__ # type: ignore func.__self__.__class__.__name__ # type: ignore
== "FederationMediaDownloadServlet" == "FederationMediaDownloadServlet"
or func.__self__.__class__.__name__ # type: ignore
== "FederationMediaThumbnailServlet"
): ):
response = await func( response = await func(
origin, content, request, *args, **kwargs origin, content, request, *args, **kwargs
@@ -46,11 +46,13 @@ from synapse.http.servlet import (
parse_boolean_from_args, parse_boolean_from_args,
parse_integer, parse_integer,
parse_integer_from_args, parse_integer_from_args,
parse_string,
parse_string_from_args, parse_string_from_args,
parse_strings_from_args, parse_strings_from_args,
) )
from synapse.http.site import SynapseRequest from synapse.http.site import SynapseRequest
from synapse.media._base import DEFAULT_MAX_TIMEOUT_MS, MAXIMUM_ALLOWED_MAX_TIMEOUT_MS from synapse.media._base import DEFAULT_MAX_TIMEOUT_MS, MAXIMUM_ALLOWED_MAX_TIMEOUT_MS
from synapse.media.thumbnailer import ThumbnailProvider
from synapse.types import JsonDict from synapse.types import JsonDict
from synapse.util import SYNAPSE_VERSION from synapse.util import SYNAPSE_VERSION
from synapse.util.ratelimitutils import FederationRateLimiter from synapse.util.ratelimitutils import FederationRateLimiter
@@ -826,6 +828,59 @@ class FederationMediaDownloadServlet(BaseFederationServerServlet):
) )
class FederationMediaThumbnailServlet(BaseFederationServerServlet):
"""
Implementation of new federation media `/thumbnail` endpoint outlined in MSC3916. Returns
a multipart/mixed response consisting of a JSON object and the requested media
item. This endpoint only returns local media.
"""
PATH = "/media/thumbnail/(?P<media_id>[^/]*)"
RATELIMIT = True
def __init__(
self,
hs: "HomeServer",
ratelimiter: FederationRateLimiter,
authenticator: Authenticator,
server_name: str,
):
super().__init__(hs, authenticator, ratelimiter, server_name)
self.media_repo = self.hs.get_media_repository()
self.dynamic_thumbnails = hs.config.media.dynamic_thumbnails
self.thumbnail_provider = ThumbnailProvider(
hs, self.media_repo, self.media_repo.media_storage
)
async def on_GET(
self,
origin: Optional[str],
content: Literal[None],
request: SynapseRequest,
media_id: str,
) -> None:
width = parse_integer(request, "width", required=True)
height = parse_integer(request, "height", required=True)
method = parse_string(request, "method", "scale")
# TODO Parse the Accept header to get an prioritised list of thumbnail types.
m_type = "image/png"
max_timeout_ms = parse_integer(
request, "timeout_ms", default=DEFAULT_MAX_TIMEOUT_MS
)
max_timeout_ms = min(max_timeout_ms, MAXIMUM_ALLOWED_MAX_TIMEOUT_MS)
if self.dynamic_thumbnails:
await self.thumbnail_provider.select_or_generate_local_thumbnail(
request, media_id, width, height, method, m_type, max_timeout_ms, True
)
else:
await self.thumbnail_provider.respond_local_thumbnail(
request, media_id, width, height, method, m_type, max_timeout_ms, True
)
self.media_repo.mark_recently_accessed(None, media_id)
FEDERATION_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = ( FEDERATION_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = (
FederationSendServlet, FederationSendServlet,
FederationEventServlet, FederationEventServlet,
@@ -858,4 +913,5 @@ FEDERATION_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = (
FederationMakeKnockServlet, FederationMakeKnockServlet,
FederationAccountStatusServlet, FederationAccountStatusServlet,
FederationMediaDownloadServlet, FederationMediaDownloadServlet,
FederationMediaThumbnailServlet,
) )
+4
View File
@@ -283,6 +283,10 @@ class DeactivateAccountHandler:
ratelimit=False, ratelimit=False,
require_consent=False, require_consent=False,
) )
# Mark the room forgotten too, because they won't be able to do this
# for us. This may lead to the room being purged eventually.
await self._room_member_handler.forget(user, room_id)
except Exception: except Exception:
logger.exception( logger.exception(
"Failed to part user %r from room %r: ignoring and continuing", "Failed to part user %r from room %r: ignoring and continuing",
+13 -4
View File
@@ -39,6 +39,7 @@ from synapse.metrics.background_process_metrics import (
) )
from synapse.storage.databases.main.client_ips import DeviceLastConnectionInfo from synapse.storage.databases.main.client_ips import DeviceLastConnectionInfo
from synapse.types import ( from synapse.types import (
DeviceListUpdates,
JsonDict, JsonDict,
JsonMapping, JsonMapping,
ScheduledTask, ScheduledTask,
@@ -214,7 +215,7 @@ class DeviceWorkerHandler:
@cancellable @cancellable
async def get_user_ids_changed( async def get_user_ids_changed(
self, user_id: str, from_token: StreamToken self, user_id: str, from_token: StreamToken
) -> JsonDict: ) -> DeviceListUpdates:
"""Get list of users that have had the devices updated, or have newly """Get list of users that have had the devices updated, or have newly
joined a room, that `user_id` may be interested in. joined a room, that `user_id` may be interested in.
""" """
@@ -341,11 +342,19 @@ class DeviceWorkerHandler:
possibly_joined = set() possibly_joined = set()
possibly_left = set() possibly_left = set()
result = {"changed": list(possibly_joined), "left": list(possibly_left)} device_list_updates = DeviceListUpdates(
changed=possibly_joined,
left=possibly_left,
)
log_kv(result) log_kv(
{
"changed": device_list_updates.changed,
"left": device_list_updates.left,
}
)
return result return device_list_updates
async def on_federation_query_user_devices(self, user_id: str) -> JsonDict: async def on_federation_query_user_devices(self, user_id: str) -> JsonDict:
if not self.hs.is_mine(UserID.from_string(user_id)): if not self.hs.is_mine(UserID.from_string(user_id)):
+18 -8
View File
@@ -291,13 +291,20 @@ class E2eKeysHandler:
# Only try and fetch keys for destinations that are not marked as # Only try and fetch keys for destinations that are not marked as
# down. # down.
filtered_destinations = await filter_destinations_by_retry_limiter( unfiltered_destinations = remote_queries_not_in_cache.keys()
remote_queries_not_in_cache.keys(), filtered_destinations = set(
self.clock, await filter_destinations_by_retry_limiter(
self.store, unfiltered_destinations,
# Let's give an arbitrary grace period for those hosts that are self.clock,
# only recently down self.store,
retry_due_within_ms=60 * 1000, # Let's give an arbitrary grace period for those hosts that are
# only recently down
retry_due_within_ms=60 * 1000,
)
)
failures.update(
(dest, _NOT_READY_FOR_RETRY_FAILURE)
for dest in (unfiltered_destinations - filtered_destinations)
) )
await concurrently_execute( await concurrently_execute(
@@ -1641,6 +1648,9 @@ def _check_device_signature(
raise SynapseError(400, "Invalid signature", Codes.INVALID_SIGNATURE) raise SynapseError(400, "Invalid signature", Codes.INVALID_SIGNATURE)
_NOT_READY_FOR_RETRY_FAILURE = {"status": 503, "message": "Not ready for retry"}
def _exception_to_failure(e: Exception) -> JsonDict: def _exception_to_failure(e: Exception) -> JsonDict:
if isinstance(e, SynapseError): if isinstance(e, SynapseError):
return {"status": e.code, "errcode": e.errcode, "message": str(e)} return {"status": e.code, "errcode": e.errcode, "message": str(e)}
@@ -1649,7 +1659,7 @@ def _exception_to_failure(e: Exception) -> JsonDict:
return {"status": e.code, "message": str(e)} return {"status": e.code, "message": str(e)}
if isinstance(e, NotRetryingDestination): if isinstance(e, NotRetryingDestination):
return {"status": 503, "message": "Not ready for retry"} return _NOT_READY_FOR_RETRY_FAILURE
# include ConnectionRefused and other errors # include ConnectionRefused and other errors
# #
+9 -9
View File
@@ -34,7 +34,7 @@ from synapse.api.errors import (
from synapse.logging.opentracing import log_kv, trace from synapse.logging.opentracing import log_kv, trace
from synapse.storage.databases.main.e2e_room_keys import RoomKey from synapse.storage.databases.main.e2e_room_keys import RoomKey
from synapse.types import JsonDict from synapse.types import JsonDict
from synapse.util.async_helpers import Linearizer from synapse.util.async_helpers import ReadWriteLock
if TYPE_CHECKING: if TYPE_CHECKING:
from synapse.server import HomeServer from synapse.server import HomeServer
@@ -58,7 +58,7 @@ class E2eRoomKeysHandler:
# clients belonging to a user will receive and try to upload a new session at # clients belonging to a user will receive and try to upload a new session at
# roughly the same time. Also used to lock out uploads when the key is being # roughly the same time. Also used to lock out uploads when the key is being
# changed. # changed.
self._upload_linearizer = Linearizer("upload_room_keys_lock") self._upload_lock = ReadWriteLock()
@trace @trace
async def get_room_keys( async def get_room_keys(
@@ -89,7 +89,7 @@ class E2eRoomKeysHandler:
# we deliberately take the lock to get keys so that changing the version # we deliberately take the lock to get keys so that changing the version
# works atomically # works atomically
async with self._upload_linearizer.queue(user_id): async with self._upload_lock.read(user_id):
# make sure the backup version exists # make sure the backup version exists
try: try:
await self.store.get_e2e_room_keys_version_info(user_id, version) await self.store.get_e2e_room_keys_version_info(user_id, version)
@@ -132,7 +132,7 @@ class E2eRoomKeysHandler:
""" """
# lock for consistency with uploading # lock for consistency with uploading
async with self._upload_linearizer.queue(user_id): async with self._upload_lock.write(user_id):
# make sure the backup version exists # make sure the backup version exists
try: try:
version_info = await self.store.get_e2e_room_keys_version_info( version_info = await self.store.get_e2e_room_keys_version_info(
@@ -193,7 +193,7 @@ class E2eRoomKeysHandler:
# TODO: Validate the JSON to make sure it has the right keys. # TODO: Validate the JSON to make sure it has the right keys.
# XXX: perhaps we should use a finer grained lock here? # XXX: perhaps we should use a finer grained lock here?
async with self._upload_linearizer.queue(user_id): async with self._upload_lock.write(user_id):
# Check that the version we're trying to upload is the current version # Check that the version we're trying to upload is the current version
try: try:
version_info = await self.store.get_e2e_room_keys_version_info(user_id) version_info = await self.store.get_e2e_room_keys_version_info(user_id)
@@ -355,7 +355,7 @@ class E2eRoomKeysHandler:
# TODO: Validate the JSON to make sure it has the right keys. # TODO: Validate the JSON to make sure it has the right keys.
# lock everyone out until we've switched version # lock everyone out until we've switched version
async with self._upload_linearizer.queue(user_id): async with self._upload_lock.write(user_id):
new_version = await self.store.create_e2e_room_keys_version( new_version = await self.store.create_e2e_room_keys_version(
user_id, version_info user_id, version_info
) )
@@ -382,7 +382,7 @@ class E2eRoomKeysHandler:
} }
""" """
async with self._upload_linearizer.queue(user_id): async with self._upload_lock.read(user_id):
try: try:
res = await self.store.get_e2e_room_keys_version_info(user_id, version) res = await self.store.get_e2e_room_keys_version_info(user_id, version)
except StoreError as e: except StoreError as e:
@@ -407,7 +407,7 @@ class E2eRoomKeysHandler:
NotFoundError: if this backup version doesn't exist NotFoundError: if this backup version doesn't exist
""" """
async with self._upload_linearizer.queue(user_id): async with self._upload_lock.write(user_id):
try: try:
await self.store.delete_e2e_room_keys_version(user_id, version) await self.store.delete_e2e_room_keys_version(user_id, version)
except StoreError as e: except StoreError as e:
@@ -437,7 +437,7 @@ class E2eRoomKeysHandler:
raise SynapseError( raise SynapseError(
400, "Version in body does not match", Codes.INVALID_PARAM 400, "Version in body does not match", Codes.INVALID_PARAM
) )
async with self._upload_linearizer.queue(user_id): async with self._upload_lock.write(user_id):
try: try:
old_info = await self.store.get_e2e_room_keys_version_info( old_info = await self.store.get_e2e_room_keys_version_info(
user_id, version user_id, version
+7 -1
View File
@@ -286,8 +286,14 @@ class ReceiptEventSource(EventSource[MultiWriterStreamToken, JsonMapping]):
room_ids: Iterable[str], room_ids: Iterable[str],
is_guest: bool, is_guest: bool,
explicit_room_id: Optional[str] = None, explicit_room_id: Optional[str] = None,
to_key: Optional[MultiWriterStreamToken] = None,
) -> Tuple[List[JsonMapping], MultiWriterStreamToken]: ) -> Tuple[List[JsonMapping], MultiWriterStreamToken]:
to_key = self.get_current_key() """
Find read receipts for given rooms (> `from_token` and <= `to_token`)
"""
if to_key is None:
to_key = self.get_current_key()
if from_key == to_key: if from_key == to_key:
return [], to_key return [], to_key
+2
View File
@@ -1188,6 +1188,8 @@ class RoomCreationHandler:
) )
events_to_send.append((power_event, power_context)) events_to_send.append((power_event, power_context))
else: else:
# Please update the docs for `default_power_level_content_override` when
# updating the `events` dict below
power_level_content: JsonDict = { power_level_content: JsonDict = {
"users": {creator_id: 100}, "users": {creator_id: 100},
"users_default": 0, "users_default": 0,
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -293,7 +293,9 @@ class StatsHandler:
"history_visibility" "history_visibility"
) )
elif delta.event_type == EventTypes.RoomEncryption: elif delta.event_type == EventTypes.RoomEncryption:
room_state["encryption"] = event_content.get("algorithm") room_state["encryption"] = event_content.get(
EventContentFields.ENCRYPTION_ALGORITHM
)
elif delta.event_type == EventTypes.Name: elif delta.event_type == EventTypes.Name:
room_state["name"] = event_content.get("name") room_state["name"] = event_content.get("name")
elif delta.event_type == EventTypes.Topic: elif delta.event_type == EventTypes.Topic:
+17 -11
View File
@@ -1352,7 +1352,7 @@ class SyncHandler:
await_full_state = True await_full_state = True
lazy_load_members = False lazy_load_members = False
state_at_timeline_end = await self._state_storage_controller.get_state_at( state_at_timeline_end = await self._state_storage_controller.get_state_ids_at(
room_id, room_id,
stream_position=end_token, stream_position=end_token,
state_filter=state_filter, state_filter=state_filter,
@@ -1480,11 +1480,13 @@ class SyncHandler:
else: else:
# We can get here if the user has ignored the senders of all # We can get here if the user has ignored the senders of all
# the recent events. # the recent events.
state_at_timeline_start = await self._state_storage_controller.get_state_at( state_at_timeline_start = (
room_id, await self._state_storage_controller.get_state_ids_at(
stream_position=end_token, room_id,
state_filter=state_filter, stream_position=end_token,
await_full_state=await_full_state, state_filter=state_filter,
await_full_state=await_full_state,
)
) )
if batch.limited: if batch.limited:
@@ -1502,14 +1504,14 @@ class SyncHandler:
# about them). # about them).
state_filter = StateFilter.all() state_filter = StateFilter.all()
state_at_previous_sync = await self._state_storage_controller.get_state_at( state_at_previous_sync = await self._state_storage_controller.get_state_ids_at(
room_id, room_id,
stream_position=since_token, stream_position=since_token,
state_filter=state_filter, state_filter=state_filter,
await_full_state=await_full_state, await_full_state=await_full_state,
) )
state_at_timeline_end = await self._state_storage_controller.get_state_at( state_at_timeline_end = await self._state_storage_controller.get_state_ids_at(
room_id, room_id,
stream_position=end_token, stream_position=end_token,
state_filter=state_filter, state_filter=state_filter,
@@ -2268,7 +2270,11 @@ class SyncHandler:
user=user, user=user,
from_key=presence_key, from_key=presence_key,
is_guest=sync_config.is_guest, is_guest=sync_config.is_guest,
include_offline=include_offline, include_offline=(
True
if self.hs_config.server.presence_include_offline_users_on_sync
else include_offline
),
) )
assert presence_key assert presence_key
sync_result_builder.now_token = now_token.copy_and_replace( sync_result_builder.now_token = now_token.copy_and_replace(
@@ -2508,7 +2514,7 @@ class SyncHandler:
continue continue
if room_id in sync_result_builder.joined_room_ids or has_join: if room_id in sync_result_builder.joined_room_ids or has_join:
old_state_ids = await self._state_storage_controller.get_state_at( old_state_ids = await self._state_storage_controller.get_state_ids_at(
room_id, room_id,
since_token, since_token,
state_filter=StateFilter.from_types([(EventTypes.Member, user_id)]), state_filter=StateFilter.from_types([(EventTypes.Member, user_id)]),
@@ -2539,7 +2545,7 @@ class SyncHandler:
else: else:
if not old_state_ids: if not old_state_ids:
old_state_ids = ( old_state_ids = (
await self._state_storage_controller.get_state_at( await self._state_storage_controller.get_state_ids_at(
room_id, room_id,
since_token, since_token,
state_filter=StateFilter.from_types( state_filter=StateFilter.from_types(
+8 -1
View File
@@ -565,7 +565,12 @@ class TypingNotificationEventSource(EventSource[int, JsonMapping]):
room_ids: Iterable[str], room_ids: Iterable[str],
is_guest: bool, is_guest: bool,
explicit_room_id: Optional[str] = None, explicit_room_id: Optional[str] = None,
to_key: Optional[int] = None,
) -> Tuple[List[JsonMapping], int]: ) -> Tuple[List[JsonMapping], int]:
"""
Find typing notifications for given rooms (> `from_token` and <= `to_token`)
"""
with Measure(self.clock, "typing.get_new_events"): with Measure(self.clock, "typing.get_new_events"):
from_key = int(from_key) from_key = int(from_key)
handler = self.get_typing_handler() handler = self.get_typing_handler()
@@ -574,7 +579,9 @@ class TypingNotificationEventSource(EventSource[int, JsonMapping]):
for room_id in room_ids: for room_id in room_ids:
if room_id not in handler._room_serials: if room_id not in handler._room_serials:
continue continue
if handler._room_serials[room_id] <= from_key: if handler._room_serials[room_id] <= from_key or (
to_key is not None and handler._room_serials[room_id] > to_key
):
continue continue
events.append(self._make_event_for(room_id)) events.append(self._make_event_for(room_id))
+80 -46
View File
@@ -90,7 +90,7 @@ from synapse.logging.context import make_deferred_yieldable, run_in_background
from synapse.logging.opentracing import set_tag, start_active_span, tags from synapse.logging.opentracing import set_tag, start_active_span, tags
from synapse.types import JsonDict from synapse.types import JsonDict
from synapse.util import json_decoder from synapse.util import json_decoder
from synapse.util.async_helpers import AwakenableSleeper, timeout_deferred from synapse.util.async_helpers import AwakenableSleeper, Linearizer, timeout_deferred
from synapse.util.metrics import Measure from synapse.util.metrics import Measure
from synapse.util.stringutils import parse_and_validate_server_name from synapse.util.stringutils import parse_and_validate_server_name
@@ -475,6 +475,8 @@ class MatrixFederationHttpClient:
use_proxy=True, use_proxy=True,
) )
self.remote_download_linearizer = Linearizer("remote_download_linearizer", 6)
def wake_destination(self, destination: str) -> None: def wake_destination(self, destination: str) -> None:
"""Called when the remote server may have come back online.""" """Called when the remote server may have come back online."""
@@ -1486,35 +1488,44 @@ class MatrixFederationHttpClient:
) )
headers = dict(response.headers.getAllRawHeaders()) headers = dict(response.headers.getAllRawHeaders())
expected_size = response.length expected_size = response.length
# if we don't get an expected length then use the max length
if expected_size == UNKNOWN_LENGTH: if expected_size == UNKNOWN_LENGTH:
expected_size = max_size expected_size = max_size
logger.debug( else:
f"File size unknown, assuming file is max allowable size: {max_size}" if int(expected_size) > max_size:
) msg = "Requested file is too large > %r bytes" % (max_size,)
logger.warning(
"{%s} [%s] %s",
request.txn_id,
request.destination,
msg,
)
raise SynapseError(HTTPStatus.BAD_GATEWAY, msg, Codes.TOO_LARGE)
read_body, _ = await download_ratelimiter.can_do_action( read_body, _ = await download_ratelimiter.can_do_action(
requester=None, requester=None,
key=ip_address, key=ip_address,
n_actions=expected_size, n_actions=expected_size,
)
if not read_body:
msg = "Requested file size exceeds ratelimits"
logger.warning(
"{%s} [%s] %s",
request.txn_id,
request.destination,
msg,
) )
raise SynapseError(HTTPStatus.TOO_MANY_REQUESTS, msg, Codes.LIMIT_EXCEEDED) if not read_body:
msg = "Requested file size exceeds ratelimits"
logger.warning(
"{%s} [%s] %s",
request.txn_id,
request.destination,
msg,
)
raise SynapseError(
HTTPStatus.TOO_MANY_REQUESTS, msg, Codes.LIMIT_EXCEEDED
)
try: try:
# add a byte of headroom to max size as function errs at >= async with self.remote_download_linearizer.queue(ip_address):
d = read_body_with_max_size(response, output_stream, expected_size + 1) # add a byte of headroom to max size as function errs at >=
d.addTimeout(self.default_timeout_seconds, self.reactor) d = read_body_with_max_size(response, output_stream, expected_size + 1)
length = await make_deferred_yieldable(d) d.addTimeout(self.default_timeout_seconds, self.reactor)
length = await make_deferred_yieldable(d)
except BodyExceededMaxSize: except BodyExceededMaxSize:
msg = "Requested file is too large > %r bytes" % (expected_size,) msg = "Requested file is too large > %r bytes" % (expected_size,)
logger.warning( logger.warning(
@@ -1560,6 +1571,13 @@ class MatrixFederationHttpClient:
request.method, request.method,
request.uri.decode("ascii"), request.uri.decode("ascii"),
) )
# if we didn't know the length upfront, decrement the actual size from ratelimiter
if response.length == UNKNOWN_LENGTH:
download_ratelimiter.record_action(
requester=None, key=ip_address, n_actions=length
)
return length, headers return length, headers
async def federation_get_file( async def federation_get_file(
@@ -1630,29 +1648,37 @@ class MatrixFederationHttpClient:
) )
headers = dict(response.headers.getAllRawHeaders()) headers = dict(response.headers.getAllRawHeaders())
expected_size = response.length expected_size = response.length
# if we don't get an expected length then use the max length
if expected_size == UNKNOWN_LENGTH: if expected_size == UNKNOWN_LENGTH:
expected_size = max_size expected_size = max_size
logger.debug( else:
f"File size unknown, assuming file is max allowable size: {max_size}" if int(expected_size) > max_size:
) msg = "Requested file is too large > %r bytes" % (max_size,)
logger.warning(
"{%s} [%s] %s",
request.txn_id,
request.destination,
msg,
)
raise SynapseError(HTTPStatus.BAD_GATEWAY, msg, Codes.TOO_LARGE)
read_body, _ = await download_ratelimiter.can_do_action( read_body, _ = await download_ratelimiter.can_do_action(
requester=None, requester=None,
key=ip_address, key=ip_address,
n_actions=expected_size, n_actions=expected_size,
)
if not read_body:
msg = "Requested file size exceeds ratelimits"
logger.warning(
"{%s} [%s] %s",
request.txn_id,
request.destination,
msg,
) )
raise SynapseError(HTTPStatus.TOO_MANY_REQUESTS, msg, Codes.LIMIT_EXCEEDED) if not read_body:
msg = "Requested file size exceeds ratelimits"
logger.warning(
"{%s} [%s] %s",
request.txn_id,
request.destination,
msg,
)
raise SynapseError(
HTTPStatus.TOO_MANY_REQUESTS, msg, Codes.LIMIT_EXCEEDED
)
# this should be a multipart/mixed response with the boundary string in the header # this should be a multipart/mixed response with the boundary string in the header
try: try:
@@ -1672,11 +1698,12 @@ class MatrixFederationHttpClient:
raise SynapseError(HTTPStatus.BAD_GATEWAY, msg) raise SynapseError(HTTPStatus.BAD_GATEWAY, msg)
try: try:
# add a byte of headroom to max size as `_MultipartParserProtocol.dataReceived` errs at >= async with self.remote_download_linearizer.queue(ip_address):
deferred = read_multipart_response( # add a byte of headroom to max size as `_MultipartParserProtocol.dataReceived` errs at >=
response, output_stream, boundary, expected_size + 1 deferred = read_multipart_response(
) response, output_stream, boundary, expected_size + 1
deferred.addTimeout(self.default_timeout_seconds, self.reactor) )
deferred.addTimeout(self.default_timeout_seconds, self.reactor)
except BodyExceededMaxSize: except BodyExceededMaxSize:
msg = "Requested file is too large > %r bytes" % (expected_size,) msg = "Requested file is too large > %r bytes" % (expected_size,)
logger.warning( logger.warning(
@@ -1743,6 +1770,13 @@ class MatrixFederationHttpClient:
request.method, request.method,
request.uri.decode("ascii"), request.uri.decode("ascii"),
) )
# if we didn't know the length upfront, decrement the actual size from ratelimiter
if response.length == UNKNOWN_LENGTH:
download_ratelimiter.record_action(
requester=None, key=ip_address, n_actions=length
)
return length, headers, multipart_response.json return length, headers, multipart_response.json
+10 -2
View File
@@ -62,6 +62,15 @@ HOP_BY_HOP_HEADERS = {
"Upgrade", "Upgrade",
} }
if hasattr(Headers, "_canonicalNameCaps"):
# Twisted < 24.7.0rc1
_canonicalHeaderName = Headers()._canonicalNameCaps # type: ignore[attr-defined]
else:
# Twisted >= 24.7.0rc1
# But note that `_encodeName` still exists on prior versions,
# it just encodes differently
_canonicalHeaderName = Headers()._encodeName
def parse_connection_header_value( def parse_connection_header_value(
connection_header_value: Optional[bytes], connection_header_value: Optional[bytes],
@@ -85,11 +94,10 @@ def parse_connection_header_value(
The set of header names that should not be copied over from the remote response. The set of header names that should not be copied over from the remote response.
The keys are capitalized in canonical capitalization. The keys are capitalized in canonical capitalization.
""" """
headers = Headers()
extra_headers_to_remove: Set[str] = set() extra_headers_to_remove: Set[str] = set()
if connection_header_value: if connection_header_value:
extra_headers_to_remove = { extra_headers_to_remove = {
headers._canonicalNameCaps(connection_option.strip()).decode("ascii") _canonicalHeaderName(connection_option.strip()).decode("ascii")
for connection_option in connection_header_value.split(b",") for connection_option in connection_header_value.split(b",")
} }
+3 -1
View File
@@ -74,6 +74,7 @@ from synapse.api.errors import (
from synapse.config.homeserver import HomeServerConfig from synapse.config.homeserver import HomeServerConfig
from synapse.logging.context import defer_to_thread, preserve_fn, run_in_background from synapse.logging.context import defer_to_thread, preserve_fn, run_in_background
from synapse.logging.opentracing import active_span, start_active_span, trace_servlet from synapse.logging.opentracing import active_span, start_active_span, trace_servlet
from synapse.types import ISynapseReactor
from synapse.util import json_encoder from synapse.util import json_encoder
from synapse.util.caches import intern_dict from synapse.util.caches import intern_dict
from synapse.util.cancellation import is_function_cancellable from synapse.util.cancellation import is_function_cancellable
@@ -868,7 +869,8 @@ async def _async_write_json_to_request_in_thread(
with start_active_span("encode_json_response"): with start_active_span("encode_json_response"):
span = active_span() span = active_span()
json_str = await defer_to_thread(request.reactor, encode, span) reactor: ISynapseReactor = request.reactor # type: ignore
json_str = await defer_to_thread(reactor, encode, span)
_write_bytes_to_request(request, json_str) _write_bytes_to_request(request, json_str)
+1 -1
View File
@@ -683,7 +683,7 @@ class SynapseSite(ProxySite):
self.access_logger = logging.getLogger(logger_name) self.access_logger = logging.getLogger(logger_name)
self.server_version_string = server_version_string.encode("ascii") self.server_version_string = server_version_string.encode("ascii")
def log(self, request: SynapseRequest) -> None: def log(self, request: SynapseRequest) -> None: # type: ignore[override]
pass pass
+47 -3
View File
@@ -430,6 +430,7 @@ class MediaRepository:
media_id: str, media_id: str,
name: Optional[str], name: Optional[str],
max_timeout_ms: int, max_timeout_ms: int,
allow_authenticated: bool = True,
federation: bool = False, federation: bool = False,
) -> None: ) -> None:
"""Responds to requests for local media, if exists, or returns 404. """Responds to requests for local media, if exists, or returns 404.
@@ -442,6 +443,7 @@ class MediaRepository:
the filename in the Content-Disposition header of the response. the filename in the Content-Disposition header of the response.
max_timeout_ms: the maximum number of milliseconds to wait for the max_timeout_ms: the maximum number of milliseconds to wait for the
media to be uploaded. media to be uploaded.
allow_authenticated: whether media marked as authenticated may be served to this request
federation: whether the local media being fetched is for a federation request federation: whether the local media being fetched is for a federation request
Returns: Returns:
@@ -451,6 +453,10 @@ class MediaRepository:
if not media_info: if not media_info:
return return
if self.hs.config.media.enable_authenticated_media and not allow_authenticated:
if media_info.authenticated:
raise NotFoundError()
self.mark_recently_accessed(None, media_id) self.mark_recently_accessed(None, media_id)
media_type = media_info.media_type media_type = media_info.media_type
@@ -481,6 +487,7 @@ class MediaRepository:
max_timeout_ms: int, max_timeout_ms: int,
ip_address: str, ip_address: str,
use_federation_endpoint: bool, use_federation_endpoint: bool,
allow_authenticated: bool = True,
) -> None: ) -> None:
"""Respond to requests for remote media. """Respond to requests for remote media.
@@ -495,6 +502,8 @@ class MediaRepository:
ip_address: the IP address of the requester ip_address: the IP address of the requester
use_federation_endpoint: whether to request the remote media over the new use_federation_endpoint: whether to request the remote media over the new
federation `/download` endpoint federation `/download` endpoint
allow_authenticated: whether media marked as authenticated may be served to this
request
Returns: Returns:
Resolves once a response has successfully been written to request Resolves once a response has successfully been written to request
@@ -526,6 +535,7 @@ class MediaRepository:
self.download_ratelimiter, self.download_ratelimiter,
ip_address, ip_address,
use_federation_endpoint, use_federation_endpoint,
allow_authenticated,
) )
# We deliberately stream the file outside the lock # We deliberately stream the file outside the lock
@@ -542,7 +552,13 @@ class MediaRepository:
respond_404(request) respond_404(request)
async def get_remote_media_info( async def get_remote_media_info(
self, server_name: str, media_id: str, max_timeout_ms: int, ip_address: str self,
server_name: str,
media_id: str,
max_timeout_ms: int,
ip_address: str,
use_federation: bool,
allow_authenticated: bool,
) -> RemoteMedia: ) -> RemoteMedia:
"""Gets the media info associated with the remote file, downloading """Gets the media info associated with the remote file, downloading
if necessary. if necessary.
@@ -553,6 +569,10 @@ class MediaRepository:
max_timeout_ms: the maximum number of milliseconds to wait for the max_timeout_ms: the maximum number of milliseconds to wait for the
media to be uploaded. media to be uploaded.
ip_address: IP address of the requester ip_address: IP address of the requester
use_federation: if a download is necessary, whether to request the remote file
over the federation `/download` endpoint
allow_authenticated: whether media marked as authenticated may be served to this
request
Returns: Returns:
The media info of the file The media info of the file
@@ -573,7 +593,8 @@ class MediaRepository:
max_timeout_ms, max_timeout_ms,
self.download_ratelimiter, self.download_ratelimiter,
ip_address, ip_address,
False, use_federation,
allow_authenticated,
) )
# Ensure we actually use the responder so that it releases resources # Ensure we actually use the responder so that it releases resources
@@ -591,6 +612,7 @@ class MediaRepository:
download_ratelimiter: Ratelimiter, download_ratelimiter: Ratelimiter,
ip_address: str, ip_address: str,
use_federation_endpoint: bool, use_federation_endpoint: bool,
allow_authenticated: bool,
) -> Tuple[Optional[Responder], RemoteMedia]: ) -> Tuple[Optional[Responder], RemoteMedia]:
"""Looks for media in local cache, if not there then attempt to """Looks for media in local cache, if not there then attempt to
download from remote server. download from remote server.
@@ -612,6 +634,11 @@ class MediaRepository:
""" """
media_info = await self.store.get_cached_remote_media(server_name, media_id) media_info = await self.store.get_cached_remote_media(server_name, media_id)
if self.hs.config.media.enable_authenticated_media and not allow_authenticated:
# if it isn't cached then don't fetch it or if it's authenticated then don't serve it
if not media_info or media_info.authenticated:
raise NotFoundError()
# file_id is the ID we use to track the file locally. If we've already # file_id is the ID we use to track the file locally. If we've already
# seen the file then reuse the existing ID, otherwise generate a new # seen the file then reuse the existing ID, otherwise generate a new
# one. # one.
@@ -785,6 +812,11 @@ class MediaRepository:
logger.info("Stored remote media in file %r", fname) logger.info("Stored remote media in file %r", fname)
if self.hs.config.media.enable_authenticated_media:
authenticated = True
else:
authenticated = False
return RemoteMedia( return RemoteMedia(
media_origin=server_name, media_origin=server_name,
media_id=media_id, media_id=media_id,
@@ -795,6 +827,7 @@ class MediaRepository:
filesystem_id=file_id, filesystem_id=file_id,
last_access_ts=time_now_ms, last_access_ts=time_now_ms,
quarantined_by=None, quarantined_by=None,
authenticated=authenticated,
) )
async def _federation_download_remote_file( async def _federation_download_remote_file(
@@ -908,6 +941,11 @@ class MediaRepository:
logger.debug("Stored remote media in file %r", fname) logger.debug("Stored remote media in file %r", fname)
if self.hs.config.media.enable_authenticated_media:
authenticated = True
else:
authenticated = False
return RemoteMedia( return RemoteMedia(
media_origin=server_name, media_origin=server_name,
media_id=media_id, media_id=media_id,
@@ -918,6 +956,7 @@ class MediaRepository:
filesystem_id=file_id, filesystem_id=file_id,
last_access_ts=time_now_ms, last_access_ts=time_now_ms,
quarantined_by=None, quarantined_by=None,
authenticated=authenticated,
) )
def _get_thumbnail_requirements( def _get_thumbnail_requirements(
@@ -1023,7 +1062,12 @@ class MediaRepository:
t_len = os.path.getsize(output_path) t_len = os.path.getsize(output_path)
await self.store.store_local_thumbnail( await self.store.store_local_thumbnail(
media_id, t_width, t_height, t_type, t_method, t_len media_id,
t_width,
t_height,
t_type,
t_method,
t_len,
) )
return output_path return output_path
+101 -23
View File
@@ -26,7 +26,7 @@ from typing import TYPE_CHECKING, List, Optional, Tuple, Type
from PIL import Image from PIL import Image
from synapse.api.errors import Codes, SynapseError, cs_error from synapse.api.errors import Codes, NotFoundError, SynapseError, cs_error
from synapse.config.repository import THUMBNAIL_SUPPORTED_MEDIA_FORMAT_MAP from synapse.config.repository import THUMBNAIL_SUPPORTED_MEDIA_FORMAT_MAP
from synapse.http.server import respond_with_json from synapse.http.server import respond_with_json
from synapse.http.site import SynapseRequest from synapse.http.site import SynapseRequest
@@ -36,9 +36,11 @@ from synapse.media._base import (
ThumbnailInfo, ThumbnailInfo,
respond_404, respond_404,
respond_with_file, respond_with_file,
respond_with_multipart_responder,
respond_with_responder, respond_with_responder,
) )
from synapse.media.media_storage import MediaStorage from synapse.media.media_storage import FileResponder, MediaStorage
from synapse.storage.databases.main.media_repository import LocalMedia
if TYPE_CHECKING: if TYPE_CHECKING:
from synapse.media.media_repository import MediaRepository from synapse.media.media_repository import MediaRepository
@@ -271,6 +273,8 @@ class ThumbnailProvider:
method: str, method: str,
m_type: str, m_type: str,
max_timeout_ms: int, max_timeout_ms: int,
for_federation: bool,
allow_authenticated: bool = True,
) -> None: ) -> None:
media_info = await self.media_repo.get_local_media_info( media_info = await self.media_repo.get_local_media_info(
request, media_id, max_timeout_ms request, media_id, max_timeout_ms
@@ -278,6 +282,12 @@ class ThumbnailProvider:
if not media_info: if not media_info:
return return
# if the media the thumbnail is generated from is authenticated, don't serve the
# thumbnail over an unauthenticated endpoint
if self.hs.config.media.enable_authenticated_media and not allow_authenticated:
if media_info.authenticated:
raise NotFoundError()
thumbnail_infos = await self.store.get_local_media_thumbnails(media_id) thumbnail_infos = await self.store.get_local_media_thumbnails(media_id)
await self._select_and_respond_with_thumbnail( await self._select_and_respond_with_thumbnail(
request, request,
@@ -290,6 +300,8 @@ class ThumbnailProvider:
media_id, media_id,
url_cache=bool(media_info.url_cache), url_cache=bool(media_info.url_cache),
server_name=None, server_name=None,
for_federation=for_federation,
media_info=media_info,
) )
async def select_or_generate_local_thumbnail( async def select_or_generate_local_thumbnail(
@@ -301,14 +313,21 @@ class ThumbnailProvider:
desired_method: str, desired_method: str,
desired_type: str, desired_type: str,
max_timeout_ms: int, max_timeout_ms: int,
for_federation: bool,
allow_authenticated: bool = True,
) -> None: ) -> None:
media_info = await self.media_repo.get_local_media_info( media_info = await self.media_repo.get_local_media_info(
request, media_id, max_timeout_ms request, media_id, max_timeout_ms
) )
if not media_info: if not media_info:
return return
# if the media the thumbnail is generated from is authenticated, don't serve the
# thumbnail over an unauthenticated endpoint
if self.hs.config.media.enable_authenticated_media and not allow_authenticated:
if media_info.authenticated:
raise NotFoundError()
thumbnail_infos = await self.store.get_local_media_thumbnails(media_id) thumbnail_infos = await self.store.get_local_media_thumbnails(media_id)
for info in thumbnail_infos: for info in thumbnail_infos:
t_w = info.width == desired_width t_w = info.width == desired_width
@@ -326,10 +345,16 @@ class ThumbnailProvider:
responder = await self.media_storage.fetch_media(file_info) responder = await self.media_storage.fetch_media(file_info)
if responder: if responder:
await respond_with_responder( if for_federation:
request, responder, info.type, info.length await respond_with_multipart_responder(
) self.hs.get_clock(), request, responder, media_info
return )
return
else:
await respond_with_responder(
request, responder, info.type, info.length
)
return
logger.debug("We don't have a thumbnail of that size. Generating") logger.debug("We don't have a thumbnail of that size. Generating")
@@ -344,7 +369,15 @@ class ThumbnailProvider:
) )
if file_path: if file_path:
await respond_with_file(request, desired_type, file_path) if for_federation:
await respond_with_multipart_responder(
self.hs.get_clock(),
request,
FileResponder(open(file_path, "rb")),
media_info,
)
else:
await respond_with_file(request, desired_type, file_path)
else: else:
logger.warning("Failed to generate thumbnail") logger.warning("Failed to generate thumbnail")
raise SynapseError(400, "Failed to generate thumbnail.") raise SynapseError(400, "Failed to generate thumbnail.")
@@ -360,14 +393,28 @@ class ThumbnailProvider:
desired_type: str, desired_type: str,
max_timeout_ms: int, max_timeout_ms: int,
ip_address: str, ip_address: str,
use_federation: bool,
allow_authenticated: bool = True,
) -> None: ) -> None:
media_info = await self.media_repo.get_remote_media_info( media_info = await self.media_repo.get_remote_media_info(
server_name, media_id, max_timeout_ms, ip_address server_name,
media_id,
max_timeout_ms,
ip_address,
use_federation,
allow_authenticated,
) )
if not media_info: if not media_info:
respond_404(request) respond_404(request)
return return
# if the media the thumbnail is generated from is authenticated, don't serve the
# thumbnail over an unauthenticated endpoint
if self.hs.config.media.enable_authenticated_media and not allow_authenticated:
if media_info.authenticated:
respond_404(request)
return
thumbnail_infos = await self.store.get_remote_media_thumbnails( thumbnail_infos = await self.store.get_remote_media_thumbnails(
server_name, media_id server_name, media_id
) )
@@ -424,16 +471,29 @@ class ThumbnailProvider:
m_type: str, m_type: str,
max_timeout_ms: int, max_timeout_ms: int,
ip_address: str, ip_address: str,
use_federation: bool,
allow_authenticated: bool = True,
) -> None: ) -> None:
# TODO: Don't download the whole remote file # TODO: Don't download the whole remote file
# We should proxy the thumbnail from the remote server instead of # We should proxy the thumbnail from the remote server instead of
# downloading the remote file and generating our own thumbnails. # downloading the remote file and generating our own thumbnails.
media_info = await self.media_repo.get_remote_media_info( media_info = await self.media_repo.get_remote_media_info(
server_name, media_id, max_timeout_ms, ip_address server_name,
media_id,
max_timeout_ms,
ip_address,
use_federation,
allow_authenticated,
) )
if not media_info: if not media_info:
return return
# if the media the thumbnail is generated from is authenticated, don't serve the
# thumbnail over an unauthenticated endpoint
if self.hs.config.media.enable_authenticated_media and not allow_authenticated:
if media_info.authenticated:
raise NotFoundError()
thumbnail_infos = await self.store.get_remote_media_thumbnails( thumbnail_infos = await self.store.get_remote_media_thumbnails(
server_name, media_id server_name, media_id
) )
@@ -448,6 +508,7 @@ class ThumbnailProvider:
media_info.filesystem_id, media_info.filesystem_id,
url_cache=False, url_cache=False,
server_name=server_name, server_name=server_name,
for_federation=False,
) )
async def _select_and_respond_with_thumbnail( async def _select_and_respond_with_thumbnail(
@@ -461,6 +522,8 @@ class ThumbnailProvider:
media_id: str, media_id: str,
file_id: str, file_id: str,
url_cache: bool, url_cache: bool,
for_federation: bool,
media_info: Optional[LocalMedia] = None,
server_name: Optional[str] = None, server_name: Optional[str] = None,
) -> None: ) -> None:
""" """
@@ -476,6 +539,8 @@ class ThumbnailProvider:
file_id: The ID of the media that a thumbnail is being requested for. file_id: The ID of the media that a thumbnail is being requested for.
url_cache: True if this is from a URL cache. url_cache: True if this is from a URL cache.
server_name: The server name, if this is a remote thumbnail. server_name: The server name, if this is a remote thumbnail.
for_federation: whether the request is from the federation /thumbnail request
media_info: metadata about the media being requested.
""" """
logger.debug( logger.debug(
"_select_and_respond_with_thumbnail: media_id=%s desired=%sx%s (%s) thumbnail_infos=%s", "_select_and_respond_with_thumbnail: media_id=%s desired=%sx%s (%s) thumbnail_infos=%s",
@@ -511,13 +576,20 @@ class ThumbnailProvider:
responder = await self.media_storage.fetch_media(file_info) responder = await self.media_storage.fetch_media(file_info)
if responder: if responder:
await respond_with_responder( if for_federation:
request, assert media_info is not None
responder, await respond_with_multipart_responder(
file_info.thumbnail.type, self.hs.get_clock(), request, responder, media_info
file_info.thumbnail.length, )
) return
return else:
await respond_with_responder(
request,
responder,
file_info.thumbnail.type,
file_info.thumbnail.length,
)
return
# If we can't find the thumbnail we regenerate it. This can happen # If we can't find the thumbnail we regenerate it. This can happen
# if e.g. we've deleted the thumbnails but still have the original # if e.g. we've deleted the thumbnails but still have the original
@@ -558,12 +630,18 @@ class ThumbnailProvider:
) )
responder = await self.media_storage.fetch_media(file_info) responder = await self.media_storage.fetch_media(file_info)
await respond_with_responder( if for_federation:
request, assert media_info is not None
responder, await respond_with_multipart_responder(
file_info.thumbnail.type, self.hs.get_clock(), request, responder, media_info
file_info.thumbnail.length, )
) else:
await respond_with_responder(
request,
responder,
file_info.thumbnail.type,
file_info.thumbnail.length,
)
else: else:
# This might be because: # This might be because:
# 1. We can't create thumbnails for the given media (corrupted or # 1. We can't create thumbnails for the given media (corrupted or
+8 -5
View File
@@ -773,6 +773,7 @@ class Notifier:
stream_token = await self.event_sources.bound_future_token(stream_token) stream_token = await self.event_sources.bound_future_token(stream_token)
start = self.clock.time_msec() start = self.clock.time_msec()
logged = False
while True: while True:
current_token = self.event_sources.get_current_token() current_token = self.event_sources.get_current_token()
if stream_token.is_before_or_eq(current_token): if stream_token.is_before_or_eq(current_token):
@@ -783,11 +784,13 @@ class Notifier:
if now - start > 10_000: if now - start > 10_000:
return False return False
logger.info( if not logged:
"Waiting for current token to reach %s; currently at %s", logger.info(
stream_token, "Waiting for current token to reach %s; currently at %s",
current_token, stream_token,
) current_token,
)
logged = True
# TODO: be better # TODO: be better
await self.clock.sleep(0.5) await self.clock.sleep(0.5)
+1 -1
View File
@@ -145,7 +145,7 @@ class ClientRestResource(JsonResource):
password_policy.register_servlets(hs, client_resource) password_policy.register_servlets(hs, client_resource)
knock.register_servlets(hs, client_resource) knock.register_servlets(hs, client_resource)
appservice_ping.register_servlets(hs, client_resource) appservice_ping.register_servlets(hs, client_resource)
if hs.config.server.enable_media_repo: if hs.config.media.can_load_media_repo:
from synapse.rest.client import media from synapse.rest.client import media
media.register_servlets(hs, client_resource) media.register_servlets(hs, client_resource)
+12 -2
View File
@@ -31,7 +31,9 @@ from synapse.rest.admin import admin_patterns, assert_requester_is_admin
from synapse.types import JsonDict, UserID from synapse.types import JsonDict, UserID
if TYPE_CHECKING: if TYPE_CHECKING:
from synapse.server import HomeServer from typing_extensions import assert_never
from synapse.server import HomeServer, HomeServerConfig
class ExperimentalFeature(str, Enum): class ExperimentalFeature(str, Enum):
@@ -39,8 +41,16 @@ class ExperimentalFeature(str, Enum):
Currently supported per-user features Currently supported per-user features
""" """
MSC3026 = "msc3026"
MSC3881 = "msc3881" MSC3881 = "msc3881"
MSC3575 = "msc3575"
def is_globally_enabled(self, config: "HomeServerConfig") -> bool:
if self is ExperimentalFeature.MSC3881:
return config.experimental.msc3881_enabled
if self is ExperimentalFeature.MSC3575:
return config.experimental.msc3575_enabled
assert_never(self)
class ExperimentalFeaturesRestServlet(RestServlet): class ExperimentalFeaturesRestServlet(RestServlet):
+8 -2
View File
@@ -256,9 +256,15 @@ class KeyChangesServlet(RestServlet):
user_id = requester.user.to_string() user_id = requester.user.to_string()
results = await self.device_handler.get_user_ids_changed(user_id, from_token) device_list_updates = await self.device_handler.get_user_ids_changed(
user_id, from_token
)
return 200, results response: JsonDict = {}
response["changed"] = list(device_list_updates.changed)
response["left"] = list(device_list_updates.left)
return 200, response
class OneTimeKeyServlet(RestServlet): class OneTimeKeyServlet(RestServlet):
+26 -17
View File
@@ -47,7 +47,7 @@ from synapse.util.stringutils import parse_and_validate_server_name
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class UnstablePreviewURLServlet(RestServlet): class PreviewURLServlet(RestServlet):
""" """
Same as `GET /_matrix/media/r0/preview_url`, this endpoint provides a generic preview API Same as `GET /_matrix/media/r0/preview_url`, this endpoint provides a generic preview API
for URLs which outputs Open Graph (https://ogp.me/) responses (with some Matrix for URLs which outputs Open Graph (https://ogp.me/) responses (with some Matrix
@@ -65,9 +65,7 @@ class UnstablePreviewURLServlet(RestServlet):
* Matrix cannot be used to distribute the metadata between homeservers. * Matrix cannot be used to distribute the metadata between homeservers.
""" """
PATTERNS = [ PATTERNS = [re.compile(r"^/_matrix/client/v1/media/preview_url$")]
re.compile(r"^/_matrix/client/unstable/org.matrix.msc3916/media/preview_url$")
]
def __init__( def __init__(
self, self,
@@ -95,10 +93,8 @@ class UnstablePreviewURLServlet(RestServlet):
respond_with_json_bytes(request, 200, og, send_cors=True) respond_with_json_bytes(request, 200, og, send_cors=True)
class UnstableMediaConfigResource(RestServlet): class MediaConfigResource(RestServlet):
PATTERNS = [ PATTERNS = [re.compile(r"^/_matrix/client/v1/media/config$")]
re.compile(r"^/_matrix/client/unstable/org.matrix.msc3916/media/config$")
]
def __init__(self, hs: "HomeServer"): def __init__(self, hs: "HomeServer"):
super().__init__() super().__init__()
@@ -112,10 +108,10 @@ class UnstableMediaConfigResource(RestServlet):
respond_with_json(request, 200, self.limits_dict, send_cors=True) respond_with_json(request, 200, self.limits_dict, send_cors=True)
class UnstableThumbnailResource(RestServlet): class ThumbnailResource(RestServlet):
PATTERNS = [ PATTERNS = [
re.compile( re.compile(
"/_matrix/client/unstable/org.matrix.msc3916/media/thumbnail/(?P<server_name>[^/]*)/(?P<media_id>[^/]*)$" "/_matrix/client/v1/media/thumbnail/(?P<server_name>[^/]*)/(?P<media_id>[^/]*)$"
) )
] ]
@@ -159,11 +155,25 @@ class UnstableThumbnailResource(RestServlet):
if self._is_mine_server_name(server_name): if self._is_mine_server_name(server_name):
if self.dynamic_thumbnails: if self.dynamic_thumbnails:
await self.thumbnailer.select_or_generate_local_thumbnail( await self.thumbnailer.select_or_generate_local_thumbnail(
request, media_id, width, height, method, m_type, max_timeout_ms request,
media_id,
width,
height,
method,
m_type,
max_timeout_ms,
False,
) )
else: else:
await self.thumbnailer.respond_local_thumbnail( await self.thumbnailer.respond_local_thumbnail(
request, media_id, width, height, method, m_type, max_timeout_ms request,
media_id,
width,
height,
method,
m_type,
max_timeout_ms,
False,
) )
self.media_repo.mark_recently_accessed(None, media_id) self.media_repo.mark_recently_accessed(None, media_id)
else: else:
@@ -191,6 +201,7 @@ class UnstableThumbnailResource(RestServlet):
m_type, m_type,
max_timeout_ms, max_timeout_ms,
ip_address, ip_address,
True,
) )
self.media_repo.mark_recently_accessed(server_name, media_id) self.media_repo.mark_recently_accessed(server_name, media_id)
@@ -260,11 +271,9 @@ class DownloadResource(RestServlet):
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
media_repo = hs.get_media_repository() media_repo = hs.get_media_repository()
if hs.config.media.url_preview_enabled: if hs.config.media.url_preview_enabled:
UnstablePreviewURLServlet(hs, media_repo, media_repo.media_storage).register( PreviewURLServlet(hs, media_repo, media_repo.media_storage).register(
http_server http_server
) )
UnstableMediaConfigResource(hs).register(http_server) MediaConfigResource(hs).register(http_server)
UnstableThumbnailResource(hs, media_repo, media_repo.media_storage).register( ThumbnailResource(hs, media_repo, media_repo.media_storage).register(http_server)
http_server
)
DownloadResource(hs, media_repo).register(http_server) DownloadResource(hs, media_repo).register(http_server)
+18 -11
View File
@@ -32,6 +32,7 @@ from synapse.http.servlet import (
) )
from synapse.http.site import SynapseRequest from synapse.http.site import SynapseRequest
from synapse.push import PusherConfigException from synapse.push import PusherConfigException
from synapse.rest.admin.experimental_features import ExperimentalFeature
from synapse.rest.client._base import client_patterns from synapse.rest.client._base import client_patterns
from synapse.rest.synapse.client.unsubscribe import UnsubscribeResource from synapse.rest.synapse.client.unsubscribe import UnsubscribeResource
from synapse.types import JsonDict from synapse.types import JsonDict
@@ -49,20 +50,22 @@ class PushersRestServlet(RestServlet):
super().__init__() super().__init__()
self.hs = hs self.hs = hs
self.auth = hs.get_auth() self.auth = hs.get_auth()
self._msc3881_enabled = self.hs.config.experimental.msc3881_enabled self._store = hs.get_datastores().main
async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request) requester = await self.auth.get_user_by_req(request)
user = requester.user user_id = requester.user.to_string()
pushers = await self.hs.get_datastores().main.get_pushers_by_user_id( msc3881_enabled = await self._store.is_feature_enabled(
user.to_string() user_id, ExperimentalFeature.MSC3881
) )
pushers = await self.hs.get_datastores().main.get_pushers_by_user_id(user_id)
pusher_dicts = [p.as_dict() for p in pushers] pusher_dicts = [p.as_dict() for p in pushers]
for pusher in pusher_dicts: for pusher in pusher_dicts:
if self._msc3881_enabled: if msc3881_enabled:
pusher["org.matrix.msc3881.enabled"] = pusher["enabled"] pusher["org.matrix.msc3881.enabled"] = pusher["enabled"]
pusher["org.matrix.msc3881.device_id"] = pusher["device_id"] pusher["org.matrix.msc3881.device_id"] = pusher["device_id"]
del pusher["enabled"] del pusher["enabled"]
@@ -80,11 +83,15 @@ class PushersSetRestServlet(RestServlet):
self.auth = hs.get_auth() self.auth = hs.get_auth()
self.notifier = hs.get_notifier() self.notifier = hs.get_notifier()
self.pusher_pool = self.hs.get_pusherpool() self.pusher_pool = self.hs.get_pusherpool()
self._msc3881_enabled = self.hs.config.experimental.msc3881_enabled self._store = hs.get_datastores().main
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]: async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request) requester = await self.auth.get_user_by_req(request)
user = requester.user user_id = requester.user.to_string()
msc3881_enabled = await self._store.is_feature_enabled(
user_id, ExperimentalFeature.MSC3881
)
content = parse_json_object_from_request(request) content = parse_json_object_from_request(request)
@@ -95,7 +102,7 @@ class PushersSetRestServlet(RestServlet):
and content["kind"] is None and content["kind"] is None
): ):
await self.pusher_pool.remove_pusher( await self.pusher_pool.remove_pusher(
content["app_id"], content["pushkey"], user_id=user.to_string() content["app_id"], content["pushkey"], user_id=user_id
) )
return 200, {} return 200, {}
@@ -120,19 +127,19 @@ class PushersSetRestServlet(RestServlet):
append = content["append"] append = content["append"]
enabled = True enabled = True
if self._msc3881_enabled and "org.matrix.msc3881.enabled" in content: if msc3881_enabled and "org.matrix.msc3881.enabled" in content:
enabled = content["org.matrix.msc3881.enabled"] enabled = content["org.matrix.msc3881.enabled"]
if not append: if not append:
await self.pusher_pool.remove_pushers_by_app_id_and_pushkey_not_user( await self.pusher_pool.remove_pushers_by_app_id_and_pushkey_not_user(
app_id=content["app_id"], app_id=content["app_id"],
pushkey=content["pushkey"], pushkey=content["pushkey"],
not_user_id=user.to_string(), not_user_id=user_id,
) )
try: try:
await self.pusher_pool.add_or_update_pusher( await self.pusher_pool.add_or_update_pusher(
user_id=user.to_string(), user_id=user_id,
kind=content["kind"], kind=content["kind"],
app_id=content["app_id"], app_id=content["app_id"],
app_display_name=content["app_display_name"], app_display_name=content["app_display_name"],
+138 -24
View File
@@ -52,8 +52,9 @@ from synapse.http.servlet import (
parse_string, parse_string,
) )
from synapse.http.site import SynapseRequest from synapse.http.site import SynapseRequest
from synapse.logging.opentracing import trace_with_opname from synapse.logging.opentracing import log_kv, set_tag, trace_with_opname
from synapse.types import JsonDict, Requester, StreamToken from synapse.rest.admin.experimental_features import ExperimentalFeature
from synapse.types import JsonDict, Requester, SlidingSyncStreamToken, StreamToken
from synapse.types.rest.client import SlidingSyncBody from synapse.types.rest.client import SlidingSyncBody
from synapse.util import json_decoder from synapse.util import json_decoder
from synapse.util.caches.lrucache import LruCache from synapse.util.caches.lrucache import LruCache
@@ -673,7 +674,9 @@ class SlidingSyncE2eeRestServlet(RestServlet):
) )
async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True) requester = await self.auth.get_user_by_req_experimental_feature(
request, allow_guest=True, feature=ExperimentalFeature.MSC3575
)
user = requester.user user = requester.user
device_id = requester.device_id device_id = requester.device_id
@@ -873,9 +876,11 @@ class SlidingSyncRestServlet(RestServlet):
self.event_serializer = hs.get_event_client_serializer() self.event_serializer = hs.get_event_client_serializer()
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]: async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request, allow_guest=True) requester = await self.auth.get_user_by_req_experimental_feature(
request, allow_guest=True, feature=ExperimentalFeature.MSC3575
)
user = requester.user user = requester.user
device_id = requester.device_id
timeout = parse_integer(request, "timeout", default=0) timeout = parse_integer(request, "timeout", default=0)
# Position in the stream # Position in the stream
@@ -883,22 +888,41 @@ class SlidingSyncRestServlet(RestServlet):
from_token = None from_token = None
if from_token_string is not None: if from_token_string is not None:
from_token = await StreamToken.from_string(self.store, from_token_string) from_token = await SlidingSyncStreamToken.from_string(
self.store, from_token_string
)
# TODO: We currently don't know whether we're going to use sticky params or # TODO: We currently don't know whether we're going to use sticky params or
# maybe some filters like sync v2 where they are built up once and referenced # maybe some filters like sync v2 where they are built up once and referenced
# by filter ID. For now, we will just prototype with always passing everything # by filter ID. For now, we will just prototype with always passing everything
# in. # in.
body = parse_and_validate_json_object_from_request(request, SlidingSyncBody) body = parse_and_validate_json_object_from_request(request, SlidingSyncBody)
logger.info("Sliding sync request: %r", body)
# Tag and log useful data to differentiate requests.
set_tag("sliding_sync.conn_id", body.conn_id or "")
log_kv(
{
"sliding_sync.lists": {
list_name: {
"ranges": list_config.ranges,
"timeline_limit": list_config.timeline_limit,
}
for list_name, list_config in (body.lists or {}).items()
},
"sliding_sync.room_subscriptions": list(
(body.room_subscriptions or {}).keys()
),
}
)
sync_config = SlidingSyncConfig( sync_config = SlidingSyncConfig(
user=user, user=user,
device_id=device_id, requester=requester,
# FIXME: Currently, we're just manually copying the fields from the # FIXME: Currently, we're just manually copying the fields from the
# `SlidingSyncBody` into the config. How can we gurantee into the future # `SlidingSyncBody` into the config. How can we guarantee into the future
# that we don't forget any? I would like something more structured like # that we don't forget any? I would like something more structured like
# `copy_attributes(from=body, to=config)` # `copy_attributes(from=body, to=config)`
conn_id=body.conn_id,
lists=body.lists, lists=body.lists,
room_subscriptions=body.room_subscriptions, room_subscriptions=body.room_subscriptions,
extensions=body.extensions, extensions=body.extensions,
@@ -921,7 +945,6 @@ class SlidingSyncRestServlet(RestServlet):
return 200, response_content return 200, response_content
# TODO: Is there a better way to encode things?
async def encode_response( async def encode_response(
self, self,
requester: Requester, requester: Requester,
@@ -936,7 +959,9 @@ class SlidingSyncRestServlet(RestServlet):
response["rooms"] = await self.encode_rooms( response["rooms"] = await self.encode_rooms(
requester, sliding_sync_result.rooms requester, sliding_sync_result.rooms
) )
response["extensions"] = {} # TODO: sliding_sync_result.extensions response["extensions"] = await self.encode_extensions(
requester, sliding_sync_result.extensions
)
return response return response
@@ -976,6 +1001,7 @@ class SlidingSyncRestServlet(RestServlet):
serialized_rooms: Dict[str, JsonDict] = {} serialized_rooms: Dict[str, JsonDict] = {}
for room_id, room_result in rooms.items(): for room_id, room_result in rooms.items():
serialized_rooms[room_id] = { serialized_rooms[room_id] = {
"bump_stamp": room_result.bump_stamp,
"joined_count": room_result.joined_count, "joined_count": room_result.joined_count,
"invited_count": room_result.invited_count, "invited_count": room_result.invited_count,
"notification_count": room_result.notification_count, "notification_count": room_result.notification_count,
@@ -988,16 +1014,32 @@ class SlidingSyncRestServlet(RestServlet):
if room_result.avatar: if room_result.avatar:
serialized_rooms[room_id]["avatar"] = room_result.avatar serialized_rooms[room_id]["avatar"] = room_result.avatar
if room_result.heroes: if room_result.heroes is not None and len(room_result.heroes) > 0:
serialized_rooms[room_id]["heroes"] = room_result.heroes serialized_heroes = []
for hero in room_result.heroes:
serialized_hero = {
"user_id": hero.user_id,
}
if hero.display_name is not None:
# Not a typo, just how "displayname" is spelled in the spec
serialized_hero["displayname"] = hero.display_name
if hero.avatar_url is not None:
serialized_hero["avatar_url"] = hero.avatar_url
serialized_heroes.append(serialized_hero)
serialized_rooms[room_id]["heroes"] = serialized_heroes
# We should only include the `initial` key if it's `True` to save bandwidth. # We should only include the `initial` key if it's `True` to save bandwidth.
# The absense of this flag means `False`. # The absense of this flag means `False`.
if room_result.initial: if room_result.initial:
serialized_rooms[room_id]["initial"] = room_result.initial serialized_rooms[room_id]["initial"] = room_result.initial
# This will omitted for invite/knock rooms with `stripped_state` # This will be omitted for invite/knock rooms with `stripped_state`
if room_result.required_state is not None: if (
room_result.required_state is not None
and len(room_result.required_state) > 0
):
serialized_required_state = ( serialized_required_state = (
await self.event_serializer.serialize_events( await self.event_serializer.serialize_events(
room_result.required_state, room_result.required_state,
@@ -1007,8 +1049,11 @@ class SlidingSyncRestServlet(RestServlet):
) )
serialized_rooms[room_id]["required_state"] = serialized_required_state serialized_rooms[room_id]["required_state"] = serialized_required_state
# This will omitted for invite/knock rooms with `stripped_state` # This will be omitted for invite/knock rooms with `stripped_state`
if room_result.timeline_events is not None: if (
room_result.timeline_events is not None
and len(room_result.timeline_events) > 0
):
serialized_timeline = await self.event_serializer.serialize_events( serialized_timeline = await self.event_serializer.serialize_events(
room_result.timeline_events, room_result.timeline_events,
time_now, time_now,
@@ -1017,17 +1062,17 @@ class SlidingSyncRestServlet(RestServlet):
) )
serialized_rooms[room_id]["timeline"] = serialized_timeline serialized_rooms[room_id]["timeline"] = serialized_timeline
# This will omitted for invite/knock rooms with `stripped_state` # This will be omitted for invite/knock rooms with `stripped_state`
if room_result.limited is not None: if room_result.limited is not None:
serialized_rooms[room_id]["limited"] = room_result.limited serialized_rooms[room_id]["limited"] = room_result.limited
# This will omitted for invite/knock rooms with `stripped_state` # This will be omitted for invite/knock rooms with `stripped_state`
if room_result.prev_batch is not None: if room_result.prev_batch is not None:
serialized_rooms[room_id]["prev_batch"] = ( serialized_rooms[room_id]["prev_batch"] = (
await room_result.prev_batch.to_string(self.store) await room_result.prev_batch.to_string(self.store)
) )
# This will omitted for invite/knock rooms with `stripped_state` # This will be omitted for invite/knock rooms with `stripped_state`
if room_result.num_live is not None: if room_result.num_live is not None:
serialized_rooms[room_id]["num_live"] = room_result.num_live serialized_rooms[room_id]["num_live"] = room_result.num_live
@@ -1036,7 +1081,10 @@ class SlidingSyncRestServlet(RestServlet):
serialized_rooms[room_id]["is_dm"] = room_result.is_dm serialized_rooms[room_id]["is_dm"] = room_result.is_dm
# Stripped state only applies to invite/knock rooms # Stripped state only applies to invite/knock rooms
if room_result.stripped_state is not None: if (
room_result.stripped_state is not None
and len(room_result.stripped_state) > 0
):
# TODO: `knocked_state` but that isn't specced yet. # TODO: `knocked_state` but that isn't specced yet.
# #
# TODO: Instead of adding `knocked_state`, it would be good to rename # TODO: Instead of adding `knocked_state`, it would be good to rename
@@ -1047,10 +1095,76 @@ class SlidingSyncRestServlet(RestServlet):
return serialized_rooms return serialized_rooms
async def encode_extensions(
self, requester: Requester, extensions: SlidingSyncResult.Extensions
) -> JsonDict:
serialized_extensions: JsonDict = {}
if extensions.to_device is not None:
serialized_extensions["to_device"] = {
"next_batch": extensions.to_device.next_batch,
"events": extensions.to_device.events,
}
if extensions.e2ee is not None:
serialized_extensions["e2ee"] = {
# We always include this because
# https://github.com/vector-im/element-android/issues/3725. The spec
# isn't terribly clear on when this can be omitted and how a client
# would tell the difference between "no keys present" and "nothing
# changed" in terms of whole field absent / individual key type entry
# absent Corresponding synapse issue:
# https://github.com/matrix-org/synapse/issues/10456
"device_one_time_keys_count": extensions.e2ee.device_one_time_keys_count,
# https://github.com/matrix-org/matrix-doc/blob/54255851f642f84a4f1aaf7bc063eebe3d76752b/proposals/2732-olm-fallback-keys.md
# states that this field should always be included, as long as the
# server supports the feature.
"device_unused_fallback_key_types": extensions.e2ee.device_unused_fallback_key_types,
}
if extensions.e2ee.device_list_updates is not None:
serialized_extensions["e2ee"]["device_lists"] = {}
serialized_extensions["e2ee"]["device_lists"]["changed"] = list(
extensions.e2ee.device_list_updates.changed
)
serialized_extensions["e2ee"]["device_lists"]["left"] = list(
extensions.e2ee.device_list_updates.left
)
if extensions.account_data is not None:
serialized_extensions["account_data"] = {
# Same as the the top-level `account_data.events` field in Sync v2.
"global": [
{"type": account_data_type, "content": content}
for account_data_type, content in extensions.account_data.global_account_data_map.items()
],
# Same as the joined room's account_data field in Sync v2, e.g the path
# `rooms.join["!foo:bar"].account_data.events`.
"rooms": {
room_id: [
{"type": account_data_type, "content": content}
for account_data_type, content in event_map.items()
]
for room_id, event_map in extensions.account_data.account_data_by_room_map.items()
},
}
if extensions.receipts is not None:
serialized_extensions["receipts"] = {
"rooms": extensions.receipts.room_id_to_receipt_map,
}
if extensions.typing is not None:
serialized_extensions["typing"] = {
"rooms": extensions.typing.room_id_to_typing_map,
}
return serialized_extensions
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
SyncRestServlet(hs).register(http_server) SyncRestServlet(hs).register(http_server)
if hs.config.experimental.msc3575_enabled: SlidingSyncRestServlet(hs).register(http_server)
SlidingSyncRestServlet(hs).register(http_server) SlidingSyncE2eeRestServlet(hs).register(http_server)
SlidingSyncE2eeRestServlet(hs).register(http_server)
+22 -4
View File
@@ -25,11 +25,11 @@ import logging
import re import re
from typing import TYPE_CHECKING, Tuple from typing import TYPE_CHECKING, Tuple
from twisted.web.server import Request
from synapse.api.constants import RoomCreationPreset from synapse.api.constants import RoomCreationPreset
from synapse.http.server import HttpServer from synapse.http.server import HttpServer
from synapse.http.servlet import RestServlet from synapse.http.servlet import RestServlet
from synapse.http.site import SynapseRequest
from synapse.rest.admin.experimental_features import ExperimentalFeature
from synapse.types import JsonDict from synapse.types import JsonDict
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -45,6 +45,8 @@ class VersionsRestServlet(RestServlet):
def __init__(self, hs: "HomeServer"): def __init__(self, hs: "HomeServer"):
super().__init__() super().__init__()
self.config = hs.config self.config = hs.config
self.auth = hs.get_auth()
self.store = hs.get_datastores().main
# Calculate these once since they shouldn't change after start-up. # Calculate these once since they shouldn't change after start-up.
self.e2ee_forced_public = ( self.e2ee_forced_public = (
@@ -60,7 +62,22 @@ class VersionsRestServlet(RestServlet):
in self.config.room.encryption_enabled_by_default_for_room_presets in self.config.room.encryption_enabled_by_default_for_room_presets
) )
def on_GET(self, request: Request) -> Tuple[int, JsonDict]: async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
msc3881_enabled = self.config.experimental.msc3881_enabled
if self.auth.has_access_token(request):
requester = await self.auth.get_user_by_req(
request,
allow_guest=True,
allow_locked=True,
allow_expired=True,
)
user_id = requester.user.to_string()
msc3881_enabled = await self.store.is_feature_enabled(
user_id, ExperimentalFeature.MSC3881
)
return ( return (
200, 200,
{ {
@@ -90,6 +107,7 @@ class VersionsRestServlet(RestServlet):
"v1.8", "v1.8",
"v1.9", "v1.9",
"v1.10", "v1.10",
"v1.11",
], ],
# as per MSC1497: # as per MSC1497:
"unstable_features": { "unstable_features": {
@@ -124,7 +142,7 @@ class VersionsRestServlet(RestServlet):
# TODO: this is no longer needed once unstable MSC3882 does not need to be supported: # TODO: this is no longer needed once unstable MSC3882 does not need to be supported:
"org.matrix.msc3882": self.config.auth.login_via_existing_enabled, "org.matrix.msc3882": self.config.auth.login_via_existing_enabled,
# Adds support for remotely enabling/disabling pushers, as per MSC3881 # Adds support for remotely enabling/disabling pushers, as per MSC3881
"org.matrix.msc3881": self.config.experimental.msc3881_enabled, "org.matrix.msc3881": msc3881_enabled,
# Adds support for filtering /messages by event relation. # Adds support for filtering /messages by event relation.
"org.matrix.msc3874": self.config.experimental.msc3874_enabled, "org.matrix.msc3874": self.config.experimental.msc3874_enabled,
# Adds support for simple HTTP rendezvous as per MSC3886 # Adds support for simple HTTP rendezvous as per MSC3886
+2 -1
View File
@@ -84,7 +84,7 @@ class DownloadResource(RestServlet):
if self._is_mine_server_name(server_name): if self._is_mine_server_name(server_name):
await self.media_repo.get_local_media( await self.media_repo.get_local_media(
request, media_id, file_name, max_timeout_ms request, media_id, file_name, max_timeout_ms, allow_authenticated=False
) )
else: else:
allow_remote = parse_boolean(request, "allow_remote", default=True) allow_remote = parse_boolean(request, "allow_remote", default=True)
@@ -106,4 +106,5 @@ class DownloadResource(RestServlet):
max_timeout_ms, max_timeout_ms,
ip_address, ip_address,
False, False,
allow_authenticated=False,
) )
+20 -2
View File
@@ -88,11 +88,27 @@ class ThumbnailResource(RestServlet):
if self._is_mine_server_name(server_name): if self._is_mine_server_name(server_name):
if self.dynamic_thumbnails: if self.dynamic_thumbnails:
await self.thumbnail_provider.select_or_generate_local_thumbnail( await self.thumbnail_provider.select_or_generate_local_thumbnail(
request, media_id, width, height, method, m_type, max_timeout_ms request,
media_id,
width,
height,
method,
m_type,
max_timeout_ms,
False,
allow_authenticated=False,
) )
else: else:
await self.thumbnail_provider.respond_local_thumbnail( await self.thumbnail_provider.respond_local_thumbnail(
request, media_id, width, height, method, m_type, max_timeout_ms request,
media_id,
width,
height,
method,
m_type,
max_timeout_ms,
False,
allow_authenticated=False,
) )
self.media_repo.mark_recently_accessed(None, media_id) self.media_repo.mark_recently_accessed(None, media_id)
else: else:
@@ -120,5 +136,7 @@ class ThumbnailResource(RestServlet):
m_type, m_type,
max_timeout_ms, max_timeout_ms,
ip_address, ip_address,
use_federation=False,
allow_authenticated=False,
) )
self.media_repo.mark_recently_accessed(server_name, media_id) self.media_repo.mark_recently_accessed(server_name, media_id)
+10 -5
View File
@@ -28,7 +28,7 @@
import abc import abc
import functools import functools
import logging import logging
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, TypeVar, cast from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Type, TypeVar, cast
from typing_extensions import TypeAlias from typing_extensions import TypeAlias
@@ -161,6 +161,7 @@ if TYPE_CHECKING:
from synapse.handlers.jwt import JwtHandler from synapse.handlers.jwt import JwtHandler
from synapse.handlers.oidc import OidcHandler from synapse.handlers.oidc import OidcHandler
from synapse.handlers.saml import SamlHandler from synapse.handlers.saml import SamlHandler
from synapse.storage._base import SQLBaseStore
# The annotation for `cache_in_self` used to be # The annotation for `cache_in_self` used to be
@@ -255,10 +256,13 @@ class HomeServer(metaclass=abc.ABCMeta):
"stats", "stats",
] ]
# This is overridden in derived application classes @property
# (such as synapse.app.homeserver.SynapseHomeServer) and gives the class to be @abc.abstractmethod
# instantiated during setup() for future return by get_datastores() def DATASTORE_CLASS(self) -> Type["SQLBaseStore"]:
DATASTORE_CLASS = abc.abstractproperty() # This is overridden in derived application classes
# (such as synapse.app.homeserver.SynapseHomeServer) and gives the class to be
# instantiated during setup() for future return by get_datastores()
pass
def __init__( def __init__(
self, self,
@@ -555,6 +559,7 @@ class HomeServer(metaclass=abc.ABCMeta):
def get_sync_handler(self) -> SyncHandler: def get_sync_handler(self) -> SyncHandler:
return SyncHandler(self) return SyncHandler(self)
@cache_in_self
def get_sliding_sync_handler(self) -> SlidingSyncHandler: def get_sliding_sync_handler(self) -> SlidingSyncHandler:
return SlidingSyncHandler(self) return SlidingSyncHandler(self)
+10 -6
View File
@@ -119,14 +119,16 @@ class SQLBaseStore(metaclass=ABCMeta):
self._attempt_to_invalidate_cache( self._attempt_to_invalidate_cache(
"get_user_in_room_with_profile", (room_id, user_id) "get_user_in_room_with_profile", (room_id, user_id)
) )
self._attempt_to_invalidate_cache(
"get_rooms_for_user_with_stream_ordering", (user_id,)
)
self._attempt_to_invalidate_cache("get_rooms_for_user", (user_id,)) self._attempt_to_invalidate_cache("get_rooms_for_user", (user_id,))
self._attempt_to_invalidate_cache(
"_get_rooms_for_local_user_where_membership_is_inner", (user_id,)
)
# Purge other caches based on room state. # Purge other caches based on room state.
self._attempt_to_invalidate_cache("get_room_summary", (room_id,)) self._attempt_to_invalidate_cache("get_room_summary", (room_id,))
self._attempt_to_invalidate_cache("get_partial_current_state_ids", (room_id,)) self._attempt_to_invalidate_cache("get_partial_current_state_ids", (room_id,))
self._attempt_to_invalidate_cache("get_room_type", (room_id,))
self._attempt_to_invalidate_cache("get_room_encryption", (room_id,))
def _invalidate_state_caches_all(self, room_id: str) -> None: def _invalidate_state_caches_all(self, room_id: str) -> None:
"""Invalidates caches that are based on the current state, but does """Invalidates caches that are based on the current state, but does
@@ -148,11 +150,13 @@ class SQLBaseStore(metaclass=ABCMeta):
self._attempt_to_invalidate_cache("get_local_users_in_room", (room_id,)) self._attempt_to_invalidate_cache("get_local_users_in_room", (room_id,))
self._attempt_to_invalidate_cache("does_pair_of_users_share_a_room", None) self._attempt_to_invalidate_cache("does_pair_of_users_share_a_room", None)
self._attempt_to_invalidate_cache("get_user_in_room_with_profile", None) self._attempt_to_invalidate_cache("get_user_in_room_with_profile", None)
self._attempt_to_invalidate_cache(
"get_rooms_for_user_with_stream_ordering", None
)
self._attempt_to_invalidate_cache("get_rooms_for_user", None) self._attempt_to_invalidate_cache("get_rooms_for_user", None)
self._attempt_to_invalidate_cache(
"_get_rooms_for_local_user_where_membership_is_inner", None
)
self._attempt_to_invalidate_cache("get_room_summary", (room_id,)) self._attempt_to_invalidate_cache("get_room_summary", (room_id,))
self._attempt_to_invalidate_cache("get_room_type", (room_id,))
self._attempt_to_invalidate_cache("get_room_encryption", (room_id,))
def _attempt_to_invalidate_cache( def _attempt_to_invalidate_cache(
self, cache_name: str, key: Optional[Collection[Any]] self, cache_name: str, key: Optional[Collection[Any]]
+25 -1
View File
@@ -409,7 +409,7 @@ class StateStorageController:
return state_ids return state_ids
async def get_state_at( async def get_state_ids_at(
self, self,
room_id: str, room_id: str,
stream_position: StreamToken, stream_position: StreamToken,
@@ -460,6 +460,30 @@ class StateStorageController:
) )
return state return state
@trace
@tag_args
async def get_state_at(
self,
room_id: str,
stream_position: StreamToken,
state_filter: Optional[StateFilter] = None,
await_full_state: bool = True,
) -> StateMap[EventBase]:
"""Same as `get_state_ids_at` but also fetches the events"""
state_map_ids = await self.get_state_ids_at(
room_id, stream_position, state_filter, await_full_state
)
event_map = await self.stores.main.get_events(list(state_map_ids.values()))
state_map = {}
for key, event_id in state_map_ids.items():
event = event_map.get(event_id)
if event:
state_map[key] = event
return state_map
@trace @trace
@tag_args @tag_args
async def get_state_for_groups( async def get_state_for_groups(
+25 -11
View File
@@ -268,17 +268,23 @@ class CacheInvalidationWorkerStore(SQLBaseStore):
self._curr_state_delta_stream_cache.entity_has_changed(data.room_id, token) # type: ignore[attr-defined] self._curr_state_delta_stream_cache.entity_has_changed(data.room_id, token) # type: ignore[attr-defined]
if data.type == EventTypes.Member: if data.type == EventTypes.Member:
self.get_rooms_for_user_with_stream_ordering.invalidate( # type: ignore[attr-defined] self._attempt_to_invalidate_cache(
(data.state_key,) "get_rooms_for_user", (data.state_key,)
) )
self.get_rooms_for_user.invalidate((data.state_key,)) # type: ignore[attr-defined] elif data.type == EventTypes.RoomEncryption:
self._attempt_to_invalidate_cache(
"get_room_encryption", (data.room_id,)
)
elif data.type == EventTypes.Create:
self._attempt_to_invalidate_cache("get_room_type", (data.room_id,))
elif row.type == EventsStreamAllStateRow.TypeId: elif row.type == EventsStreamAllStateRow.TypeId:
assert isinstance(data, EventsStreamAllStateRow) assert isinstance(data, EventsStreamAllStateRow)
# Similar to the above, but the entire caches are invalidated. This is # Similar to the above, but the entire caches are invalidated. This is
# unfortunate for the membership caches, but should recover quickly. # unfortunate for the membership caches, but should recover quickly.
self._curr_state_delta_stream_cache.entity_has_changed(data.room_id, token) # type: ignore[attr-defined] self._curr_state_delta_stream_cache.entity_has_changed(data.room_id, token) # type: ignore[attr-defined]
self.get_rooms_for_user_with_stream_ordering.invalidate_all() # type: ignore[attr-defined] self._attempt_to_invalidate_cache("get_rooms_for_user", None)
self.get_rooms_for_user.invalidate_all() # type: ignore[attr-defined] self._attempt_to_invalidate_cache("get_room_type", (data.room_id,))
self._attempt_to_invalidate_cache("get_room_encryption", (data.room_id,))
else: else:
raise Exception("Unknown events stream row type %s" % (row.type,)) raise Exception("Unknown events stream row type %s" % (row.type,))
@@ -334,10 +340,10 @@ class CacheInvalidationWorkerStore(SQLBaseStore):
self._attempt_to_invalidate_cache( self._attempt_to_invalidate_cache(
"get_invited_rooms_for_local_user", (state_key,) "get_invited_rooms_for_local_user", (state_key,)
) )
self._attempt_to_invalidate_cache(
"get_rooms_for_user_with_stream_ordering", (state_key,)
)
self._attempt_to_invalidate_cache("get_rooms_for_user", (state_key,)) self._attempt_to_invalidate_cache("get_rooms_for_user", (state_key,))
self._attempt_to_invalidate_cache(
"_get_rooms_for_local_user_where_membership_is_inner", (state_key,)
)
self._attempt_to_invalidate_cache( self._attempt_to_invalidate_cache(
"did_forget", "did_forget",
@@ -349,6 +355,10 @@ class CacheInvalidationWorkerStore(SQLBaseStore):
self._attempt_to_invalidate_cache( self._attempt_to_invalidate_cache(
"get_forgotten_rooms_for_user", (state_key,) "get_forgotten_rooms_for_user", (state_key,)
) )
elif etype == EventTypes.Create:
self._attempt_to_invalidate_cache("get_room_type", (room_id,))
elif etype == EventTypes.RoomEncryption:
self._attempt_to_invalidate_cache("get_room_encryption", (room_id,))
if relates_to: if relates_to:
self._attempt_to_invalidate_cache( self._attempt_to_invalidate_cache(
@@ -399,16 +409,18 @@ class CacheInvalidationWorkerStore(SQLBaseStore):
self._attempt_to_invalidate_cache("get_thread_id", None) self._attempt_to_invalidate_cache("get_thread_id", None)
self._attempt_to_invalidate_cache("get_thread_id_for_receipts", None) self._attempt_to_invalidate_cache("get_thread_id_for_receipts", None)
self._attempt_to_invalidate_cache("get_invited_rooms_for_local_user", None) self._attempt_to_invalidate_cache("get_invited_rooms_for_local_user", None)
self._attempt_to_invalidate_cache(
"get_rooms_for_user_with_stream_ordering", None
)
self._attempt_to_invalidate_cache("get_rooms_for_user", None) self._attempt_to_invalidate_cache("get_rooms_for_user", None)
self._attempt_to_invalidate_cache(
"_get_rooms_for_local_user_where_membership_is_inner", None
)
self._attempt_to_invalidate_cache("did_forget", None) self._attempt_to_invalidate_cache("did_forget", None)
self._attempt_to_invalidate_cache("get_forgotten_rooms_for_user", None) self._attempt_to_invalidate_cache("get_forgotten_rooms_for_user", None)
self._attempt_to_invalidate_cache("get_references_for_event", None) self._attempt_to_invalidate_cache("get_references_for_event", None)
self._attempt_to_invalidate_cache("get_thread_summary", None) self._attempt_to_invalidate_cache("get_thread_summary", None)
self._attempt_to_invalidate_cache("get_thread_participated", None) self._attempt_to_invalidate_cache("get_thread_participated", None)
self._attempt_to_invalidate_cache("get_threads", (room_id,)) self._attempt_to_invalidate_cache("get_threads", (room_id,))
self._attempt_to_invalidate_cache("get_room_type", (room_id,))
self._attempt_to_invalidate_cache("get_room_encryption", (room_id,))
self._attempt_to_invalidate_cache("_get_state_group_for_event", None) self._attempt_to_invalidate_cache("_get_state_group_for_event", None)
@@ -461,6 +473,8 @@ class CacheInvalidationWorkerStore(SQLBaseStore):
self._attempt_to_invalidate_cache("get_forgotten_rooms_for_user", None) self._attempt_to_invalidate_cache("get_forgotten_rooms_for_user", None)
self._attempt_to_invalidate_cache("_get_membership_from_event_id", None) self._attempt_to_invalidate_cache("_get_membership_from_event_id", None)
self._attempt_to_invalidate_cache("get_room_version_id", (room_id,)) self._attempt_to_invalidate_cache("get_room_version_id", (room_id,))
self._attempt_to_invalidate_cache("get_room_type", (room_id,))
self._attempt_to_invalidate_cache("get_room_encryption", (room_id,))
# And delete state caches. # And delete state caches.

Some files were not shown because too many files have changed in this diff Show More