mirror of
https://mau.dev/maunium/synapse.git
synced 2024-06-28 23:42:18 +00:00
Compare commits
308 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ef43d4495a | ||
![]() |
a8dcd686fb | ||
![]() |
315b8d2032 | ||
![]() |
9f47513458 | ||
![]() |
ef7fbdfebd | ||
![]() |
9cf0ef9c70 | ||
![]() |
a023538822 | ||
![]() |
f79dbd0f61 | ||
![]() |
c89fea3fd1 | ||
![]() |
554a92601a | ||
![]() |
a98cb87bee | ||
![]() |
6e8af83193 | ||
![]() |
805e6c9a8f | ||
![]() |
3c61ddbbc9 | ||
![]() |
ae4c236a6d | ||
![]() |
930a64b6c1 | ||
![]() |
7a11c0ac4f | ||
![]() |
cf711ac03c | ||
![]() |
700d2cc4a0 | ||
![]() |
1e74b50dc6 | ||
![]() |
7c2d8f1f01 | ||
![]() |
118b734081 | ||
![]() |
7a6186b888 | ||
![]() |
452a59f887 | ||
![]() |
adeedb7b7c | ||
![]() |
7c5fb13f7b | ||
![]() |
f8d57ce656 | ||
![]() |
13ed84c573 | ||
![]() |
4243c1f074 | ||
![]() |
3239b7459c | ||
![]() |
c99203d98c | ||
![]() |
9104a9f0d0 | ||
![]() |
a412a5829d | ||
![]() |
7ef89b985d | ||
![]() |
bdf82efea5 | ||
![]() |
afaf2d9388 | ||
![]() |
199223062a | ||
![]() |
97c3d98816 | ||
![]() |
fa3adc896a | ||
![]() |
147069d632 | ||
![]() |
79767a1108 | ||
![]() |
4af654f0da | ||
![]() |
1c7d85fdfe | ||
![]() |
5a65e8a0d1 | ||
![]() |
088992a484 | ||
![]() |
d17d931a53 | ||
![]() |
334123f0cd | ||
![]() |
d8e81f67eb | ||
![]() |
19a3d5b606 | ||
![]() |
52813a8d94 | ||
![]() |
a5485437cf | ||
![]() |
e5b8a3e37f | ||
![]() |
e88332b5f4 | ||
![]() |
7ce30d6375 | ||
![]() |
edfb7aad3a | ||
![]() |
f983a77ab0 | ||
![]() |
12d7303707 | ||
![]() |
a3cb244755 | ||
![]() |
3aae60f17b | ||
![]() |
2c36a679ae | ||
![]() |
c12ee0d5ba | ||
![]() |
8aaff851b1 | ||
![]() |
8c58eb7f17 | ||
![]() |
ebdce69f6a | ||
![]() |
c6eb99c878 | ||
![]() |
5db3eec5bc | ||
![]() |
f1c4dfb08b | ||
![]() |
0edf1cacf7 | ||
![]() |
d0f90bd04e | ||
![]() |
d21cd494ca | ||
![]() |
0248ed70a9 | ||
![]() |
e6816babf6 | ||
![]() |
a8069e9739 | ||
![]() |
863578bfcf | ||
![]() |
9e59d18022 | ||
![]() |
491365f199 | ||
![]() |
dad1559721 | ||
![]() |
8c4937b216 | ||
![]() |
b84e31375b | ||
![]() |
06953bc193 | ||
![]() |
265ee88f34 | ||
![]() |
ab94bce02c | ||
![]() |
17d6c28285 | ||
![]() |
4a7c58642c | ||
![]() |
ce9385819b | ||
![]() |
a963f579de | ||
![]() |
3f06bbc0ac | ||
![]() |
fcbc79bb87 | ||
![]() |
aabf577166 | ||
![]() |
896b1ea2ba | ||
![]() |
7d8f0ef351 | ||
![]() |
eab0b548e4 | ||
![]() |
81cef38d4b | ||
![]() |
e2f8476044 | ||
![]() |
18c1196893 | ||
![]() |
8a3270075b | ||
![]() |
f458dff16d | ||
![]() |
6b709c512d | ||
![]() |
5c2a837e3c | ||
![]() |
966a886b36 | ||
![]() |
535e97da7b | ||
![]() |
64f5a4a353 | ||
![]() |
7dd14fadb1 | ||
![]() |
5624c8b961 | ||
![]() |
4e3868dc46 | ||
![]() |
d16910ca02 | ||
![]() |
225f378ffa | ||
![]() |
8bd9ff0783 | ||
![]() |
466f344547 | ||
![]() |
726006cdf2 | ||
![]() |
967b6948b0 | ||
![]() |
d7198dfb95 | ||
![]() |
94ef2f4f5d | ||
![]() |
bb5a692946 | ||
![]() |
ad179b0136 | ||
![]() |
5147ce294a | ||
![]() |
acfc71a2de | ||
![]() |
f35bc08d39 | ||
![]() |
f2616edb73 | ||
![]() |
86a2a0258f | ||
![]() |
0893ee9af8 | ||
![]() |
887f773472 | ||
![]() |
9edb725ebc | ||
![]() |
c97251d5ba | ||
![]() |
7e2412265d | ||
![]() |
7ef00b7628 | ||
![]() |
b71d277438 | ||
![]() |
a547b49773 | ||
![]() |
6a9a641fb8 | ||
![]() |
b5facbac0f | ||
![]() |
b250ca5df2 | ||
![]() |
048b912ac1 | ||
![]() |
e0d420fbd1 | ||
![]() |
9956f35c6a | ||
![]() |
d464ee3602 | ||
![]() |
439a095edc | ||
![]() |
5d040f2066 | ||
![]() |
f33266232e | ||
![]() |
d43042864a | ||
![]() |
f4ce030608 | ||
![]() |
8b43cc89fa | ||
![]() |
52af16c561 | ||
![]() |
38f03a09ff | ||
![]() |
c856ae4724 | ||
![]() |
fe07995e69 | ||
![]() |
52a649580f | ||
![]() |
28a948f04f | ||
![]() |
7cb3f8a979 | ||
![]() |
fd12003441 | ||
![]() |
5e892671a7 | ||
![]() |
d2d48cce85 | ||
![]() |
2359c64dec | ||
![]() |
68dca8076f | ||
![]() |
284d85dee3 | ||
![]() |
fee7ccd51a | ||
![]() |
ebe77381b0 | ||
![]() |
0b91ccce47 | ||
![]() |
ecf4e0674c | ||
![]() |
7d82987b27 | ||
![]() |
bd8d8865fb | ||
![]() |
caf528477e | ||
![]() |
f0c72d8e87 | ||
![]() |
03a342b049 | ||
![]() |
aa6345cb3b | ||
![]() |
2b438df9b3 | ||
![]() |
038b9ec59a | ||
![]() |
59ac541310 | ||
![]() |
a2e6f43f11 | ||
![]() |
6f07fc4e00 | ||
![]() |
4cf4a8281b | ||
![]() |
ef7e040e54 | ||
![]() |
393429d692 | ||
![]() |
34a8652366 | ||
![]() |
414ddcd457 | ||
![]() |
4d408cb4dd | ||
![]() |
212f150208 | ||
![]() |
4c6e78fa14 | ||
![]() |
1b155362ca | ||
![]() |
522a40c4de | ||
![]() |
dcd03d3b15 | ||
![]() |
438bc23560 | ||
![]() |
cf30cfe5d1 | ||
![]() |
1726b49457 | ||
![]() |
792cfe7ba6 | ||
![]() |
c3682ff668 | ||
![]() |
3e6ee8ff88 | ||
![]() |
7c9ac01eb5 | ||
![]() |
3818597751 | ||
![]() |
3aadf43122 | ||
![]() |
5b6a75935e | ||
![]() |
c0ea2bf800 | ||
![]() |
37558d5e4c | ||
![]() |
0b358f8643 | ||
![]() |
7254015665 | ||
![]() |
1e10f437cf | ||
![]() |
e84a493f41 | ||
![]() |
07232e27a8 | ||
![]() |
e26673fe97 | ||
![]() |
7ab0f630da | ||
![]() |
b548f7803a | ||
![]() |
758aec6b34 | ||
![]() |
c897ac63e9 | ||
![]() |
38bc7a009d | ||
![]() |
6a275828c8 | ||
![]() |
6e373468a4 | ||
![]() |
48ee17dc79 | ||
![]() |
f6437ca1c4 | ||
![]() |
02bda250f8 | ||
![]() |
0fd6b269d3 | ||
![]() |
ef1db42843 | ||
![]() |
89fc579329 | ||
![]() |
9c91873922 | ||
![]() |
41fbe387d6 | ||
![]() |
90cc9e5b29 | ||
![]() |
516fd891ee | ||
![]() |
0ef2315a99 | ||
![]() |
59710437e4 | ||
![]() |
9985aa6821 | ||
![]() |
31742149d4 | ||
![]() |
947e8a6cb0 | ||
![]() |
0d4d00a07c | ||
![]() |
3166445514 | ||
![]() |
922656fc77 | ||
![]() |
30c50e0240 | ||
![]() |
48a90c697b | ||
![]() |
47773232b0 | ||
![]() |
2e92b718d5 | ||
![]() |
646cb6ff24 | ||
![]() |
f85f2a0455 | ||
![]() |
0fe9e1f7da | ||
![]() |
ae181233aa | ||
![]() |
20c9e19519 | ||
![]() |
55b0aa847a | ||
![]() |
074ef4d75f | ||
![]() |
301c9771c4 | ||
![]() |
800a5b6ef3 | ||
![]() |
8c667759ad | ||
![]() |
14e9ab19be | ||
![]() |
20c8991a94 | ||
![]() |
dcae2b4ba4 | ||
![]() |
98f57ea3f2 | ||
![]() |
f5b6005559 | ||
![]() |
47f3870894 | ||
![]() |
6d64f1b2b8 | ||
![]() |
1d47532310 | ||
![]() |
09f0957b36 | ||
![]() |
803f05f60c | ||
![]() |
c8e0bed426 | ||
![]() |
28f5ad07d3 | ||
![]() |
f0d6f14047 | ||
![]() |
3a196b3227 | ||
![]() |
4d5f585dee | ||
![]() |
fbb2573525 | ||
![]() |
259442fa4c | ||
![]() |
fe4719a268 | ||
![]() |
3a30846bd0 | ||
![]() |
15947bbd71 | ||
![]() |
db4e321219 | ||
![]() |
657b8cc75c | ||
![]() |
a2a543fd12 | ||
![]() |
89f1092284 | ||
![]() |
4ffed6330f | ||
![]() |
e363881592 | ||
![]() |
d40878451c | ||
![]() |
892cbd0624 | ||
![]() |
106cfd4b39 | ||
![]() |
0a6ae6fe4c | ||
![]() |
13a3987929 | ||
![]() |
680f60102b | ||
![]() |
3e51b370c5 | ||
![]() |
9b8597e431 | ||
![]() |
4d10a8fb18 | ||
![]() |
1f8f991d51 | ||
![]() |
5360baeb64 | ||
![]() |
0e68e9b7f4 | ||
![]() |
230b709d9d | ||
![]() |
05957ac70f | ||
![]() |
31122b71bc | ||
![]() |
698ceabe2a | ||
![]() |
51776745b9 | ||
![]() |
ec174d0470 | ||
![]() |
fd48fc4585 | ||
![]() |
ea6bfae0fc | ||
![]() |
67b2fad49e | ||
![]() |
f2f54cb6af | ||
![]() |
2ba175485f | ||
![]() |
14c2066db6 | ||
![]() |
15d050f5f4 | ||
![]() |
aef880992a | ||
![]() |
1cf18958a4 | ||
![]() |
3a8e8c750c | ||
![]() |
3568fb0874 | ||
![]() |
9354d32fc9 | ||
![]() |
0d0f138bbf | ||
![]() |
0f5e09524d | ||
![]() |
1b784b06d4 | ||
![]() |
f4f711f28b | ||
![]() |
de89885d15 | ||
![]() |
3108b67232 | ||
![]() |
b07561405c | ||
![]() |
9eb9372eb4 | ||
![]() |
ab635c80a7 | ||
![]() |
5e7ff45534 | ||
![]() |
0de822af4d | ||
![]() |
83f9a6cdd5 | ||
![]() |
78584d476c | ||
![]() |
ce38046124 | ||
![]() |
e95889bab3 |
|
@ -8,6 +8,7 @@
|
||||||
!README.rst
|
!README.rst
|
||||||
!pyproject.toml
|
!pyproject.toml
|
||||||
!poetry.lock
|
!poetry.lock
|
||||||
|
!requirements.txt
|
||||||
!Cargo.lock
|
!Cargo.lock
|
||||||
!Cargo.toml
|
!Cargo.toml
|
||||||
!build_rust.py
|
!build_rust.py
|
||||||
|
|
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
|
@ -2,4 +2,4 @@
|
||||||
(using a matrix.org account if necessary). We do not use GitHub issues for
|
(using a matrix.org account if necessary). We do not use GitHub issues for
|
||||||
support.
|
support.
|
||||||
|
|
||||||
**If you want to report a security issue** please see https://matrix.org/security-disclosure-policy/
|
**If you want to report a security issue** please see https://element.io/security/security-disclosure-policy
|
||||||
|
|
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
|
@ -7,7 +7,7 @@ body:
|
||||||
**THIS IS NOT A SUPPORT CHANNEL!**
|
**THIS IS NOT A SUPPORT CHANNEL!**
|
||||||
**IF YOU HAVE SUPPORT QUESTIONS ABOUT RUNNING OR CONFIGURING YOUR OWN HOME SERVER**, please ask in **[#synapse:matrix.org](https://matrix.to/#/#synapse:matrix.org)** (using a matrix.org account if necessary).
|
**IF YOU HAVE SUPPORT QUESTIONS ABOUT RUNNING OR CONFIGURING YOUR OWN HOME SERVER**, please ask in **[#synapse:matrix.org](https://matrix.to/#/#synapse:matrix.org)** (using a matrix.org account if necessary).
|
||||||
|
|
||||||
If you want to report a security issue, please see https://matrix.org/security-disclosure-policy/
|
If you want to report a security issue, please see https://element.io/security/security-disclosure-policy
|
||||||
|
|
||||||
This is a bug report form. By following the instructions below and completing the sections with your information, you will help the us to get all the necessary data to fix your issue.
|
This is a bug report form. By following the instructions below and completing the sections with your information, you will help the us to get all the necessary data to fix your issue.
|
||||||
|
|
||||||
|
|
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
||||||
run: docker buildx inspect
|
run: docker buildx inspect
|
||||||
|
|
||||||
- name: Install Cosign
|
- name: Install Cosign
|
||||||
uses: sigstore/cosign-installer@v3.4.0
|
uses: sigstore/cosign-installer@v3.5.0
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
@ -72,7 +72,7 @@ jobs:
|
||||||
|
|
||||||
- name: Build and push all platforms
|
- name: Build and push all platforms
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
labels: |
|
labels: |
|
||||||
|
|
2
.github/workflows/docs-pr-netlify.yaml
vendored
2
.github/workflows/docs-pr-netlify.yaml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
||||||
# There's a 'download artifact' action, but it hasn't been updated for the workflow_run action
|
# There's a 'download artifact' action, but it hasn't been updated for the workflow_run action
|
||||||
# (https://github.com/actions/download-artifact/issues/60) so instead we get this mess:
|
# (https://github.com/actions/download-artifact/issues/60) so instead we get this mess:
|
||||||
- name: 📥 Download artifact
|
- name: 📥 Download artifact
|
||||||
uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
|
uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 # v6
|
||||||
with:
|
with:
|
||||||
workflow: docs-pr.yaml
|
workflow: docs-pr.yaml
|
||||||
run_id: ${{ github.event.workflow_run.id }}
|
run_id: ${{ github.event.workflow_run.id }}
|
||||||
|
|
4
.github/workflows/docs-pr.yaml
vendored
4
.github/workflows/docs-pr.yaml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup mdbook
|
- name: Setup mdbook
|
||||||
uses: peaceiris/actions-mdbook@adeb05db28a0c0004681db83893d56c0388ea9ea # v1.2.0
|
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2.0.0
|
||||||
with:
|
with:
|
||||||
mdbook-version: '0.4.17'
|
mdbook-version: '0.4.17'
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup mdbook
|
- name: Setup mdbook
|
||||||
uses: peaceiris/actions-mdbook@adeb05db28a0c0004681db83893d56c0388ea9ea # v1.2.0
|
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2.0.0
|
||||||
with:
|
with:
|
||||||
mdbook-version: '0.4.17'
|
mdbook-version: '0.4.17'
|
||||||
|
|
||||||
|
|
34
.github/workflows/docs.yaml
vendored
34
.github/workflows/docs.yaml
vendored
|
@ -56,7 +56,7 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup mdbook
|
- name: Setup mdbook
|
||||||
uses: peaceiris/actions-mdbook@adeb05db28a0c0004681db83893d56c0388ea9ea # v1.2.0
|
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2.0.0
|
||||||
with:
|
with:
|
||||||
mdbook-version: '0.4.17'
|
mdbook-version: '0.4.17'
|
||||||
|
|
||||||
|
@ -80,38 +80,8 @@ jobs:
|
||||||
|
|
||||||
# Deploy to the target directory.
|
# Deploy to the target directory.
|
||||||
- name: Deploy to gh pages
|
- name: Deploy to gh pages
|
||||||
uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3.9.3
|
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: ./book
|
publish_dir: ./book
|
||||||
destination_dir: ./${{ needs.pre.outputs.branch-version }}
|
destination_dir: ./${{ needs.pre.outputs.branch-version }}
|
||||||
|
|
||||||
################################################################################
|
|
||||||
pages-devdocs:
|
|
||||||
name: GitHub Pages (developer docs)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- pre
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: "Set up Sphinx"
|
|
||||||
uses: matrix-org/setup-python-poetry@v1
|
|
||||||
with:
|
|
||||||
python-version: "3.x"
|
|
||||||
poetry-version: "1.3.2"
|
|
||||||
groups: "dev-docs"
|
|
||||||
extras: ""
|
|
||||||
|
|
||||||
- name: Build the documentation
|
|
||||||
run: |
|
|
||||||
cd dev-docs
|
|
||||||
poetry run make html
|
|
||||||
|
|
||||||
# Deploy to the target directory.
|
|
||||||
- name: Deploy to gh pages
|
|
||||||
uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3.9.3
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
publish_dir: ./dev-docs/_build/html
|
|
||||||
destination_dir: ./dev-docs/${{ needs.pre.outputs.branch-version }}
|
|
||||||
|
|
8
.github/workflows/release-artifacts.yml
vendored
8
.github/workflows/release-artifacts.yml
vendored
|
@ -102,7 +102,7 @@ jobs:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-20.04, macos-11]
|
os: [ubuntu-20.04, macos-12]
|
||||||
arch: [x86_64, aarch64]
|
arch: [x86_64, aarch64]
|
||||||
# is_pr is a flag used to exclude certain jobs from the matrix on PRs.
|
# is_pr is a flag used to exclude certain jobs from the matrix on PRs.
|
||||||
# It is not read by the rest of the workflow.
|
# It is not read by the rest of the workflow.
|
||||||
|
@ -112,9 +112,9 @@ jobs:
|
||||||
exclude:
|
exclude:
|
||||||
# Don't build macos wheels on PR CI.
|
# Don't build macos wheels on PR CI.
|
||||||
- is_pr: true
|
- is_pr: true
|
||||||
os: "macos-11"
|
os: "macos-12"
|
||||||
# Don't build aarch64 wheels on mac.
|
# Don't build aarch64 wheels on mac.
|
||||||
- os: "macos-11"
|
- os: "macos-12"
|
||||||
arch: aarch64
|
arch: aarch64
|
||||||
# Don't build aarch64 wheels on PR CI.
|
# Don't build aarch64 wheels on PR CI.
|
||||||
- is_pr: true
|
- is_pr: true
|
||||||
|
@ -130,7 +130,7 @@ jobs:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
- name: Install cibuildwheel
|
- name: Install cibuildwheel
|
||||||
run: python -m pip install cibuildwheel==2.16.2
|
run: python -m pip install cibuildwheel==2.19.1
|
||||||
|
|
||||||
- name: Set up QEMU to emulate aarch64
|
- name: Set up QEMU to emulate aarch64
|
||||||
if: matrix.arch == 'aarch64'
|
if: matrix.arch == 'aarch64'
|
||||||
|
|
21
.github/workflows/tests.yml
vendored
21
.github/workflows/tests.yml
vendored
|
@ -81,7 +81,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.65.0
|
uses: dtolnay/rust-toolchain@1.66.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@v1
|
||||||
with:
|
with:
|
||||||
|
@ -148,7 +148,7 @@ jobs:
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.65.0
|
uses: dtolnay/rust-toolchain@1.66.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
- name: Setup Poetry
|
- name: Setup Poetry
|
||||||
|
@ -208,7 +208,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.65.0
|
uses: dtolnay/rust-toolchain@1.66.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@v1
|
||||||
with:
|
with:
|
||||||
|
@ -225,7 +225,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.65.0
|
uses: dtolnay/rust-toolchain@1.66.0
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
@ -344,7 +344,7 @@ jobs:
|
||||||
postgres:${{ matrix.job.postgres-version }}
|
postgres:${{ matrix.job.postgres-version }}
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.65.0
|
uses: dtolnay/rust-toolchain@1.66.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@v1
|
||||||
|
@ -386,7 +386,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.65.0
|
uses: dtolnay/rust-toolchain@1.66.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
# There aren't wheels for some of the older deps, so we need to install
|
# There aren't wheels for some of the older deps, so we need to install
|
||||||
|
@ -479,6 +479,9 @@ jobs:
|
||||||
volumes:
|
volumes:
|
||||||
- ${{ github.workspace }}:/src
|
- ${{ github.workspace }}:/src
|
||||||
env:
|
env:
|
||||||
|
# If this is a pull request to a release branch, use that branch as default branch for sytest, else use develop
|
||||||
|
# This works because the release script always create a branch on the sytest repo with the same name as the release branch
|
||||||
|
SYTEST_DEFAULT_BRANCH: ${{ startsWith(github.base_ref, 'release-') && github.base_ref || 'develop' }}
|
||||||
SYTEST_BRANCH: ${{ github.head_ref }}
|
SYTEST_BRANCH: ${{ github.head_ref }}
|
||||||
POSTGRES: ${{ matrix.job.postgres && 1}}
|
POSTGRES: ${{ matrix.job.postgres && 1}}
|
||||||
MULTI_POSTGRES: ${{ (matrix.job.postgres == 'multi-postgres') || '' }}
|
MULTI_POSTGRES: ${{ (matrix.job.postgres == 'multi-postgres') || '' }}
|
||||||
|
@ -498,7 +501,7 @@ jobs:
|
||||||
run: cat sytest-blacklist .ci/worker-blacklist > synapse-blacklist-with-workers
|
run: cat sytest-blacklist .ci/worker-blacklist > synapse-blacklist-with-workers
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.65.0
|
uses: dtolnay/rust-toolchain@1.66.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
- name: Run SyTest
|
- name: Run SyTest
|
||||||
|
@ -642,7 +645,7 @@ jobs:
|
||||||
path: synapse
|
path: synapse
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.65.0
|
uses: dtolnay/rust-toolchain@1.66.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
- name: Prepare Complement's Prerequisites
|
- name: Prepare Complement's Prerequisites
|
||||||
|
@ -674,7 +677,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.65.0
|
uses: dtolnay/rust-toolchain@1.66.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
- run: cargo test
|
- run: cargo test
|
||||||
|
|
19
.gitlab-ci.yml
Normal file
19
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
image: docker:stable
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
|
||||||
|
build amd64:
|
||||||
|
stage: build
|
||||||
|
tags:
|
||||||
|
- amd64
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
before_script:
|
||||||
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
script:
|
||||||
|
- synversion=$(cat pyproject.toml | grep '^version =' | sed -E 's/^version = "(.+)"$/\1/')
|
||||||
|
- docker build --tag $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:$synversion .
|
||||||
|
- docker push $CI_REGISTRY_IMAGE:latest
|
||||||
|
- docker push $CI_REGISTRY_IMAGE:$synversion
|
||||||
|
- docker rmi $CI_REGISTRY_IMAGE:latest $CI_REGISTRY_IMAGE:$synversion
|
388
CHANGES.md
388
CHANGES.md
|
@ -1,3 +1,391 @@
|
||||||
|
# Synapse 1.110.0rc1 (2024-06-26)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add initial implementation of an experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17187](https://github.com/element-hq/synapse/issues/17187))
|
||||||
|
- Add experimental support for [MSC3823](https://github.com/matrix-org/matrix-spec-proposals/pull/3823) - Account suspension. ([\#17255](https://github.com/element-hq/synapse/issues/17255))
|
||||||
|
- Improve ratelimiting in Synapse. ([\#17256](https://github.com/element-hq/synapse/issues/17256))
|
||||||
|
- Add support for the unstable [MSC4151](https://github.com/matrix-org/matrix-spec-proposals/pull/4151) report room API. ([\#17270](https://github.com/element-hq/synapse/issues/17270), [\#17296](https://github.com/element-hq/synapse/issues/17296))
|
||||||
|
- Filter for public and empty rooms added to Admin-API [List Room API](https://element-hq.github.io/synapse/latest/admin_api/rooms.html#list-room-api). ([\#17276](https://github.com/element-hq/synapse/issues/17276))
|
||||||
|
- Add `is_dm` filtering to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17277](https://github.com/element-hq/synapse/issues/17277))
|
||||||
|
- Add `is_encrypted` filtering to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17281](https://github.com/element-hq/synapse/issues/17281))
|
||||||
|
- Include user membership in events served to clients, per [MSC4115](https://github.com/matrix-org/matrix-spec-proposals/pull/4115). ([\#17282](https://github.com/element-hq/synapse/issues/17282))
|
||||||
|
- Do not require user-interactive authentication for uploading cross-signing keys for the first time, per [MSC3967](https://github.com/matrix-org/matrix-spec-proposals/pull/3967). ([\#17284](https://github.com/element-hq/synapse/issues/17284))
|
||||||
|
- Add `stream_ordering` sort to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17293](https://github.com/element-hq/synapse/issues/17293))
|
||||||
|
- `register_new_matrix_user` now supports a --password-file flag, which
|
||||||
|
is useful for scripting. ([\#17294](https://github.com/element-hq/synapse/issues/17294))
|
||||||
|
- `register_new_matrix_user` now supports a --exists-ok flag to allow registration of users that already exist in the database.
|
||||||
|
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 `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))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fix searching for users with their exact localpart whose ID includes a hyphen. ([\#17254](https://github.com/element-hq/synapse/issues/17254))
|
||||||
|
- Fix wrong retention policy being used when filtering events. ([\#17272](https://github.com/element-hq/synapse/issues/17272))
|
||||||
|
- Fix bug where OTKs were not always included in `/sync` response when using workers. ([\#17275](https://github.com/element-hq/synapse/issues/17275))
|
||||||
|
- Fix a long-standing bug where an invalid 'from' parameter to [`/notifications`](https://spec.matrix.org/v1.10/client-server-api/#get_matrixclientv3notifications) would result in an Internal Server Error. ([\#17283](https://github.com/element-hq/synapse/issues/17283))
|
||||||
|
- Fix edge case in `/sync` returning the wrong the state when using sharded event persisters. ([\#17295](https://github.com/element-hq/synapse/issues/17295))
|
||||||
|
- Add initial implementation of an experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint. ([\#17301](https://github.com/element-hq/synapse/issues/17301))
|
||||||
|
- Fix email notification subject when invited to a space. ([\#17336](https://github.com/element-hq/synapse/issues/17336))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Add missing quotes for example for `exclude_rooms_from_sync`. ([\#17308](https://github.com/element-hq/synapse/issues/17308))
|
||||||
|
- Update header in the README to visually fix the the auto-generated table of contents. ([\#17329](https://github.com/element-hq/synapse/issues/17329))
|
||||||
|
- Fix stale references to the Foundation's Security Disclosure Policy. ([\#17341](https://github.com/element-hq/synapse/issues/17341))
|
||||||
|
- Add default values for `rc_invites.per_issuer` to docs. ([\#17347](https://github.com/element-hq/synapse/issues/17347))
|
||||||
|
- Fix an error in the docs for `search_all_users` parameter under `user_directory`. ([\#17348](https://github.com/element-hq/synapse/issues/17348))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Remove unused `expire_access_token` option in the Synapse Docker config file. Contributed by @AaronDewes. ([\#17198](https://github.com/element-hq/synapse/issues/17198))
|
||||||
|
- Use fully-qualified `PersistedEventPosition` when returning `RoomsForUser` to facilitate proper comparisons and `RoomStreamToken` generation. ([\#17265](https://github.com/element-hq/synapse/issues/17265))
|
||||||
|
- Add debug logging for when room keys are uploaded, including whether they are replacing other room keys. ([\#17266](https://github.com/element-hq/synapse/issues/17266))
|
||||||
|
- Handle OTK uploads off master. ([\#17271](https://github.com/element-hq/synapse/issues/17271))
|
||||||
|
- Don't try and resync devices for remote users whose servers are marked as down. ([\#17273](https://github.com/element-hq/synapse/issues/17273))
|
||||||
|
- Re-organize Pydantic models and types used in handlers. ([\#17279](https://github.com/element-hq/synapse/issues/17279))
|
||||||
|
- Expose the worker instance that persisted the event on `event.internal_metadata.instance_name`. ([\#17300](https://github.com/element-hq/synapse/issues/17300))
|
||||||
|
- Update the README with Element branding, improve headers and fix the #synapse:matrix.org support room link rendering. ([\#17324](https://github.com/element-hq/synapse/issues/17324))
|
||||||
|
- Change path of the experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync implementation to `/org.matrix.simplified_msc3575/sync` since our simplified API is slightly incompatible with what's in the current MSC. ([\#17331](https://github.com/element-hq/synapse/issues/17331))
|
||||||
|
- Handle device lists notifications for large accounts more efficiently in worker mode. ([\#17333](https://github.com/element-hq/synapse/issues/17333), [\#17358](https://github.com/element-hq/synapse/issues/17358))
|
||||||
|
- Do not block event sending/receiving while calculating large event auth chains. ([\#17338](https://github.com/element-hq/synapse/issues/17338))
|
||||||
|
- Tidy up `parse_integer` docs and call sites to reflect the fact that they require non-negative integers by default, and bring `parse_integer_from_args` default in alignment. Contributed by Denis Kasak (@dkasak). ([\#17339](https://github.com/element-hq/synapse/issues/17339))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump authlib from 1.3.0 to 1.3.1. ([\#17343](https://github.com/element-hq/synapse/issues/17343))
|
||||||
|
* Bump dawidd6/action-download-artifact from 3.1.4 to 5. ([\#17289](https://github.com/element-hq/synapse/issues/17289))
|
||||||
|
* Bump dawidd6/action-download-artifact from 5 to 6. ([\#17313](https://github.com/element-hq/synapse/issues/17313))
|
||||||
|
* Bump docker/build-push-action from 5 to 6. ([\#17312](https://github.com/element-hq/synapse/issues/17312))
|
||||||
|
* Bump jinja2 from 3.1.3 to 3.1.4. ([\#17287](https://github.com/element-hq/synapse/issues/17287))
|
||||||
|
* Bump lazy_static from 1.4.0 to 1.5.0. ([\#17355](https://github.com/element-hq/synapse/issues/17355))
|
||||||
|
* Bump msgpack from 1.0.7 to 1.0.8. ([\#17317](https://github.com/element-hq/synapse/issues/17317))
|
||||||
|
* Bump netaddr from 1.2.1 to 1.3.0. ([\#17353](https://github.com/element-hq/synapse/issues/17353))
|
||||||
|
* Bump packaging from 24.0 to 24.1. ([\#17352](https://github.com/element-hq/synapse/issues/17352))
|
||||||
|
* Bump phonenumbers from 8.13.37 to 8.13.39. ([\#17315](https://github.com/element-hq/synapse/issues/17315))
|
||||||
|
* Bump regex from 1.10.4 to 1.10.5. ([\#17290](https://github.com/element-hq/synapse/issues/17290))
|
||||||
|
* Bump requests from 2.31.0 to 2.32.2. ([\#17345](https://github.com/element-hq/synapse/issues/17345))
|
||||||
|
* Bump sentry-sdk from 2.1.1 to 2.3.1. ([\#17263](https://github.com/element-hq/synapse/issues/17263))
|
||||||
|
* Bump sentry-sdk from 2.3.1 to 2.6.0. ([\#17351](https://github.com/element-hq/synapse/issues/17351))
|
||||||
|
* Bump tornado from 6.4 to 6.4.1. ([\#17344](https://github.com/element-hq/synapse/issues/17344))
|
||||||
|
* Bump mypy from 1.8.0 to 1.9.0. ([\#17297](https://github.com/element-hq/synapse/issues/17297))
|
||||||
|
* Bump types-jsonschema from 4.21.0.20240311 to 4.22.0.20240610. ([\#17288](https://github.com/element-hq/synapse/issues/17288))
|
||||||
|
* Bump types-netaddr from 1.2.0.20240219 to 1.3.0.20240530. ([\#17314](https://github.com/element-hq/synapse/issues/17314))
|
||||||
|
* Bump types-pillow from 10.2.0.20240423 to 10.2.0.20240520. ([\#17285](https://github.com/element-hq/synapse/issues/17285))
|
||||||
|
* Bump types-pyyaml from 6.0.12.12 to 6.0.12.20240311. ([\#17316](https://github.com/element-hq/synapse/issues/17316))
|
||||||
|
* Bump typing-extensions from 4.11.0 to 4.12.2. ([\#17354](https://github.com/element-hq/synapse/issues/17354))
|
||||||
|
* Bump urllib3 from 2.0.7 to 2.2.2. ([\#17346](https://github.com/element-hq/synapse/issues/17346))
|
||||||
|
|
||||||
|
# Synapse 1.109.0 (2024-06-18)
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Fix the building of binary wheels for macOS by switching to macOS 12 CI runners. ([\#17319](https://github.com/element-hq/synapse/issues/17319))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.109.0rc3 (2024-06-17)
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- When rolling back to a previous Synapse version and then forwards again to this release, don't require server operators to manually run SQL. ([\#17305](https://github.com/element-hq/synapse/issues/17305), [\#17309](https://github.com/element-hq/synapse/issues/17309))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Use the release branch for sytest in release-branch PRs. ([\#17306](https://github.com/element-hq/synapse/issues/17306))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.109.0rc2 (2024-06-11)
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fix bug where one-time-keys were not always included in `/sync` response when using workers. Introduced in v1.109.0rc1. ([\#17275](https://github.com/element-hq/synapse/issues/17275))
|
||||||
|
- Fix bug where `/sync` could get stuck due to edge case in device lists handling. Introduced in v1.109.0rc1. ([\#17292](https://github.com/element-hq/synapse/issues/17292))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.109.0rc1 (2024-06-04)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add the ability to auto-accept invites on the behalf of users. See the [`auto_accept_invites`](https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#auto-accept-invites) config option for details. ([\#17147](https://github.com/element-hq/synapse/issues/17147))
|
||||||
|
- Add experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync/e2ee` endpoint for to-device messages and device encryption info. ([\#17167](https://github.com/element-hq/synapse/issues/17167))
|
||||||
|
- Support [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/issues/3916) by adding unstable media endpoints to `/_matrix/client`. ([\#17213](https://github.com/element-hq/synapse/issues/17213))
|
||||||
|
- Add logging to tasks managed by the task scheduler, showing CPU and database usage. ([\#17219](https://github.com/element-hq/synapse/issues/17219))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fix deduplicating of membership events to not create unused state groups. ([\#17164](https://github.com/element-hq/synapse/issues/17164))
|
||||||
|
- Fix bug where duplicate events could be sent down sync when using workers that are overloaded. ([\#17215](https://github.com/element-hq/synapse/issues/17215))
|
||||||
|
- Ignore attempts to send to-device messages to bad users, to avoid log spam when we try to connect to the bad server. ([\#17240](https://github.com/element-hq/synapse/issues/17240))
|
||||||
|
- Fix handling of duplicate concurrent uploading of device one-time-keys. ([\#17241](https://github.com/element-hq/synapse/issues/17241))
|
||||||
|
- Fix reporting of default tags to Sentry, such as worker name. Broke in v1.108.0. ([\#17251](https://github.com/element-hq/synapse/issues/17251))
|
||||||
|
- Fix bug where typing updates would not be sent when using workers after a restart. ([\#17252](https://github.com/element-hq/synapse/issues/17252))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Update the LemonLDAP documentation to say that claims should be explicitly included in the returned `id_token`, as Synapse won't request them. ([\#17204](https://github.com/element-hq/synapse/issues/17204))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Improve DB usage when fetching related events. ([\#17083](https://github.com/element-hq/synapse/issues/17083))
|
||||||
|
- Log exceptions when failing to auto-join new user according to the `auto_join_rooms` option. ([\#17176](https://github.com/element-hq/synapse/issues/17176))
|
||||||
|
- Reduce work of calculating outbound device lists updates. ([\#17211](https://github.com/element-hq/synapse/issues/17211))
|
||||||
|
- Improve performance of calculating device lists changes in `/sync`. ([\#17216](https://github.com/element-hq/synapse/issues/17216))
|
||||||
|
- Move towards using `MultiWriterIdGenerator` everywhere. ([\#17226](https://github.com/element-hq/synapse/issues/17226))
|
||||||
|
- Replaces all usages of `StreamIdGenerator` with `MultiWriterIdGenerator`. ([\#17229](https://github.com/element-hq/synapse/issues/17229))
|
||||||
|
- Change the `allow_unsafe_locale` config option to also apply when setting up new databases. ([\#17238](https://github.com/element-hq/synapse/issues/17238))
|
||||||
|
- Fix errors in logs about closing incorrect logging contexts when media gets rejected by a module. ([\#17239](https://github.com/element-hq/synapse/issues/17239), [\#17246](https://github.com/element-hq/synapse/issues/17246))
|
||||||
|
- Clean out invalid destinations from `device_federation_outbox` table. ([\#17242](https://github.com/element-hq/synapse/issues/17242))
|
||||||
|
- Stop logging errors when receiving invalid User IDs in key querys requests. ([\#17250](https://github.com/element-hq/synapse/issues/17250))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump anyhow from 1.0.83 to 1.0.86. ([\#17220](https://github.com/element-hq/synapse/issues/17220))
|
||||||
|
* Bump bcrypt from 4.1.2 to 4.1.3. ([\#17224](https://github.com/element-hq/synapse/issues/17224))
|
||||||
|
* Bump lxml from 5.2.1 to 5.2.2. ([\#17261](https://github.com/element-hq/synapse/issues/17261))
|
||||||
|
* Bump mypy-zope from 1.0.3 to 1.0.4. ([\#17262](https://github.com/element-hq/synapse/issues/17262))
|
||||||
|
* Bump phonenumbers from 8.13.35 to 8.13.37. ([\#17235](https://github.com/element-hq/synapse/issues/17235))
|
||||||
|
* Bump prometheus-client from 0.19.0 to 0.20.0. ([\#17233](https://github.com/element-hq/synapse/issues/17233))
|
||||||
|
* Bump pyasn1 from 0.5.1 to 0.6.0. ([\#17223](https://github.com/element-hq/synapse/issues/17223))
|
||||||
|
* Bump pyicu from 2.13 to 2.13.1. ([\#17236](https://github.com/element-hq/synapse/issues/17236))
|
||||||
|
* Bump pyopenssl from 24.0.0 to 24.1.0. ([\#17234](https://github.com/element-hq/synapse/issues/17234))
|
||||||
|
* Bump serde from 1.0.201 to 1.0.202. ([\#17221](https://github.com/element-hq/synapse/issues/17221))
|
||||||
|
* Bump serde from 1.0.202 to 1.0.203. ([\#17232](https://github.com/element-hq/synapse/issues/17232))
|
||||||
|
* Bump twine from 5.0.0 to 5.1.0. ([\#17225](https://github.com/element-hq/synapse/issues/17225))
|
||||||
|
* Bump types-psycopg2 from 2.9.21.20240311 to 2.9.21.20240417. ([\#17222](https://github.com/element-hq/synapse/issues/17222))
|
||||||
|
* Bump types-pyopenssl from 24.0.0.20240311 to 24.1.0.20240425. ([\#17260](https://github.com/element-hq/synapse/issues/17260))
|
||||||
|
|
||||||
|
# Synapse 1.108.0 (2024-05-28)
|
||||||
|
|
||||||
|
No significant changes since 1.108.0rc1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.108.0rc1 (2024-05-21)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add a feature that allows clients to query the configured federation whitelist. Disabled by default. ([\#16848](https://github.com/element-hq/synapse/issues/16848), [\#17199](https://github.com/element-hq/synapse/issues/17199))
|
||||||
|
- Add the ability to allow numeric user IDs with a specific prefix when in the CAS flow. Contributed by Aurélien Grimpard. ([\#17098](https://github.com/element-hq/synapse/issues/17098))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fix bug where push rules would be empty in `/sync` for some accounts. Introduced in v1.93.0. ([\#17142](https://github.com/element-hq/synapse/issues/17142))
|
||||||
|
- Add support for optional whitespace around the Federation API's `Authorization` header's parameter commas. ([\#17145](https://github.com/element-hq/synapse/issues/17145))
|
||||||
|
- Fix bug where disabling room publication prevented public rooms being created on workers. ([\#17177](https://github.com/element-hq/synapse/issues/17177), [\#17184](https://github.com/element-hq/synapse/issues/17184))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Document [`/v1/make_knock`](https://spec.matrix.org/v1.10/server-server-api/#get_matrixfederationv1make_knockroomiduserid) and [`/v1/send_knock/`](https://spec.matrix.org/v1.10/server-server-api/#put_matrixfederationv1send_knockroomideventid) federation endpoints as worker-compatible. ([\#17058](https://github.com/element-hq/synapse/issues/17058))
|
||||||
|
- Update User Admin API with note about prefixing OIDC external_id providers. ([\#17139](https://github.com/element-hq/synapse/issues/17139))
|
||||||
|
- Clarify the state of the created room when using the `autocreate_auto_join_room_preset` config option. ([\#17150](https://github.com/element-hq/synapse/issues/17150))
|
||||||
|
- Update the Admin FAQ with the current libjemalloc version for latest Debian stable. Additionally update the name of the "push_rules" stream in the Workers documentation. ([\#17171](https://github.com/element-hq/synapse/issues/17171))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Add note to reflect that [MSC3886](https://github.com/matrix-org/matrix-spec-proposals/pull/3886) is closed but will remain supported for some time. ([\#17151](https://github.com/element-hq/synapse/issues/17151))
|
||||||
|
- Update dependency PyO3 to 0.21. ([\#17162](https://github.com/element-hq/synapse/issues/17162))
|
||||||
|
- Fixes linter errors found in PR #17147. ([\#17166](https://github.com/element-hq/synapse/issues/17166))
|
||||||
|
- Bump black from 24.2.0 to 24.4.2. ([\#17170](https://github.com/element-hq/synapse/issues/17170))
|
||||||
|
- Cache literal sync filter validation for performance. ([\#17186](https://github.com/element-hq/synapse/issues/17186))
|
||||||
|
- Improve performance by fixing a reactor pause. ([\#17192](https://github.com/element-hq/synapse/issues/17192))
|
||||||
|
- Route `/make_knock` and `/send_knock` federation APIs to the federation reader worker in Complement test runs. ([\#17195](https://github.com/element-hq/synapse/issues/17195))
|
||||||
|
- Prepare sync handler to be able to return different sync responses (`SyncVersion`). ([\#17200](https://github.com/element-hq/synapse/issues/17200))
|
||||||
|
- Organize the sync cache key parameter outside of the sync config (separate concerns). ([\#17201](https://github.com/element-hq/synapse/issues/17201))
|
||||||
|
- Refactor `SyncResultBuilder` assembly to its own function. ([\#17202](https://github.com/element-hq/synapse/issues/17202))
|
||||||
|
- Rename to be obvious: `joined_rooms` -> `joined_room_ids`. ([\#17203](https://github.com/element-hq/synapse/issues/17203), [\#17208](https://github.com/element-hq/synapse/issues/17208))
|
||||||
|
- Add a short pause when rate-limiting a request. ([\#17210](https://github.com/element-hq/synapse/issues/17210))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump cryptography from 42.0.5 to 42.0.7. ([\#17180](https://github.com/element-hq/synapse/issues/17180))
|
||||||
|
* Bump gitpython from 3.1.41 to 3.1.43. ([\#17181](https://github.com/element-hq/synapse/issues/17181))
|
||||||
|
* Bump immutabledict from 4.1.0 to 4.2.0. ([\#17179](https://github.com/element-hq/synapse/issues/17179))
|
||||||
|
* Bump sentry-sdk from 1.40.3 to 2.1.1. ([\#17178](https://github.com/element-hq/synapse/issues/17178))
|
||||||
|
* Bump serde from 1.0.200 to 1.0.201. ([\#17183](https://github.com/element-hq/synapse/issues/17183))
|
||||||
|
* Bump serde_json from 1.0.116 to 1.0.117. ([\#17182](https://github.com/element-hq/synapse/issues/17182))
|
||||||
|
|
||||||
|
Synapse 1.107.0 (2024-05-14)
|
||||||
|
============================
|
||||||
|
|
||||||
|
No significant changes since 1.107.0rc1.
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.107.0rc1 (2024-05-07)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add preliminary support for [MSC3823: Account Suspension](https://github.com/matrix-org/matrix-spec-proposals/pull/3823). ([\#17051](https://github.com/element-hq/synapse/issues/17051))
|
||||||
|
- Declare support for [Matrix v1.10](https://matrix.org/blog/2024/03/22/matrix-v1.10-release/). Contributed by @clokep. ([\#17082](https://github.com/element-hq/synapse/issues/17082))
|
||||||
|
- Add support for [MSC4115: membership metadata on events](https://github.com/matrix-org/matrix-spec-proposals/pull/4115). ([\#17104](https://github.com/element-hq/synapse/issues/17104), [\#17137](https://github.com/element-hq/synapse/issues/17137))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fixed search feature of Element Android on homesevers using SQLite by returning search terms as search highlights. ([\#17000](https://github.com/element-hq/synapse/issues/17000))
|
||||||
|
- Fixes a bug introduced in v1.52.0 where the `destination` query parameter for the [Destination Rooms Admin API](https://element-hq.github.io/synapse/v1.105/usage/administration/admin_api/federation.html#destination-rooms) failed to actually filter returned rooms. ([\#17077](https://github.com/element-hq/synapse/issues/17077))
|
||||||
|
- For MSC3266 room summaries, support queries at the recommended endpoint of `/_matrix/client/unstable/im.nheko.summary/summary/{roomIdOrAlias}`. The existing endpoint of `/_matrix/client/unstable/im.nheko.summary/rooms/{roomIdOrAlias}/summary` is deprecated. ([\#17078](https://github.com/element-hq/synapse/issues/17078))
|
||||||
|
- Apply user email & picture during OIDC registration if present & selected. ([\#17120](https://github.com/element-hq/synapse/issues/17120))
|
||||||
|
- Improve error message for cross signing reset with [MSC3861](https://github.com/matrix-org/matrix-spec-proposals/pull/3861) enabled. ([\#17121](https://github.com/element-hq/synapse/issues/17121))
|
||||||
|
- Fix a bug which meant that to-device messages received over federation could be dropped when the server was under load or networking problems caused problems between Synapse processes or the database. ([\#17127](https://github.com/element-hq/synapse/issues/17127))
|
||||||
|
- Fix bug where `StreamChangeCache` would not respect configured cache factors. ([\#17152](https://github.com/element-hq/synapse/issues/17152))
|
||||||
|
|
||||||
|
### Updates to the Docker image
|
||||||
|
|
||||||
|
- Correct licensing metadata on Docker image. ([\#17141](https://github.com/element-hq/synapse/issues/17141))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Update the `event_cache_size` and `global_factor` configuration options' documentation. ([\#17071](https://github.com/element-hq/synapse/issues/17071))
|
||||||
|
- Remove broken sphinx docs. ([\#17073](https://github.com/element-hq/synapse/issues/17073), [\#17148](https://github.com/element-hq/synapse/issues/17148))
|
||||||
|
- Add RuntimeDirectory to example matrix-synapse.service systemd unit. ([\#17084](https://github.com/element-hq/synapse/issues/17084))
|
||||||
|
- Fix various small typos throughout the docs. ([\#17114](https://github.com/element-hq/synapse/issues/17114))
|
||||||
|
- Update enable_notifs configuration documentation. ([\#17116](https://github.com/element-hq/synapse/issues/17116))
|
||||||
|
- Update the Upgrade Notes with the latest minimum supported Rust version of 1.66.0. Contributed by @jahway603. ([\#17140](https://github.com/element-hq/synapse/issues/17140))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Enable [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266) by default in the Synapse Complement image. ([\#17105](https://github.com/element-hq/synapse/issues/17105))
|
||||||
|
- Add optimisation to `StreamChangeCache.get_entities_changed(..)`. ([\#17130](https://github.com/element-hq/synapse/issues/17130))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump furo from 2024.1.29 to 2024.4.27. ([\#17133](https://github.com/element-hq/synapse/issues/17133))
|
||||||
|
* Bump idna from 3.6 to 3.7. ([\#17136](https://github.com/element-hq/synapse/issues/17136))
|
||||||
|
* Bump jsonschema from 4.21.1 to 4.22.0. ([\#17157](https://github.com/element-hq/synapse/issues/17157))
|
||||||
|
* Bump lxml from 5.1.0 to 5.2.1. ([\#17158](https://github.com/element-hq/synapse/issues/17158))
|
||||||
|
* Bump phonenumbers from 8.13.29 to 8.13.35. ([\#17106](https://github.com/element-hq/synapse/issues/17106))
|
||||||
|
- Bump pillow from 10.2.0 to 10.3.0. ([\#17146](https://github.com/element-hq/synapse/issues/17146))
|
||||||
|
* Bump pydantic from 2.6.4 to 2.7.0. ([\#17107](https://github.com/element-hq/synapse/issues/17107))
|
||||||
|
* Bump pydantic from 2.7.0 to 2.7.1. ([\#17160](https://github.com/element-hq/synapse/issues/17160))
|
||||||
|
* Bump pyicu from 2.12 to 2.13. ([\#17109](https://github.com/element-hq/synapse/issues/17109))
|
||||||
|
* Bump serde from 1.0.197 to 1.0.198. ([\#17111](https://github.com/element-hq/synapse/issues/17111))
|
||||||
|
* Bump serde from 1.0.198 to 1.0.199. ([\#17132](https://github.com/element-hq/synapse/issues/17132))
|
||||||
|
* Bump serde from 1.0.199 to 1.0.200. ([\#17161](https://github.com/element-hq/synapse/issues/17161))
|
||||||
|
* Bump serde_json from 1.0.115 to 1.0.116. ([\#17112](https://github.com/element-hq/synapse/issues/17112))
|
||||||
|
- Update `tornado` Python dependency from 6.2 to 6.4. ([\#17131](https://github.com/element-hq/synapse/issues/17131))
|
||||||
|
* Bump twisted from 23.10.0 to 24.3.0. ([\#17135](https://github.com/element-hq/synapse/issues/17135))
|
||||||
|
* Bump types-bleach from 6.1.0.1 to 6.1.0.20240331. ([\#17110](https://github.com/element-hq/synapse/issues/17110))
|
||||||
|
* Bump types-pillow from 10.2.0.20240415 to 10.2.0.20240423. ([\#17159](https://github.com/element-hq/synapse/issues/17159))
|
||||||
|
* Bump types-setuptools from 69.0.0.20240125 to 69.5.0.20240423. ([\#17134](https://github.com/element-hq/synapse/issues/17134))
|
||||||
|
|
||||||
|
# Synapse 1.106.0 (2024-04-30)
|
||||||
|
|
||||||
|
No significant changes since 1.106.0rc1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.106.0rc1 (2024-04-25)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Send an email if the address is already bound to an user account. ([\#16819](https://github.com/element-hq/synapse/issues/16819))
|
||||||
|
- Implement the rendezvous mechanism described by [MSC4108](https://github.com/matrix-org/matrix-spec-proposals/issues/4108). ([\#17056](https://github.com/element-hq/synapse/issues/17056))
|
||||||
|
- Support delegating the rendezvous mechanism described [MSC4108](https://github.com/matrix-org/matrix-spec-proposals/issues/4108) to an external implementation. ([\#17086](https://github.com/element-hq/synapse/issues/17086))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Add validation to ensure that the `limit` parameter on `/publicRooms` is non-negative. ([\#16920](https://github.com/element-hq/synapse/issues/16920))
|
||||||
|
- Return `400 M_NOT_JSON` upon receiving invalid JSON in query parameters across various client and admin endpoints, rather than an internal server error. ([\#16923](https://github.com/element-hq/synapse/issues/16923))
|
||||||
|
- Make the CSAPI endpoint `/keys/device_signing/upload` idempotent. ([\#16943](https://github.com/element-hq/synapse/issues/16943))
|
||||||
|
- Redact membership events if the user requested erasure upon deactivating. ([\#17076](https://github.com/element-hq/synapse/issues/17076))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Add a prompt in the contributing guide to manually configure icu4c. ([\#17069](https://github.com/element-hq/synapse/issues/17069))
|
||||||
|
- Clarify what part of message retention is still experimental. ([\#17099](https://github.com/element-hq/synapse/issues/17099))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Use new receipts column to optimise receipt and push action SQL queries. Contributed by Nick @ Beeper (@fizzadar). ([\#17032](https://github.com/element-hq/synapse/issues/17032), [\#17096](https://github.com/element-hq/synapse/issues/17096))
|
||||||
|
- Fix mypy with latest Twisted release. ([\#17036](https://github.com/element-hq/synapse/issues/17036))
|
||||||
|
- Bump minimum supported Rust version to 1.66.0. ([\#17079](https://github.com/element-hq/synapse/issues/17079))
|
||||||
|
- Add helpers to transform Twisted requests to Rust http Requests/Responses. ([\#17081](https://github.com/element-hq/synapse/issues/17081))
|
||||||
|
- Fix type annotation for `visited_chains` after `mypy` upgrade. ([\#17125](https://github.com/element-hq/synapse/issues/17125))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump anyhow from 1.0.81 to 1.0.82. ([\#17095](https://github.com/element-hq/synapse/issues/17095))
|
||||||
|
* Bump peaceiris/actions-gh-pages from 3.9.3 to 4.0.0. ([\#17087](https://github.com/element-hq/synapse/issues/17087))
|
||||||
|
* Bump peaceiris/actions-mdbook from 1.2.0 to 2.0.0. ([\#17089](https://github.com/element-hq/synapse/issues/17089))
|
||||||
|
* Bump pyasn1-modules from 0.3.0 to 0.4.0. ([\#17093](https://github.com/element-hq/synapse/issues/17093))
|
||||||
|
* Bump pygithub from 2.2.0 to 2.3.0. ([\#17092](https://github.com/element-hq/synapse/issues/17092))
|
||||||
|
* Bump ruff from 0.3.5 to 0.3.7. ([\#17094](https://github.com/element-hq/synapse/issues/17094))
|
||||||
|
* Bump sigstore/cosign-installer from 3.4.0 to 3.5.0. ([\#17088](https://github.com/element-hq/synapse/issues/17088))
|
||||||
|
* Bump twine from 4.0.2 to 5.0.0. ([\#17091](https://github.com/element-hq/synapse/issues/17091))
|
||||||
|
* Bump types-pillow from 10.2.0.20240406 to 10.2.0.20240415. ([\#17090](https://github.com/element-hq/synapse/issues/17090))
|
||||||
|
|
||||||
|
# Synapse 1.105.1 (2024-04-23)
|
||||||
|
|
||||||
|
## Security advisory
|
||||||
|
|
||||||
|
The following issues are fixed in 1.105.1.
|
||||||
|
|
||||||
|
- [GHSA-3h7q-rfh9-xm4v](https://github.com/element-hq/synapse/security/advisories/GHSA-3h7q-rfh9-xm4v) / [CVE-2024-31208](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-31208) — High Severity
|
||||||
|
|
||||||
|
Weakness in auth chain indexing allows DoS from remote room members through disk fill and high CPU usage.
|
||||||
|
|
||||||
|
See the advisories for more details. If you have any questions, email security@element.io.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.105.0 (2024-04-16)
|
||||||
|
|
||||||
|
No significant changes since 1.105.0rc1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.105.0rc1 (2024-04-11)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Stabilize support for [MSC4010](https://github.com/matrix-org/matrix-spec-proposals/pull/4010) which clarifies the interaction of push rules and account data. Contributed by @clokep. ([\#17022](https://github.com/element-hq/synapse/issues/17022))
|
||||||
|
- Stabilize support for [MSC3981](https://github.com/matrix-org/matrix-spec-proposals/pull/3981): `/relations` recursion. Contributed by @clokep. ([\#17023](https://github.com/element-hq/synapse/issues/17023))
|
||||||
|
- Add support for moving `/pushrules` off of main process. ([\#17037](https://github.com/element-hq/synapse/issues/17037), [\#17038](https://github.com/element-hq/synapse/issues/17038))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fix various long-standing bugs which could cause incorrect state to be returned from `/sync` in certain situations. ([\#16930](https://github.com/element-hq/synapse/issues/16930), [\#16932](https://github.com/element-hq/synapse/issues/16932), [\#16942](https://github.com/element-hq/synapse/issues/16942), [\#17064](https://github.com/element-hq/synapse/issues/17064), [\#17065](https://github.com/element-hq/synapse/issues/17065), [\#17066](https://github.com/element-hq/synapse/issues/17066))
|
||||||
|
- Fix server notice rooms not always being created as unencrypted rooms, even when `encryption_enabled_by_default_for_room_type` is in use (server notices are always unencrypted). ([\#17033](https://github.com/element-hq/synapse/issues/17033))
|
||||||
|
- Fix the `.m.rule.encrypted_room_one_to_one` and `.m.rule.room_one_to_one` default underride push rules being in the wrong order. Contributed by @Sumpy1. ([\#17043](https://github.com/element-hq/synapse/issues/17043))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Refactor auth chain fetching to reduce duplication. ([\#17044](https://github.com/element-hq/synapse/issues/17044))
|
||||||
|
- Improve database performance by adding a missing index to `access_tokens.refresh_token_id`. ([\#17045](https://github.com/element-hq/synapse/issues/17045), [\#17054](https://github.com/element-hq/synapse/issues/17054))
|
||||||
|
- Improve database performance by reducing number of receipts fetched when sending push notifications. ([\#17049](https://github.com/element-hq/synapse/issues/17049))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump packaging from 23.2 to 24.0. ([\#17027](https://github.com/element-hq/synapse/issues/17027))
|
||||||
|
* Bump regex from 1.10.3 to 1.10.4. ([\#17028](https://github.com/element-hq/synapse/issues/17028))
|
||||||
|
* Bump ruff from 0.3.2 to 0.3.5. ([\#17060](https://github.com/element-hq/synapse/issues/17060))
|
||||||
|
* Bump serde_json from 1.0.114 to 1.0.115. ([\#17041](https://github.com/element-hq/synapse/issues/17041))
|
||||||
|
* Bump types-pillow from 10.2.0.20240125 to 10.2.0.20240406. ([\#17061](https://github.com/element-hq/synapse/issues/17061))
|
||||||
|
* Bump types-requests from 2.31.0.20240125 to 2.31.0.20240406. ([\#17063](https://github.com/element-hq/synapse/issues/17063))
|
||||||
|
* Bump typing-extensions from 4.9.0 to 4.11.0. ([\#17062](https://github.com/element-hq/synapse/issues/17062))
|
||||||
|
|
||||||
# Synapse 1.104.0 (2024-04-02)
|
# Synapse 1.104.0 (2024-04-02)
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
|
|
479
Cargo.lock
generated
479
Cargo.lock
generated
|
@ -4,36 +4,42 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.0.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 = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.81"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.5.1"
|
version = "1.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
|
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.21.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2"
|
name = "blake2"
|
||||||
|
@ -46,19 +52,40 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.3"
|
version = "0.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -71,9 +98,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.5"
|
version = "0.10.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
@ -81,15 +108,58 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "fnv"
|
||||||
version = "0.14.6"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "headers"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"headers-core",
|
||||||
|
"http",
|
||||||
|
"httpdate",
|
||||||
|
"mime",
|
||||||
|
"sha1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "headers-core"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
|
||||||
|
dependencies = [
|
||||||
|
"http",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -103,34 +173,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indoc"
|
name = "http"
|
||||||
version = "2.0.4"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
|
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpdate"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indoc"
|
||||||
|
version = "2.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.4"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.135"
|
version = "0.2.154"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.9"
|
version = "0.4.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
|
@ -144,30 +240,36 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.3"
|
version = "2.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
|
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.9.0"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "mime"
|
||||||
version = "1.15.0"
|
version = "0.3.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.1"
|
version = "0.12.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
|
@ -175,15 +277,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.9.3"
|
version = "0.9.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-sys",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -193,19 +295,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "ppv-lite86"
|
||||||
version = "1.0.76"
|
version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyo3"
|
name = "pyo3"
|
||||||
version = "0.20.3"
|
version = "0.21.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53bdbb96d49157e65d45cc287af5f32ffadd5f4761438b527b055fb0d4bb8233"
|
checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
@ -222,9 +330,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyo3-build-config"
|
name = "pyo3-build-config"
|
||||||
version = "0.20.3"
|
version = "0.21.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "deaa5745de3f5231ce10517a1f5dd97d53e5a2fd77aa6b5842292085831d48d7"
|
checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
|
@ -232,9 +340,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyo3-ffi"
|
name = "pyo3-ffi"
|
||||||
version = "0.20.3"
|
version = "0.21.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa"
|
checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"pyo3-build-config",
|
"pyo3-build-config",
|
||||||
|
@ -242,9 +350,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyo3-log"
|
name = "pyo3-log"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c10808ee7250403bedb24bc30c32493e93875fef7ba3e4292226fe924f398bd"
|
checksum = "2af49834b8d2ecd555177e63b273b708dea75150abc6f5341d0a6e1a9623976c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"log",
|
"log",
|
||||||
|
@ -253,9 +361,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyo3-macros"
|
name = "pyo3-macros"
|
||||||
version = "0.20.3"
|
version = "0.21.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7305c720fa01b8055ec95e484a6eca7a83c841267f0dd5280f0c8b8551d2c158"
|
checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"pyo3-macros-backend",
|
"pyo3-macros-backend",
|
||||||
|
@ -265,9 +373,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyo3-macros-backend"
|
name = "pyo3-macros-backend"
|
||||||
version = "0.20.3"
|
version = "0.21.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c7e9b68bb9c3149c5b0cade5d07f953d6d125eb4337723c4ccdb665f1f96185"
|
checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -278,9 +386,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pythonize"
|
name = "pythonize"
|
||||||
version = "0.20.0"
|
version = "0.21.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ffd1c3ef39c725d63db5f9bc455461bafd80540cb7824c61afb823501921a850"
|
checksum = "9d0664248812c38cc55a4ed07f88e4df516ce82604b93b1ffdc041aa77a6cb3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pyo3",
|
"pyo3",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -288,27 +396,57 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.35"
|
version = "1.0.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "rand"
|
||||||
version = "0.2.16"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.3"
|
version = "1.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -318,9 +456,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.4"
|
version = "0.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
|
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -329,36 +467,36 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.2"
|
version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.11"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.197"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.197"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -367,9 +505,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.114"
|
version = "1.0.117"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
|
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -377,22 +515,44 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "sha1"
|
||||||
version = "1.10.0"
|
version = "0.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.4.1"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.48"
|
version = "2.0.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -404,35 +564,53 @@ name = "synapse"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"base64",
|
||||||
"blake2",
|
"blake2",
|
||||||
|
"bytes",
|
||||||
|
"headers",
|
||||||
"hex",
|
"hex",
|
||||||
|
"http",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
"mime",
|
||||||
"pyo3",
|
"pyo3",
|
||||||
"pyo3-log",
|
"pyo3-log",
|
||||||
"pythonize",
|
"pythonize",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"ulid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.4"
|
version = "0.12.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1"
|
checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.15.0"
|
version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ulid"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"rand",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.5"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unindent"
|
name = "unindent"
|
||||||
|
@ -447,44 +625,135 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "wasi"
|
||||||
version = "0.36.1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-time"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.36.1"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.36.1"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.36.1"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.36.1"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||||
|
|
61
Dockerfile
Normal file
61
Dockerfile
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
ARG PYTHON_VERSION=3.11
|
||||||
|
|
||||||
|
FROM docker.io/python:${PYTHON_VERSION}-slim as builder
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
libffi-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libpq-dev \
|
||||||
|
libssl-dev \
|
||||||
|
libwebp-dev \
|
||||||
|
libxml++2.6-dev \
|
||||||
|
libxslt1-dev \
|
||||||
|
zlib1g-dev \
|
||||||
|
openssl \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV RUSTUP_HOME=/rust
|
||||||
|
ENV CARGO_HOME=/cargo
|
||||||
|
ENV PATH=/cargo/bin:/rust/bin:$PATH
|
||||||
|
RUN mkdir /rust /cargo
|
||||||
|
|
||||||
|
RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path --default-toolchain stable
|
||||||
|
|
||||||
|
COPY synapse /synapse/synapse/
|
||||||
|
COPY rust /synapse/rust/
|
||||||
|
COPY README.rst pyproject.toml requirements.txt build_rust.py /synapse/
|
||||||
|
|
||||||
|
RUN pip install --prefix="/install" --no-warn-script-location --ignore-installed \
|
||||||
|
--no-deps -r /synapse/requirements.txt \
|
||||||
|
&& pip install --prefix="/install" --no-warn-script-location \
|
||||||
|
--no-deps \
|
||||||
|
'git+https://github.com/maunium/synapse-simple-antispam#egg=synapse-simple-antispam' \
|
||||||
|
'git+https://github.com/devture/matrix-synapse-shared-secret-auth@2.0.3#egg=shared_secret_authenticator' \
|
||||||
|
&& pip install --prefix="/install" --no-warn-script-location \
|
||||||
|
--no-deps /synapse
|
||||||
|
|
||||||
|
FROM docker.io/python:${PYTHON_VERSION}-slim
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
libjpeg62-turbo \
|
||||||
|
libpq5 \
|
||||||
|
libwebp7 \
|
||||||
|
xmlsec1 \
|
||||||
|
libjemalloc2 \
|
||||||
|
openssl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY --from=builder /install /usr/local
|
||||||
|
|
||||||
|
VOLUME ["/data"]
|
||||||
|
ENV LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
|
||||||
|
|
||||||
|
ENTRYPOINT ["python3", "-m", "synapse.app.homeserver"]
|
||||||
|
CMD ["--keys-directory", "/data", "-c", "/data/homeserver.yaml"]
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=5s --interval=1m --timeout=5s \
|
||||||
|
CMD curl -fSs http://localhost:8008/health || exit 1
|
63
README.md
Normal file
63
README.md
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# Maunium Synapse
|
||||||
|
This is a fork of [Synapse] to remove dumb limits and fix bugs that the
|
||||||
|
upstream devs don't want to fix.
|
||||||
|
|
||||||
|
The only official distribution is the docker image in the [GitLab container
|
||||||
|
registry], but you can also install from source ([upstream instructions]).
|
||||||
|
|
||||||
|
The master branch and `:latest` docker tag are upgraded to each upstream
|
||||||
|
release candidate very soon after release (usually within 10 minutes†). There
|
||||||
|
are also docker tags for each release, e.g. `:1.75.0`. If you don't want RCs,
|
||||||
|
use the specific release tags.
|
||||||
|
|
||||||
|
†If there are merge conflicts, the update may be delayed for up to a few days
|
||||||
|
after the full release.
|
||||||
|
|
||||||
|
[Synapse]: https://github.com/matrix-org/synapse
|
||||||
|
[GitLab container registry]: https://mau.dev/maunium/synapse/container_registry
|
||||||
|
[upstream instructions]: https://github.com/matrix-org/synapse/blob/develop/INSTALL.md#installing-from-source
|
||||||
|
|
||||||
|
## List of changes
|
||||||
|
* Default power level for room creator is 9001 instead of 100.
|
||||||
|
* Room creator can specify a custom room ID with the `room_id` param in the
|
||||||
|
request body. If the room ID is already in use, it will return `M_CONFLICT`.
|
||||||
|
* ~~URL previewer user agent includes `Bot` so Twitter previews work properly.~~
|
||||||
|
Upstreamed after over 2 years 🎉
|
||||||
|
* ~~Local event creation concurrency is disabled to avoid unnecessary state
|
||||||
|
resolution.~~ Upstreamed after over 3 years 🎉
|
||||||
|
* Register admin API can register invalid user IDs.
|
||||||
|
* Docker image with jemalloc enabled by default.
|
||||||
|
* Config option to allow specific users to send events without unnecessary
|
||||||
|
validation.
|
||||||
|
* Config option to allow specific users to receive events that are usually
|
||||||
|
filtered away (e.g. `org.matrix.dummy_event` and `m.room.aliases`).
|
||||||
|
* Config option to allow specific users to use timestamp massaging without
|
||||||
|
being appservice users.
|
||||||
|
* Removed bad pusher URL validation.
|
||||||
|
* webp images are thumbnailed to webp instead of jpeg to avoid losing
|
||||||
|
transparency.
|
||||||
|
* Media repo `Cache-Control` header says `immutable` and 1 year for all media
|
||||||
|
that exists, as media IDs in Matrix are immutable.
|
||||||
|
* Allowed sending custom data with read receipts.
|
||||||
|
|
||||||
|
You can view the full list of changes on the [meow-patchset] branch.
|
||||||
|
Additionally, historical patch sets are saved as `meow-patchset-vX` [tags].
|
||||||
|
|
||||||
|
[meow-patchset]: https://mau.dev/maunium/synapse/-/compare/patchset-base...meow-patchset
|
||||||
|
[tags]: https://mau.dev/maunium/synapse/-/tags?search=meow-patchset&sort=updated_desc
|
||||||
|
|
||||||
|
## Configuration reference
|
||||||
|
```yaml
|
||||||
|
meow:
|
||||||
|
# List of users who aren't subject to unnecessary validation in the C-S API.
|
||||||
|
validation_override:
|
||||||
|
- "@you:example.com"
|
||||||
|
# List of users who will get org.matrix.dummy_event and m.room.aliases events down /sync
|
||||||
|
filter_override:
|
||||||
|
- "@you:example.com"
|
||||||
|
# Whether or not the admin API should be able to register invalid user IDs.
|
||||||
|
admin_api_register_invalid: true
|
||||||
|
# List of users who can use timestamp massaging without being appservices
|
||||||
|
timestamp_override:
|
||||||
|
- "@you:example.com"
|
||||||
|
```
|
71
README.rst
71
README.rst
|
@ -1,21 +1,34 @@
|
||||||
=========================================================================
|
.. image:: https://github.com/element-hq/product/assets/87339233/7abf477a-5277-47f3-be44-ea44917d8ed7
|
||||||
Synapse |support| |development| |documentation| |license| |pypi| |python|
|
:height: 60px
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
Synapse is an open-source `Matrix <https://matrix.org/>`_ homeserver written and
|
**Element Synapse - Matrix homeserver implementation**
|
||||||
maintained by the Matrix.org Foundation. We began rapid development in 2014,
|
|
||||||
reaching v1.0.0 in 2019. Development on Synapse and the Matrix protocol itself continues
|
|
||||||
in earnest today.
|
|
||||||
|
|
||||||
Briefly, Matrix is an open standard for communications on the internet, supporting
|
|support| |development| |documentation| |license| |pypi| |python|
|
||||||
federation, encryption and VoIP. Matrix.org has more to say about the `goals of the
|
|
||||||
Matrix project <https://matrix.org/docs/guides/introduction>`_, and the `formal specification
|
Synapse is an open source `Matrix <https://matrix.org>`_ homeserver
|
||||||
<https://spec.matrix.org/>`_ describes the technical details.
|
implementation, written and maintained by `Element <https://element.io>`_.
|
||||||
|
`Matrix <https://github.com/matrix-org>`_ is the open standard for
|
||||||
|
secure and interoperable real time communications. You can directly run
|
||||||
|
and manage the source code in this repository, available under an AGPL
|
||||||
|
license. There is no support provided from Element unless you have a
|
||||||
|
subscription.
|
||||||
|
|
||||||
|
Subscription alternative
|
||||||
|
========================
|
||||||
|
|
||||||
|
Alternatively, for those that need an enterprise-ready solution, Element
|
||||||
|
Server Suite (ESS) is `available as a subscription <https://element.io/pricing>`_.
|
||||||
|
ESS builds on Synapse to offer a complete Matrix-based backend including the full
|
||||||
|
`Admin Console product <https://element.io/enterprise-functionality/admin-console>`_,
|
||||||
|
giving admins the power to easily manage an organization-wide
|
||||||
|
deployment. It includes advanced identity management, auditing,
|
||||||
|
moderation and data retention options as well as Long Term Support and
|
||||||
|
SLAs. ESS can be used to support any Matrix-based frontend client.
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
Installing and configuration
|
🛠️ Installing and configuration
|
||||||
============================
|
===============================
|
||||||
|
|
||||||
The Synapse documentation describes `how to install Synapse <https://element-hq.github.io/synapse/latest/setup/installation.html>`_. We recommend using
|
The Synapse documentation describes `how to install Synapse <https://element-hq.github.io/synapse/latest/setup/installation.html>`_. We recommend using
|
||||||
`Docker images <https://element-hq.github.io/synapse/latest/setup/installation.html#docker-images-and-ansible-playbooks>`_ or `Debian packages from Matrix.org
|
`Docker images <https://element-hq.github.io/synapse/latest/setup/installation.html#docker-images-and-ansible-playbooks>`_ or `Debian packages from Matrix.org
|
||||||
|
@ -105,8 +118,8 @@ Following this advice ensures that even if an XSS is found in Synapse, the
|
||||||
impact to other applications will be minimal.
|
impact to other applications will be minimal.
|
||||||
|
|
||||||
|
|
||||||
Testing a new installation
|
🧪 Testing a new installation
|
||||||
==========================
|
============================
|
||||||
|
|
||||||
The easiest way to try out your new Synapse installation is by connecting to it
|
The easiest way to try out your new Synapse installation is by connecting to it
|
||||||
from a web client.
|
from a web client.
|
||||||
|
@ -159,8 +172,20 @@ the form of::
|
||||||
As when logging in, you will need to specify a "Custom server". Specify your
|
As when logging in, you will need to specify a "Custom server". Specify your
|
||||||
desired ``localpart`` in the 'User name' box.
|
desired ``localpart`` in the 'User name' box.
|
||||||
|
|
||||||
Troubleshooting and support
|
🎯 Troubleshooting and support
|
||||||
===========================
|
=============================
|
||||||
|
|
||||||
|
🚀 Professional support
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Enterprise quality support for Synapse including SLAs is available as part of an
|
||||||
|
`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>`
|
||||||
|
and access the `knowledge base <https://ems-docs.element.io>`.
|
||||||
|
|
||||||
|
🤝 Community support
|
||||||
|
-------------------
|
||||||
|
|
||||||
The `Admin FAQ <https://element-hq.github.io/synapse/latest/usage/administration/admin_faq.html>`_
|
The `Admin FAQ <https://element-hq.github.io/synapse/latest/usage/administration/admin_faq.html>`_
|
||||||
includes tips on dealing with some common problems. For more details, see
|
includes tips on dealing with some common problems. For more details, see
|
||||||
|
@ -176,8 +201,8 @@ issues for support requests, only for bug reports and feature requests.
|
||||||
.. |docs| replace:: ``docs``
|
.. |docs| replace:: ``docs``
|
||||||
.. _docs: docs
|
.. _docs: docs
|
||||||
|
|
||||||
Identity Servers
|
🪪 Identity Servers
|
||||||
================
|
==================
|
||||||
|
|
||||||
Identity servers have the job of mapping email addresses and other 3rd Party
|
Identity servers have the job of mapping email addresses and other 3rd Party
|
||||||
IDs (3PIDs) to Matrix user IDs, as well as verifying the ownership of 3PIDs
|
IDs (3PIDs) to Matrix user IDs, as well as verifying the ownership of 3PIDs
|
||||||
|
@ -206,8 +231,8 @@ an email address with your account, or send an invite to another user via their
|
||||||
email address.
|
email address.
|
||||||
|
|
||||||
|
|
||||||
Development
|
🛠️ Development
|
||||||
===========
|
==============
|
||||||
|
|
||||||
We welcome contributions to Synapse from the community!
|
We welcome contributions to Synapse from the community!
|
||||||
The best place to get started is our
|
The best place to get started is our
|
||||||
|
@ -225,8 +250,8 @@ Alongside all that, join our developer community on Matrix:
|
||||||
`#synapse-dev:matrix.org <https://matrix.to/#/#synapse-dev:matrix.org>`_, featuring real humans!
|
`#synapse-dev:matrix.org <https://matrix.to/#/#synapse-dev:matrix.org>`_, featuring real humans!
|
||||||
|
|
||||||
|
|
||||||
.. |support| image:: https://img.shields.io/matrix/synapse:matrix.org?label=support&logo=matrix
|
.. |support| image:: https://img.shields.io/badge/matrix-community%20support-success
|
||||||
:alt: (get support on #synapse:matrix.org)
|
:alt: (get community support in #synapse:matrix.org)
|
||||||
:target: https://matrix.to/#/#synapse:matrix.org
|
:target: https://matrix.to/#/#synapse:matrix.org
|
||||||
|
|
||||||
.. |development| image:: https://img.shields.io/matrix/synapse-dev:matrix.org?label=development&logo=matrix
|
.. |development| image:: https://img.shields.io/matrix/synapse-dev:matrix.org?label=development&logo=matrix
|
||||||
|
|
85
debian/changelog
vendored
85
debian/changelog
vendored
|
@ -1,3 +1,88 @@
|
||||||
|
matrix-synapse-py3 (1.110.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* `register_new_matrix_user` now supports a --password-file and a --exists-ok flag.
|
||||||
|
* New Synapse release 1.110.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Wed, 26 Jun 2024 14:07:56 +0200
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.109.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New synapse release 1.109.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 18 Jun 2024 09:45:15 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.109.0~rc3) stable; urgency=medium
|
||||||
|
|
||||||
|
* New synapse release 1.109.0rc3.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Mon, 17 Jun 2024 12:05:24 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.109.0~rc2) stable; urgency=medium
|
||||||
|
|
||||||
|
* New synapse release 1.109.0rc2.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 11 Jun 2024 13:20:17 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.109.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.109.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 04 Jun 2024 09:42:46 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.108.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.108.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 28 May 2024 11:54:22 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.108.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.108.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 21 May 2024 10:54:13 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.107.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.107.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 14 May 2024 14:15:34 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.107.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.107.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 07 May 2024 16:26:26 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.106.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.106.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 30 Apr 2024 11:51:43 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.106.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.106.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Thu, 25 Apr 2024 15:54:59 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.105.1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.105.1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 23 Apr 2024 15:56:18 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.105.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.105.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 16 Apr 2024 15:53:23 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.105.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.105.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Thu, 11 Apr 2024 12:15:49 +0100
|
||||||
|
|
||||||
matrix-synapse-py3 (1.104.0) stable; urgency=medium
|
matrix-synapse-py3 (1.104.0) stable; urgency=medium
|
||||||
|
|
||||||
* New Synapse release 1.104.0.
|
* New Synapse release 1.104.0.
|
||||||
|
|
11
debian/register_new_matrix_user.ronn
vendored
11
debian/register_new_matrix_user.ronn
vendored
|
@ -31,8 +31,12 @@ A sample YAML file accepted by `register_new_matrix_user` is described below:
|
||||||
Local part of the new user. Will prompt if omitted.
|
Local part of the new user. Will prompt if omitted.
|
||||||
|
|
||||||
* `-p`, `--password`:
|
* `-p`, `--password`:
|
||||||
New password for user. Will prompt if omitted. Supplying the password
|
New password for user. Will prompt if this option and `--password-file` are omitted.
|
||||||
on the command line is not recommended. Use the STDIN instead.
|
Supplying the password on the command line is not recommended.
|
||||||
|
|
||||||
|
* `--password-file`:
|
||||||
|
File containing the new password for user. If set, overrides `--password`.
|
||||||
|
This is a more secure alternative to specifying the password on the command line.
|
||||||
|
|
||||||
* `-a`, `--admin`:
|
* `-a`, `--admin`:
|
||||||
Register new user as an admin. Will prompt if omitted.
|
Register new user as an admin. Will prompt if omitted.
|
||||||
|
@ -44,6 +48,9 @@ A sample YAML file accepted by `register_new_matrix_user` is described below:
|
||||||
Shared secret as defined in server config file. This is an optional
|
Shared secret as defined in server config file. This is an optional
|
||||||
parameter as it can be also supplied via the YAML file.
|
parameter as it can be also supplied via the YAML file.
|
||||||
|
|
||||||
|
* `--exists-ok`:
|
||||||
|
Do not fail if the user already exists. The user account will be not updated in this case.
|
||||||
|
|
||||||
* `server_url`:
|
* `server_url`:
|
||||||
URL of the home server. Defaults to 'https://localhost:8448'.
|
URL of the home server. Defaults to 'https://localhost:8448'.
|
||||||
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
# Minimal makefile for Sphinx documentation
|
|
||||||
#
|
|
||||||
|
|
||||||
# You can set these variables from the command line, and also
|
|
||||||
# from the environment for the first two.
|
|
||||||
SPHINXOPTS ?=
|
|
||||||
SPHINXBUILD ?= sphinx-build
|
|
||||||
SOURCEDIR = .
|
|
||||||
BUILDDIR = _build
|
|
||||||
|
|
||||||
# Put it first so that "make" without argument is like "make help".
|
|
||||||
help:
|
|
||||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
||||||
|
|
||||||
.PHONY: help Makefile
|
|
||||||
|
|
||||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
||||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
||||||
%: Makefile
|
|
||||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
|
@ -1,50 +0,0 @@
|
||||||
# Configuration file for the Sphinx documentation builder.
|
|
||||||
#
|
|
||||||
# For the full list of built-in configuration values, see the documentation:
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
||||||
|
|
||||||
project = "Synapse development"
|
|
||||||
copyright = "2023, The Matrix.org Foundation C.I.C."
|
|
||||||
author = "The Synapse Maintainers and Community"
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
||||||
|
|
||||||
extensions = [
|
|
||||||
"autodoc2",
|
|
||||||
"myst_parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
templates_path = ["_templates"]
|
|
||||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Autodoc2 ----------------------------------------------------
|
|
||||||
|
|
||||||
autodoc2_docstring_parser_regexes = [
|
|
||||||
# this will render all docstrings as 'MyST' Markdown
|
|
||||||
(r".*", "myst"),
|
|
||||||
]
|
|
||||||
|
|
||||||
autodoc2_packages = [
|
|
||||||
{
|
|
||||||
"path": "../synapse",
|
|
||||||
# Don't render documentation for everything as a matter of course
|
|
||||||
"auto_mode": False,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for MyST (Markdown) ---------------------------------------------
|
|
||||||
|
|
||||||
# myst_heading_anchors = 2
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
||||||
|
|
||||||
html_theme = "furo"
|
|
||||||
html_static_path = ["_static"]
|
|
|
@ -1,22 +0,0 @@
|
||||||
.. Synapse Developer Documentation documentation master file, created by
|
|
||||||
sphinx-quickstart on Mon Mar 13 08:59:51 2023.
|
|
||||||
You can adapt this file completely to your liking, but it should at least
|
|
||||||
contain the root `toctree` directive.
|
|
||||||
|
|
||||||
Welcome to the Synapse Developer Documentation!
|
|
||||||
===========================================================
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
:caption: Contents:
|
|
||||||
|
|
||||||
modules/federation_sender
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
|
||||||
==================
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`modindex`
|
|
||||||
* :ref:`search`
|
|
|
@ -1,5 +0,0 @@
|
||||||
Federation Sender
|
|
||||||
=================
|
|
||||||
|
|
||||||
```{autodoc2-docstring} synapse.federation.sender
|
|
||||||
```
|
|
|
@ -163,7 +163,7 @@ FROM docker.io/library/python:${PYTHON_VERSION}-slim-bookworm
|
||||||
LABEL org.opencontainers.image.url='https://matrix.org/docs/projects/server/synapse'
|
LABEL org.opencontainers.image.url='https://matrix.org/docs/projects/server/synapse'
|
||||||
LABEL org.opencontainers.image.documentation='https://github.com/element-hq/synapse/blob/master/docker/README.md'
|
LABEL org.opencontainers.image.documentation='https://github.com/element-hq/synapse/blob/master/docker/README.md'
|
||||||
LABEL org.opencontainers.image.source='https://github.com/element-hq/synapse.git'
|
LABEL org.opencontainers.image.source='https://github.com/element-hq/synapse.git'
|
||||||
LABEL org.opencontainers.image.licenses='Apache-2.0'
|
LABEL org.opencontainers.image.licenses='AGPL-3.0-or-later'
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
|
|
|
@ -92,8 +92,6 @@ allow_device_name_lookup_over_federation: true
|
||||||
## Experimental Features ##
|
## Experimental Features ##
|
||||||
|
|
||||||
experimental_features:
|
experimental_features:
|
||||||
# client-side support for partial state in /send_join responses
|
|
||||||
faster_joins: true
|
|
||||||
# Enable support for polls
|
# Enable support for polls
|
||||||
msc3381_polls_enabled: true
|
msc3381_polls_enabled: true
|
||||||
# Enable deleting device-specific notification settings stored in account data
|
# Enable deleting device-specific notification settings stored in account data
|
||||||
|
@ -102,6 +100,10 @@ experimental_features:
|
||||||
msc3391_enabled: true
|
msc3391_enabled: true
|
||||||
# Filtering /messages by relation type.
|
# Filtering /messages by relation type.
|
||||||
msc3874_enabled: true
|
msc3874_enabled: true
|
||||||
|
# no UIA for x-signing upload for the first time
|
||||||
|
msc3967_enabled: true
|
||||||
|
# Expose a room summary for public rooms
|
||||||
|
msc3266_enabled: true
|
||||||
|
|
||||||
server_notices:
|
server_notices:
|
||||||
system_mxid_localpart: _server
|
system_mxid_localpart: _server
|
||||||
|
|
|
@ -176,7 +176,6 @@ app_service_config_files:
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
macaroon_secret_key: "{{ SYNAPSE_MACAROON_SECRET_KEY }}"
|
macaroon_secret_key: "{{ SYNAPSE_MACAROON_SECRET_KEY }}"
|
||||||
expire_access_token: False
|
|
||||||
|
|
||||||
## Signing Keys ##
|
## Signing Keys ##
|
||||||
|
|
||||||
|
|
|
@ -211,6 +211,8 @@ WORKERS_CONFIG: Dict[str, Dict[str, Any]] = {
|
||||||
"^/_matrix/federation/(v1|v2)/make_leave/",
|
"^/_matrix/federation/(v1|v2)/make_leave/",
|
||||||
"^/_matrix/federation/(v1|v2)/send_join/",
|
"^/_matrix/federation/(v1|v2)/send_join/",
|
||||||
"^/_matrix/federation/(v1|v2)/send_leave/",
|
"^/_matrix/federation/(v1|v2)/send_leave/",
|
||||||
|
"^/_matrix/federation/v1/make_knock/",
|
||||||
|
"^/_matrix/federation/v1/send_knock/",
|
||||||
"^/_matrix/federation/(v1|v2)/invite/",
|
"^/_matrix/federation/(v1|v2)/invite/",
|
||||||
"^/_matrix/federation/(v1|v2)/query_auth/",
|
"^/_matrix/federation/(v1|v2)/query_auth/",
|
||||||
"^/_matrix/federation/(v1|v2)/event_auth/",
|
"^/_matrix/federation/(v1|v2)/event_auth/",
|
||||||
|
@ -310,6 +312,13 @@ WORKERS_CONFIG: Dict[str, Dict[str, Any]] = {
|
||||||
"shared_extra_conf": {},
|
"shared_extra_conf": {},
|
||||||
"worker_extra_conf": "",
|
"worker_extra_conf": "",
|
||||||
},
|
},
|
||||||
|
"push_rules": {
|
||||||
|
"app": "synapse.app.generic_worker",
|
||||||
|
"listener_resources": ["client", "replication"],
|
||||||
|
"endpoint_patterns": ["^/_matrix/client/(api/v1|r0|v3|unstable)/pushrules/"],
|
||||||
|
"shared_extra_conf": {},
|
||||||
|
"worker_extra_conf": "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Templates for sections that may be inserted multiple times in config files
|
# Templates for sections that may be inserted multiple times in config files
|
||||||
|
@ -401,6 +410,7 @@ def add_worker_roles_to_shared_config(
|
||||||
"receipts",
|
"receipts",
|
||||||
"to_device",
|
"to_device",
|
||||||
"typing",
|
"typing",
|
||||||
|
"push_rules",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Worker-type specific sharding config. Now a single worker can fulfill multiple
|
# Worker-type specific sharding config. Now a single worker can fulfill multiple
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Edit Room Membership API
|
# Edit Room Membership API
|
||||||
|
|
||||||
This API allows an administrator to join an user account with a given `user_id`
|
This API allows an administrator to join a user account with a given `user_id`
|
||||||
to a room with a given `room_id_or_alias`. You can only modify the membership of
|
to a room with a given `room_id_or_alias`. You can only modify the membership of
|
||||||
local users. The server administrator must be in the room and have permission to
|
local users. The server administrator must be in the room and have permission to
|
||||||
invite users.
|
invite users.
|
||||||
|
|
|
@ -36,6 +36,10 @@ The following query parameters are available:
|
||||||
- the room's name,
|
- the room's name,
|
||||||
- the local part of the room's canonical alias, or
|
- the local part of the room's canonical alias, or
|
||||||
- the complete (local and server part) room's id (case sensitive).
|
- the complete (local and server part) room's id (case sensitive).
|
||||||
|
* `public_rooms` - Optional flag to filter public rooms. If `true`, only public rooms are queried. If `false`, public rooms are excluded from
|
||||||
|
the query. When the flag is absent (the default), **both** public and non-public rooms are included in the search results.
|
||||||
|
* `empty_rooms` - Optional flag to filter empty rooms. A room is empty if joined_members is zero. If `true`, only empty rooms are queried. If `false`, empty rooms are excluded from
|
||||||
|
the query. When the flag is absent (the default), **both** empty and non-empty rooms are included in the search results.
|
||||||
|
|
||||||
Defaults to no filtering.
|
Defaults to no filtering.
|
||||||
|
|
||||||
|
|
|
@ -141,8 +141,8 @@ Body parameters:
|
||||||
provider for SSO (Single sign-on). More details are in the configuration manual under the
|
provider for SSO (Single sign-on). More details are in the configuration manual under the
|
||||||
sections [sso](../usage/configuration/config_documentation.md#sso) and [oidc_providers](../usage/configuration/config_documentation.md#oidc_providers).
|
sections [sso](../usage/configuration/config_documentation.md#sso) and [oidc_providers](../usage/configuration/config_documentation.md#oidc_providers).
|
||||||
- `auth_provider` - **string**, required. The unique, internal ID of the external identity provider.
|
- `auth_provider` - **string**, required. The unique, internal ID of the external identity provider.
|
||||||
The same as `idp_id` from the homeserver configuration. Note that no error is raised if the
|
The same as `idp_id` from the homeserver configuration. If using OIDC, this value should be prefixed
|
||||||
provided value is not in the homeserver configuration.
|
with `oidc-`. Note that no error is raised if the provided value is not in the homeserver configuration.
|
||||||
- `external_id` - **string**, required. An identifier for the user in the external identity provider.
|
- `external_id` - **string**, required. An identifier for the user in the external identity provider.
|
||||||
When the user logs in to the identity provider, this must be the unique ID that they map to.
|
When the user logs in to the identity provider, this must be the unique ID that they map to.
|
||||||
- `admin` - **bool**, optional, defaults to `false`. Whether the user is a homeserver administrator,
|
- `admin` - **bool**, optional, defaults to `false`. Whether the user is a homeserver administrator,
|
||||||
|
|
|
@ -86,6 +86,8 @@ poetry install --extras all
|
||||||
This will install the runtime and developer dependencies for the project. Be sure to check
|
This will install the runtime and developer dependencies for the project. Be sure to check
|
||||||
that the `poetry install` step completed cleanly.
|
that the `poetry install` step completed cleanly.
|
||||||
|
|
||||||
|
For OSX users, be sure to set `PKG_CONFIG_PATH` to support `icu4c`. Run `brew info icu4c` for more details.
|
||||||
|
|
||||||
## Running Synapse via poetry
|
## Running Synapse via poetry
|
||||||
|
|
||||||
To start a local instance of Synapse in the locked poetry environment, create a config file:
|
To start a local instance of Synapse in the locked poetry environment, create a config file:
|
||||||
|
|
|
@ -7,8 +7,10 @@ follow the semantics described in
|
||||||
and allow server and room admins to configure how long messages should
|
and allow server and room admins to configure how long messages should
|
||||||
be kept in a homeserver's database before being purged from it.
|
be kept in a homeserver's database before being purged from it.
|
||||||
**Please note that, as this feature isn't part of the Matrix
|
**Please note that, as this feature isn't part of the Matrix
|
||||||
specification yet, this implementation is to be considered as
|
specification yet, the use of `m.room.retention` events for per-room
|
||||||
experimental.**
|
retention policies is to be considered as experimental. However, the use
|
||||||
|
of a default message retention policy is considered a stable feature
|
||||||
|
in Synapse.**
|
||||||
|
|
||||||
A message retention policy is mainly defined by its `max_lifetime`
|
A message retention policy is mainly defined by its `max_lifetime`
|
||||||
parameter, which defines how long a message can be kept around after
|
parameter, which defines how long a message can be kept around after
|
||||||
|
@ -49,8 +51,8 @@ clients.
|
||||||
|
|
||||||
## Server configuration
|
## Server configuration
|
||||||
|
|
||||||
Support for this feature can be enabled and configured by adding a the
|
Support for this feature can be enabled and configured by adding the
|
||||||
`retention` in the Synapse configuration file (see
|
`retention` option in the Synapse configuration file (see
|
||||||
[configuration manual](usage/configuration/config_documentation.md#retention)).
|
[configuration manual](usage/configuration/config_documentation.md#retention)).
|
||||||
|
|
||||||
To enable support for message retention policies, set the setting
|
To enable support for message retention policies, set the setting
|
||||||
|
@ -115,7 +117,7 @@ In this example, we define three jobs:
|
||||||
policy's `max_lifetime` is greater than a week.
|
policy's `max_lifetime` is greater than a week.
|
||||||
|
|
||||||
Note that this example is tailored to show different configurations and
|
Note that this example is tailored to show different configurations and
|
||||||
features slightly more jobs than it's probably necessary (in practice, a
|
features slightly more jobs than is probably necessary (in practice, a
|
||||||
server admin would probably consider it better to replace the two last
|
server admin would probably consider it better to replace the two last
|
||||||
jobs with one that runs once a day and handles rooms which
|
jobs with one that runs once a day and handles rooms which
|
||||||
policy's `max_lifetime` is greater than 3 days).
|
policy's `max_lifetime` is greater than 3 days).
|
||||||
|
|
|
@ -525,6 +525,8 @@ oidc_providers:
|
||||||
(`Options > Security > ID Token signature algorithm` and `Options > Security >
|
(`Options > Security > ID Token signature algorithm` and `Options > Security >
|
||||||
Access Token signature algorithm`)
|
Access Token signature algorithm`)
|
||||||
- Scopes: OpenID, Email and Profile
|
- Scopes: OpenID, Email and Profile
|
||||||
|
- Force claims into `id_token`
|
||||||
|
(`Options > Advanced > Force claims to be returned in ID Token`)
|
||||||
- Allowed redirection addresses for login (`Options > Basic > Allowed
|
- Allowed redirection addresses for login (`Options > Basic > Allowed
|
||||||
redirection addresses for login` ) :
|
redirection addresses for login` ) :
|
||||||
`[synapse public baseurl]/_synapse/client/oidc/callback`
|
`[synapse public baseurl]/_synapse/client/oidc/callback`
|
||||||
|
|
|
@ -128,7 +128,7 @@ can read more about that [here](https://www.postgresql.org/docs/10/kernel-resour
|
||||||
### Overview
|
### Overview
|
||||||
|
|
||||||
The script `synapse_port_db` allows porting an existing synapse server
|
The script `synapse_port_db` allows porting an existing synapse server
|
||||||
backed by SQLite to using PostgreSQL. This is done in as a two phase
|
backed by SQLite to using PostgreSQL. This is done as a two phase
|
||||||
process:
|
process:
|
||||||
|
|
||||||
1. Copy the existing SQLite database to a separate location and run
|
1. Copy the existing SQLite database to a separate location and run
|
||||||
|
@ -242,12 +242,11 @@ host all all ::1/128 ident
|
||||||
|
|
||||||
### Fixing incorrect `COLLATE` or `CTYPE`
|
### Fixing incorrect `COLLATE` or `CTYPE`
|
||||||
|
|
||||||
Synapse will refuse to set up a new database if it has the wrong values of
|
Synapse will refuse to start when using a database with incorrect values of
|
||||||
`COLLATE` and `CTYPE` set. Synapse will also refuse to start an existing database with incorrect values
|
`COLLATE` and `CTYPE` unless the config flag `allow_unsafe_locale`, found in the
|
||||||
of `COLLATE` and `CTYPE` unless the config flag `allow_unsafe_locale`, found in the
|
`database` section of the config, is set to true. Using different locales can
|
||||||
`database` section of the config, is set to true. Using different locales can cause issues if the locale library is updated from
|
cause issues if the locale library is updated from underneath the database, or
|
||||||
underneath the database, or if a different version of the locale is used on any
|
if a different version of the locale is used on any replicas.
|
||||||
replicas.
|
|
||||||
|
|
||||||
If you have a database with an unsafe locale, the safest way to fix the issue is to dump the database and recreate it with
|
If you have a database with an unsafe locale, the safest way to fix the issue is to dump the database and recreate it with
|
||||||
the correct locale parameter (as shown above). It is also possible to change the
|
the correct locale parameter (as shown above). It is also possible to change the
|
||||||
|
@ -256,13 +255,3 @@ however extreme care must be taken to avoid database corruption.
|
||||||
|
|
||||||
Note that the above may fail with an error about duplicate rows if corruption
|
Note that the above may fail with an error about duplicate rows if corruption
|
||||||
has already occurred, and such duplicate rows will need to be manually removed.
|
has already occurred, and such duplicate rows will need to be manually removed.
|
||||||
|
|
||||||
### Fixing inconsistent sequences error
|
|
||||||
|
|
||||||
Synapse uses Postgres sequences to generate IDs for various tables. A sequence
|
|
||||||
and associated table can get out of sync if, for example, Synapse has been
|
|
||||||
downgraded and then upgraded again.
|
|
||||||
|
|
||||||
To fix the issue shut down Synapse (including any and all workers) and run the
|
|
||||||
SQL command included in the error message. Once done Synapse should start
|
|
||||||
successfully.
|
|
||||||
|
|
|
@ -259,9 +259,9 @@ users, etc.) to the developers via the `--report-stats` argument.
|
||||||
|
|
||||||
This command will generate you a config file that you can then customise, but it will
|
This command will generate you a config file that you can then customise, but it will
|
||||||
also generate a set of keys for you. These keys will allow your homeserver to
|
also generate a set of keys for you. These keys will allow your homeserver to
|
||||||
identify itself to other homeserver, so don't lose or delete them. It would be
|
identify itself to other homeservers, so don't lose or delete them. It would be
|
||||||
wise to back them up somewhere safe. (If, for whatever reason, you do need to
|
wise to back them up somewhere safe. (If, for whatever reason, you do need to
|
||||||
change your homeserver's keys, you may find that other homeserver have the
|
change your homeserver's keys, you may find that other homeservers have the
|
||||||
old key cached. If you update the signing key, you should change the name of the
|
old key cached. If you update the signing key, you should change the name of the
|
||||||
key in the `<server name>.signing.key` file (the second word) to something
|
key in the `<server name>.signing.key` file (the second word) to something
|
||||||
different. See the [spec](https://matrix.org/docs/spec/server_server/latest.html#retrieving-server-keys) for more information on key management).
|
different. See the [spec](https://matrix.org/docs/spec/server_server/latest.html#retrieving-server-keys) for more information on key management).
|
||||||
|
|
|
@ -98,6 +98,7 @@ A custom mapping provider must specify the following methods:
|
||||||
either accept this localpart or pick their own username. Otherwise this
|
either accept this localpart or pick their own username. Otherwise this
|
||||||
option has no effect. If omitted, defaults to `False`.
|
option has no effect. If omitted, defaults to `False`.
|
||||||
- `display_name`: An optional string, the display name for the user.
|
- `display_name`: An optional string, the display name for the user.
|
||||||
|
- `picture`: An optional string, the avatar url for the user.
|
||||||
- `emails`: A list of strings, the email address(es) to associate with
|
- `emails`: A list of strings, the email address(es) to associate with
|
||||||
this user. If omitted, defaults to an empty list.
|
this user. If omitted, defaults to an empty list.
|
||||||
* `async def get_extra_attributes(self, userinfo, token)`
|
* `async def get_extra_attributes(self, userinfo, token)`
|
||||||
|
|
|
@ -9,6 +9,7 @@ ReloadPropagatedFrom=matrix-synapse.target
|
||||||
Type=notify
|
Type=notify
|
||||||
NotifyAccess=main
|
NotifyAccess=main
|
||||||
User=matrix-synapse
|
User=matrix-synapse
|
||||||
|
RuntimeDirectory=synapse
|
||||||
WorkingDirectory=/var/lib/matrix-synapse
|
WorkingDirectory=/var/lib/matrix-synapse
|
||||||
EnvironmentFile=-/etc/default/matrix-synapse
|
EnvironmentFile=-/etc/default/matrix-synapse
|
||||||
ExecStartPre=/opt/venvs/matrix-synapse/bin/python -m synapse.app.homeserver --config-path=/etc/matrix-synapse/homeserver.yaml --config-path=/etc/matrix-synapse/conf.d/ --generate-keys
|
ExecStartPre=/opt/venvs/matrix-synapse/bin/python -m synapse.app.homeserver --config-path=/etc/matrix-synapse/homeserver.yaml --config-path=/etc/matrix-synapse/conf.d/ --generate-keys
|
||||||
|
|
|
@ -117,6 +117,14 @@ each upgrade are complete before moving on to the next upgrade, to avoid
|
||||||
stacking them up. You can monitor the currently running background updates with
|
stacking them up. You can monitor the currently running background updates with
|
||||||
[the Admin API](usage/administration/admin_api/background_updates.html#status).
|
[the Admin API](usage/administration/admin_api/background_updates.html#status).
|
||||||
|
|
||||||
|
# Upgrading to v1.106.0
|
||||||
|
|
||||||
|
## Minimum supported Rust version
|
||||||
|
The minimum supported Rust version has been increased from v1.65.0 to v1.66.0.
|
||||||
|
Users building from source will need to ensure their `rustc` version is up to
|
||||||
|
date.
|
||||||
|
|
||||||
|
|
||||||
# Upgrading to v1.100.0
|
# Upgrading to v1.100.0
|
||||||
|
|
||||||
## Minimum supported Rust version
|
## Minimum supported Rust version
|
||||||
|
|
|
@ -44,7 +44,7 @@ For each update:
|
||||||
|
|
||||||
## Enabled
|
## Enabled
|
||||||
|
|
||||||
This API allow pausing background updates.
|
This API allows pausing background updates.
|
||||||
|
|
||||||
Background updates should *not* be paused for significant periods of time, as
|
Background updates should *not* be paused for significant periods of time, as
|
||||||
this can affect the performance of Synapse.
|
this can affect the performance of Synapse.
|
||||||
|
|
|
@ -241,7 +241,7 @@ in memory constrained environments, or increased if performance starts to
|
||||||
degrade.
|
degrade.
|
||||||
|
|
||||||
However, degraded performance due to a low cache factor, common on
|
However, degraded performance due to a low cache factor, common on
|
||||||
machines with slow disks, often leads to explosions in memory use due
|
machines with slow disks, often leads to explosions in memory use due to
|
||||||
backlogged requests. In this case, reducing the cache factor will make
|
backlogged requests. In this case, reducing the cache factor will make
|
||||||
things worse. Instead, try increasing it drastically. 2.0 is a good
|
things worse. Instead, try increasing it drastically. 2.0 is a good
|
||||||
starting value.
|
starting value.
|
||||||
|
@ -250,10 +250,10 @@ Using [libjemalloc](https://jemalloc.net) can also yield a significant
|
||||||
improvement in overall memory use, and especially in terms of giving back
|
improvement in overall memory use, and especially in terms of giving back
|
||||||
RAM to the OS. To use it, the library must simply be put in the
|
RAM to the OS. To use it, the library must simply be put in the
|
||||||
LD_PRELOAD environment variable when launching Synapse. On Debian, this
|
LD_PRELOAD environment variable when launching Synapse. On Debian, this
|
||||||
can be done by installing the `libjemalloc1` package and adding this
|
can be done by installing the `libjemalloc2` package and adding this
|
||||||
line to `/etc/default/matrix-synapse`:
|
line to `/etc/default/matrix-synapse`:
|
||||||
|
|
||||||
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.1
|
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
|
||||||
|
|
||||||
This made a significant difference on Python 2.7 - it's unclear how
|
This made a significant difference on Python 2.7 - it's unclear how
|
||||||
much of an improvement it provides on Python 3.x.
|
much of an improvement it provides on Python 3.x.
|
||||||
|
|
|
@ -676,8 +676,8 @@ This setting has the following sub-options:
|
||||||
trailing 's'.
|
trailing 's'.
|
||||||
* `app_name`: `app_name` defines the default value for '%(app)s' in `notif_from` and email
|
* `app_name`: `app_name` defines the default value for '%(app)s' in `notif_from` and email
|
||||||
subjects. It defaults to 'Matrix'.
|
subjects. It defaults to 'Matrix'.
|
||||||
* `enable_notifs`: Set to true to enable sending emails for messages that the user
|
* `enable_notifs`: Set to true to allow users to receive e-mail notifications. If this is not set,
|
||||||
has missed. Disabled by default.
|
users can configure e-mail notifications but will not receive them. Disabled by default.
|
||||||
* `notif_for_new_users`: Set to false to disable automatic subscription to email
|
* `notif_for_new_users`: Set to false to disable automatic subscription to email
|
||||||
notifications for new users. Enabled by default.
|
notifications for new users. Enabled by default.
|
||||||
* `notif_delay_before_mail`: The time to wait before emailing about a notification.
|
* `notif_delay_before_mail`: The time to wait before emailing about a notification.
|
||||||
|
@ -1232,6 +1232,31 @@ federation_domain_whitelist:
|
||||||
- syd.example.com
|
- syd.example.com
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
### `federation_whitelist_endpoint_enabled`
|
||||||
|
|
||||||
|
Enables an endpoint for fetching the federation whitelist config.
|
||||||
|
|
||||||
|
The request method and path is `GET /_synapse/client/v1/config/federation_whitelist`, and the
|
||||||
|
response format is:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"whitelist_enabled": true, // Whether the federation whitelist is being enforced
|
||||||
|
"whitelist": [ // Which server names are allowed by the whitelist
|
||||||
|
"example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If `whitelist_enabled` is `false` then the server is permitted to federate with all others.
|
||||||
|
|
||||||
|
The endpoint requires authentication.
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
```yaml
|
||||||
|
federation_whitelist_endpoint_enabled: true
|
||||||
|
```
|
||||||
|
---
|
||||||
### `federation_metrics_domains`
|
### `federation_metrics_domains`
|
||||||
|
|
||||||
Report prometheus metrics on the age of PDUs being sent to and received from
|
Report prometheus metrics on the age of PDUs being sent to and received from
|
||||||
|
@ -1317,6 +1342,12 @@ Options related to caching.
|
||||||
The number of events to cache in memory. Defaults to 10K. Like other caches,
|
The number of events to cache in memory. Defaults to 10K. Like other caches,
|
||||||
this is affected by `caches.global_factor` (see below).
|
this is affected by `caches.global_factor` (see below).
|
||||||
|
|
||||||
|
For example, the default is 10K and the global_factor default is 0.5.
|
||||||
|
|
||||||
|
Since 10K * 0.5 is 5K then the event cache size will be 5K.
|
||||||
|
|
||||||
|
The cache affected by this configuration is named as "*getEvent*".
|
||||||
|
|
||||||
Note that this option is not part of the `caches` section.
|
Note that this option is not part of the `caches` section.
|
||||||
|
|
||||||
Example configuration:
|
Example configuration:
|
||||||
|
@ -1342,6 +1373,8 @@ number of entries that can be stored.
|
||||||
|
|
||||||
Defaults to 0.5, which will halve the size of all caches.
|
Defaults to 0.5, which will halve the size of all caches.
|
||||||
|
|
||||||
|
Note that changing this value also affects the HTTP connection pool.
|
||||||
|
|
||||||
* `per_cache_factors`: A dictionary of cache name to cache factor for that individual
|
* `per_cache_factors`: A dictionary of cache name to cache factor for that individual
|
||||||
cache. Overrides the global cache factor for a given cache.
|
cache. Overrides the global cache factor for a given cache.
|
||||||
|
|
||||||
|
@ -1726,8 +1759,9 @@ rc_3pid_validation:
|
||||||
### `rc_invites`
|
### `rc_invites`
|
||||||
|
|
||||||
This option sets ratelimiting how often invites can be sent in a room or to a
|
This option sets ratelimiting how often invites can be sent in a room or to a
|
||||||
specific user. `per_room` defaults to `per_second: 0.3`, `burst_count: 10` and
|
specific user. `per_room` defaults to `per_second: 0.3`, `burst_count: 10`,
|
||||||
`per_user` defaults to `per_second: 0.003`, `burst_count: 5`.
|
`per_user` defaults to `per_second: 0.003`, `burst_count: 5`, and `per_issuer`
|
||||||
|
defaults to `per_second: 0.3`, `burst_count: 10`.
|
||||||
|
|
||||||
Client requests that invite user(s) when [creating a
|
Client requests that invite user(s) when [creating a
|
||||||
room](https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom)
|
room](https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom)
|
||||||
|
@ -1913,6 +1947,24 @@ Example configuration:
|
||||||
max_image_pixels: 35M
|
max_image_pixels: 35M
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
### `remote_media_download_burst_count`
|
||||||
|
|
||||||
|
Remote media downloads are ratelimited using a [leaky bucket algorithm](https://en.wikipedia.org/wiki/Leaky_bucket), where a given "bucket" is keyed to the IP address of the requester when requesting remote media downloads. This configuration option sets the size of the bucket against which the size in bytes of downloads are penalized - if the bucket is full, ie a given number of bytes have already been downloaded, further downloads will be denied until the bucket drains. Defaults to 500MiB. See also `remote_media_download_per_second` which determines the rate at which the "bucket" is emptied and thus has available space to authorize new requests.
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
```yaml
|
||||||
|
remote_media_download_burst_count: 200M
|
||||||
|
```
|
||||||
|
---
|
||||||
|
### `remote_media_download_per_second`
|
||||||
|
|
||||||
|
Works in conjunction with `remote_media_download_burst_count` to ratelimit remote media downloads - this configuration option determines the rate at which the "bucket" (see above) leaks in bytes per second. As requests are made to download remote media, the size of those requests in bytes is added to the bucket, and once the bucket has reached it's capacity, no more requests will be allowed until a number of bytes has "drained" from the bucket. This setting determines the rate at which bytes drain from the bucket, with the practical effect that the larger the number, the faster the bucket leaks, allowing for more bytes downloaded over a shorter period of time. Defaults to 87KiB per second. See also `remote_media_download_burst_count`.
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
```yaml
|
||||||
|
remote_media_download_per_second: 40K
|
||||||
|
```
|
||||||
|
---
|
||||||
### `prevent_media_downloads_from`
|
### `prevent_media_downloads_from`
|
||||||
|
|
||||||
A list of domains to never download media from. Media from these
|
A list of domains to never download media from. Media from these
|
||||||
|
@ -2583,6 +2635,11 @@ Possible values for this option are:
|
||||||
* "trusted_private_chat": an invitation is required to join this room and the invitee is
|
* "trusted_private_chat": an invitation is required to join this room and the invitee is
|
||||||
assigned a power level of 100 upon joining the room.
|
assigned a power level of 100 upon joining the room.
|
||||||
|
|
||||||
|
Each preset will set up a room in the same manner as if it were provided as the `preset` parameter when
|
||||||
|
calling the
|
||||||
|
[`POST /_matrix/client/v3/createRoom`](https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3createroom)
|
||||||
|
Client-Server API endpoint.
|
||||||
|
|
||||||
If a value of "private_chat" or "trusted_private_chat" is used then
|
If a value of "private_chat" or "trusted_private_chat" is used then
|
||||||
`auto_join_mxid_localpart` must also be configured.
|
`auto_join_mxid_localpart` must also be configured.
|
||||||
|
|
||||||
|
@ -2662,7 +2719,7 @@ Example configuration:
|
||||||
session_lifetime: 24h
|
session_lifetime: 24h
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
### `refresh_access_token_lifetime`
|
### `refreshable_access_token_lifetime`
|
||||||
|
|
||||||
Time that an access token remains valid for, if the session is using refresh tokens.
|
Time that an access token remains valid for, if the session is using refresh tokens.
|
||||||
|
|
||||||
|
@ -3520,6 +3577,15 @@ Has the following sub-options:
|
||||||
users. This allows the CAS SSO flow to be limited to sign in only, rather than
|
users. This allows the CAS SSO flow to be limited to sign in only, rather than
|
||||||
automatically registering users that have a valid SSO login but do not have
|
automatically registering users that have a valid SSO login but do not have
|
||||||
a pre-registered account. Defaults to true.
|
a pre-registered account. Defaults to true.
|
||||||
|
* `allow_numeric_ids`: set to 'true' allow numeric user IDs (default false).
|
||||||
|
This allows CAS SSO flow to provide user IDs composed of numbers only.
|
||||||
|
These identifiers will be prefixed by the letter "u" by default.
|
||||||
|
The prefix can be configured using the "numeric_ids_prefix" option.
|
||||||
|
Be careful to choose the prefix correctly to avoid any possible conflicts
|
||||||
|
(e.g. user 1234 becomes u1234 when a user u1234 already exists).
|
||||||
|
* `numeric_ids_prefix`: the prefix you wish to add in front of a numeric user ID
|
||||||
|
when the "allow_numeric_ids" option is set to "true".
|
||||||
|
By default, the prefix is the letter "u" and only alphanumeric characters are allowed.
|
||||||
|
|
||||||
*Added in Synapse 1.93.0.*
|
*Added in Synapse 1.93.0.*
|
||||||
|
|
||||||
|
@ -3534,6 +3600,8 @@ cas_config:
|
||||||
userGroup: "staff"
|
userGroup: "staff"
|
||||||
department: None
|
department: None
|
||||||
enable_registration: true
|
enable_registration: true
|
||||||
|
allow_numeric_ids: true
|
||||||
|
numeric_ids_prefix: "numericuser"
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
### `sso`
|
### `sso`
|
||||||
|
@ -3739,7 +3807,8 @@ This setting defines options related to the user directory.
|
||||||
This option has the following sub-options:
|
This option has the following sub-options:
|
||||||
* `enabled`: Defines whether users can search the user directory. If false then
|
* `enabled`: Defines whether users can search the user directory. If false then
|
||||||
empty responses are returned to all queries. Defaults to true.
|
empty responses are returned to all queries. Defaults to true.
|
||||||
* `search_all_users`: Defines whether to search all users visible to your HS at the time the search is performed. If set to true, will return all users who share a room with the user from the homeserver.
|
* `search_all_users`: Defines whether to search all users visible to your homeserver at the time the search is performed.
|
||||||
|
If set to true, will return all users known to the homeserver matching the search query.
|
||||||
If false, search results will only contain users
|
If false, search results will only contain users
|
||||||
visible in public rooms and users sharing a room with the requester.
|
visible in public rooms and users sharing a room with the requester.
|
||||||
Defaults to false.
|
Defaults to false.
|
||||||
|
@ -4083,7 +4152,7 @@ By default, no room is excluded.
|
||||||
Example configuration:
|
Example configuration:
|
||||||
```yaml
|
```yaml
|
||||||
exclude_rooms_from_sync:
|
exclude_rooms_from_sync:
|
||||||
- !foo:example.com
|
- "!foo:example.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -4546,3 +4615,32 @@ background_updates:
|
||||||
min_batch_size: 10
|
min_batch_size: 10
|
||||||
default_batch_size: 50
|
default_batch_size: 50
|
||||||
```
|
```
|
||||||
|
---
|
||||||
|
## Auto Accept Invites
|
||||||
|
Configuration settings related to automatically accepting invites.
|
||||||
|
|
||||||
|
---
|
||||||
|
### `auto_accept_invites`
|
||||||
|
|
||||||
|
Automatically accepting invites controls whether users are presented with an invite request or if they
|
||||||
|
are instead automatically joined to a room when receiving an invite. Set the `enabled` sub-option to true to
|
||||||
|
enable auto-accepting invites. Defaults to false.
|
||||||
|
This setting has the following sub-options:
|
||||||
|
* `enabled`: Whether to run the auto-accept invites logic. Defaults to false.
|
||||||
|
* `only_for_direct_messages`: Whether invites should be automatically accepted for all room types, or only
|
||||||
|
for direct messages. Defaults to false.
|
||||||
|
* `only_from_local_users`: Whether to only automatically accept invites from users on this homeserver. Defaults to false.
|
||||||
|
* `worker_to_run_on`: Which worker to run this module on. This must match the "worker_name".
|
||||||
|
|
||||||
|
NOTE: Care should be taken not to enable this setting if the `synapse_auto_accept_invite` module is enabled and installed.
|
||||||
|
The two modules will compete to perform the same task and may result in undesired behaviour. For example, multiple join
|
||||||
|
events could be generated from a single invite.
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
```yaml
|
||||||
|
auto_accept_invites:
|
||||||
|
enabled: true
|
||||||
|
only_for_direct_messages: true
|
||||||
|
only_from_local_users: true
|
||||||
|
worker_to_run_on: "worker_1"
|
||||||
|
```
|
||||||
|
|
|
@ -86,9 +86,9 @@ The search term is then split into words:
|
||||||
* If unavailable, then runs of ASCII characters, numbers, underscores, and hyphens
|
* If unavailable, then runs of ASCII characters, numbers, underscores, and hyphens
|
||||||
are considered words.
|
are considered words.
|
||||||
|
|
||||||
The queries for PostgreSQL and SQLite are detailed below, by their overall goal
|
The queries for PostgreSQL and SQLite are detailed below, but their overall goal
|
||||||
is to find matching users, preferring users who are "real" (e.g. not bots,
|
is to find matching users, preferring users who are "real" (e.g. not bots,
|
||||||
not deactivated). It is assumed that real users will have an display name and
|
not deactivated). It is assumed that real users will have a display name and
|
||||||
avatar set.
|
avatar set.
|
||||||
|
|
||||||
### PostgreSQL
|
### PostgreSQL
|
||||||
|
|
|
@ -62,6 +62,6 @@ following documentation:
|
||||||
|
|
||||||
## Reporting a security vulnerability
|
## Reporting a security vulnerability
|
||||||
|
|
||||||
If you've found a security issue in Synapse or any other Matrix.org Foundation
|
If you've found a security issue in Synapse or any other Element project,
|
||||||
project, please report it to us in accordance with our [Security Disclosure
|
please report it to us in accordance with our [Security Disclosure
|
||||||
Policy](https://www.matrix.org/security-disclosure-policy/). Thank you!
|
Policy](https://element.io/security/security-disclosure-policy). Thank you!
|
||||||
|
|
|
@ -211,6 +211,8 @@ information.
|
||||||
^/_matrix/federation/v1/make_leave/
|
^/_matrix/federation/v1/make_leave/
|
||||||
^/_matrix/federation/(v1|v2)/send_join/
|
^/_matrix/federation/(v1|v2)/send_join/
|
||||||
^/_matrix/federation/(v1|v2)/send_leave/
|
^/_matrix/federation/(v1|v2)/send_leave/
|
||||||
|
^/_matrix/federation/v1/make_knock/
|
||||||
|
^/_matrix/federation/v1/send_knock/
|
||||||
^/_matrix/federation/(v1|v2)/invite/
|
^/_matrix/federation/(v1|v2)/invite/
|
||||||
^/_matrix/federation/v1/event_auth/
|
^/_matrix/federation/v1/event_auth/
|
||||||
^/_matrix/federation/v1/timestamp_to_event/
|
^/_matrix/federation/v1/timestamp_to_event/
|
||||||
|
@ -232,7 +234,7 @@ information.
|
||||||
^/_matrix/client/v1/rooms/.*/hierarchy$
|
^/_matrix/client/v1/rooms/.*/hierarchy$
|
||||||
^/_matrix/client/(v1|unstable)/rooms/.*/relations/
|
^/_matrix/client/(v1|unstable)/rooms/.*/relations/
|
||||||
^/_matrix/client/v1/rooms/.*/threads$
|
^/_matrix/client/v1/rooms/.*/threads$
|
||||||
^/_matrix/client/unstable/im.nheko.summary/rooms/.*/summary$
|
^/_matrix/client/unstable/im.nheko.summary/summary/.*$
|
||||||
^/_matrix/client/(r0|v3|unstable)/account/3pid$
|
^/_matrix/client/(r0|v3|unstable)/account/3pid$
|
||||||
^/_matrix/client/(r0|v3|unstable)/account/whoami$
|
^/_matrix/client/(r0|v3|unstable)/account/whoami$
|
||||||
^/_matrix/client/(r0|v3|unstable)/devices$
|
^/_matrix/client/(r0|v3|unstable)/devices$
|
||||||
|
@ -532,6 +534,13 @@ the stream writer for the `presence` stream:
|
||||||
|
|
||||||
^/_matrix/client/(api/v1|r0|v3|unstable)/presence/
|
^/_matrix/client/(api/v1|r0|v3|unstable)/presence/
|
||||||
|
|
||||||
|
##### The `push_rules` stream
|
||||||
|
|
||||||
|
The following endpoints should be routed directly to the worker configured as
|
||||||
|
the stream writer for the `push_rules` stream:
|
||||||
|
|
||||||
|
^/_matrix/client/(api/v1|r0|v3|unstable)/pushrules/
|
||||||
|
|
||||||
#### Restrict outbound federation traffic to a specific set of workers
|
#### Restrict outbound federation traffic to a specific set of workers
|
||||||
|
|
||||||
The
|
The
|
||||||
|
@ -627,7 +636,7 @@ worker application type.
|
||||||
|
|
||||||
#### Push Notifications
|
#### Push Notifications
|
||||||
|
|
||||||
You can designate generic worker to sending push notifications to
|
You can designate generic workers to send push notifications to
|
||||||
a [push gateway](https://spec.matrix.org/v1.5/push-gateway-api/) such as
|
a [push gateway](https://spec.matrix.org/v1.5/push-gateway-api/) such as
|
||||||
[sygnal](https://github.com/matrix-org/sygnal) and email.
|
[sygnal](https://github.com/matrix-org/sygnal) and email.
|
||||||
|
|
||||||
|
|
1558
poetry.lock
generated
1558
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
|
@ -96,7 +96,7 @@ module-name = "synapse.synapse_rust"
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "matrix-synapse"
|
name = "matrix-synapse"
|
||||||
version = "1.104.0"
|
version = "1.110.0rc1"
|
||||||
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,10 +200,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 `ParamSpec` and `Concatenate`, which were added in `typing-extensions` 3.10.0.0.
|
# We use `Self`, which were added in `typing-extensions` 4.0.
|
||||||
# Additionally we need https://github.com/python/typing/pull/817 to allow types to be
|
typing-extensions = ">=4.0"
|
||||||
# generic over ParamSpecs.
|
|
||||||
typing-extensions = ">=3.10.0.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 +319,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.2"
|
ruff = "0.3.7"
|
||||||
# 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"
|
||||||
|
|
||||||
|
@ -364,17 +362,6 @@ towncrier = ">=18.6.0rc1"
|
||||||
tomli = ">=1.2.3"
|
tomli = ">=1.2.3"
|
||||||
|
|
||||||
|
|
||||||
# Dependencies for building the development documentation
|
|
||||||
[tool.poetry.group.dev-docs]
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[tool.poetry.group.dev-docs.dependencies]
|
|
||||||
sphinx = {version = "^6.1", python = "^3.8"}
|
|
||||||
sphinx-autodoc2 = {version = ">=0.4.2,<0.6.0", python = "^3.8"}
|
|
||||||
myst-parser = {version = "^1.0.0", python = "^3.8"}
|
|
||||||
furo = ">=2022.12.7,<2025.0.0"
|
|
||||||
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
# The upper bounds here are defensive, intended to prevent situations like
|
# The upper bounds here are defensive, intended to prevent situations like
|
||||||
# https://github.com/matrix-org/synapse/issues/13849 and
|
# https://github.com/matrix-org/synapse/issues/13849 and
|
||||||
|
|
1170
requirements.txt
Normal file
1170
requirements.txt
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@ name = "synapse"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65.0"
|
rust-version = "1.66.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "synapse"
|
name = "synapse"
|
||||||
|
@ -23,19 +23,26 @@ name = "synapse.synapse_rust"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.63"
|
anyhow = "1.0.63"
|
||||||
|
base64 = "0.21.7"
|
||||||
|
bytes = "1.6.0"
|
||||||
|
headers = "0.4.0"
|
||||||
|
http = "1.1.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
pyo3 = { version = "0.20.0", features = [
|
mime = "0.3.17"
|
||||||
|
pyo3 = { version = "0.21.0", features = [
|
||||||
"macros",
|
"macros",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"abi3",
|
"abi3",
|
||||||
"abi3-py38",
|
"abi3-py38",
|
||||||
] }
|
] }
|
||||||
pyo3-log = "0.9.0"
|
pyo3-log = "0.10.0"
|
||||||
pythonize = "0.20.0"
|
pythonize = "0.21.0"
|
||||||
regex = "1.6.0"
|
regex = "1.6.0"
|
||||||
|
sha2 = "0.10.8"
|
||||||
serde = { version = "1.0.144", features = ["derive"] }
|
serde = { version = "1.0.144", features = ["derive"] }
|
||||||
serde_json = "1.0.85"
|
serde_json = "1.0.85"
|
||||||
|
ulid = "1.1.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
extension-module = ["pyo3/extension-module"]
|
extension-module = ["pyo3/extension-module"]
|
||||||
|
|
|
@ -25,21 +25,21 @@ use std::net::Ipv4Addr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use pyo3::prelude::*;
|
use pyo3::{prelude::*, pybacked::PyBackedStr};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::push::utils::{glob_to_regex, GlobMatchType};
|
use crate::push::utils::{glob_to_regex, GlobMatchType};
|
||||||
|
|
||||||
/// Called when registering modules with python.
|
/// Called when registering modules with python.
|
||||||
pub fn register_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
let child_module = PyModule::new(py, "acl")?;
|
let child_module = PyModule::new_bound(py, "acl")?;
|
||||||
child_module.add_class::<ServerAclEvaluator>()?;
|
child_module.add_class::<ServerAclEvaluator>()?;
|
||||||
|
|
||||||
m.add_submodule(child_module)?;
|
m.add_submodule(&child_module)?;
|
||||||
|
|
||||||
// We need to manually add the module to sys.modules to make `from
|
// We need to manually add the module to sys.modules to make `from
|
||||||
// synapse.synapse_rust import acl` work.
|
// synapse.synapse_rust import acl` work.
|
||||||
py.import("sys")?
|
py.import_bound("sys")?
|
||||||
.getattr("modules")?
|
.getattr("modules")?
|
||||||
.set_item("synapse.synapse_rust.acl", child_module)?;
|
.set_item("synapse.synapse_rust.acl", child_module)?;
|
||||||
|
|
||||||
|
@ -59,8 +59,8 @@ impl ServerAclEvaluator {
|
||||||
#[new]
|
#[new]
|
||||||
pub fn py_new(
|
pub fn py_new(
|
||||||
allow_ip_literals: bool,
|
allow_ip_literals: bool,
|
||||||
allow: Vec<&str>,
|
allow: Vec<PyBackedStr>,
|
||||||
deny: Vec<&str>,
|
deny: Vec<PyBackedStr>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let allow = allow
|
let allow = allow
|
||||||
.iter()
|
.iter()
|
||||||
|
|
60
rust/src/errors.rs
Normal file
60
rust/src/errors.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 New Vector, Ltd
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* See the GNU Affero General Public License for more details:
|
||||||
|
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#![allow(clippy::new_ret_no_self)]
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use http::{HeaderMap, StatusCode};
|
||||||
|
use pyo3::{exceptions::PyValueError, import_exception};
|
||||||
|
|
||||||
|
import_exception!(synapse.api.errors, SynapseError);
|
||||||
|
|
||||||
|
impl SynapseError {
|
||||||
|
pub fn new(
|
||||||
|
code: StatusCode,
|
||||||
|
message: String,
|
||||||
|
errcode: &'static str,
|
||||||
|
additional_fields: Option<HashMap<String, String>>,
|
||||||
|
headers: Option<HeaderMap>,
|
||||||
|
) -> pyo3::PyErr {
|
||||||
|
// Transform the HeaderMap into a HashMap<String, String>
|
||||||
|
let headers = if let Some(headers) = headers {
|
||||||
|
let mut map = HashMap::with_capacity(headers.len());
|
||||||
|
for (key, value) in headers.iter() {
|
||||||
|
let Ok(value) = value.to_str() else {
|
||||||
|
// This should never happen, but we don't want to panic in case it does
|
||||||
|
return PyValueError::new_err(
|
||||||
|
"Could not construct SynapseError: header value is not valid ASCII",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
map.insert(key.as_str().to_owned(), value.to_owned());
|
||||||
|
}
|
||||||
|
Some(map)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
SynapseError::new_err((code.as_u16(), message, errcode, additional_fields, headers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import_exception!(synapse.api.errors, NotFoundError);
|
||||||
|
|
||||||
|
impl NotFoundError {
|
||||||
|
pub fn new() -> pyo3::PyErr {
|
||||||
|
NotFoundError::new_err(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,8 +20,10 @@
|
||||||
|
|
||||||
//! Implements the internal metadata class attached to events.
|
//! Implements the internal metadata class attached to events.
|
||||||
//!
|
//!
|
||||||
//! The internal metadata is a bit like a `TypedDict`, in that it is stored as a
|
//! The internal metadata is a bit like a `TypedDict`, in that most of
|
||||||
//! JSON dict in the DB. Most events have zero, or only a few, of these keys
|
//! it is stored as a JSON dict in the DB (the exceptions being `outlier`
|
||||||
|
//! and `stream_ordering` which have their own columns in the database).
|
||||||
|
//! Most events have zero, or only a few, of these keys
|
||||||
//! set. Therefore, since we care more about memory size than performance here,
|
//! set. Therefore, since we care more about memory size than performance here,
|
||||||
//! we store these fields in a mapping.
|
//! we store these fields in a mapping.
|
||||||
//!
|
//!
|
||||||
|
@ -36,9 +38,10 @@ use anyhow::Context;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use pyo3::{
|
use pyo3::{
|
||||||
exceptions::PyAttributeError,
|
exceptions::PyAttributeError,
|
||||||
|
pybacked::PyBackedStr,
|
||||||
pyclass, pymethods,
|
pyclass, pymethods,
|
||||||
types::{PyDict, PyString},
|
types::{PyAnyMethods, PyDict, PyDictMethods, PyString},
|
||||||
IntoPy, PyAny, PyObject, PyResult, Python,
|
Bound, IntoPy, PyAny, PyObject, PyResult, Python,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Definitions of the various fields of the internal metadata.
|
/// Definitions of the various fields of the internal metadata.
|
||||||
|
@ -57,7 +60,7 @@ enum EventInternalMetadataData {
|
||||||
|
|
||||||
impl EventInternalMetadataData {
|
impl EventInternalMetadataData {
|
||||||
/// Convert the field to its name and python object.
|
/// Convert the field to its name and python object.
|
||||||
fn to_python_pair<'a>(&self, py: Python<'a>) -> (&'a PyString, PyObject) {
|
fn to_python_pair<'a>(&self, py: Python<'a>) -> (&'a Bound<'a, PyString>, PyObject) {
|
||||||
match self {
|
match self {
|
||||||
EventInternalMetadataData::OutOfBandMembership(o) => {
|
EventInternalMetadataData::OutOfBandMembership(o) => {
|
||||||
(pyo3::intern!(py, "out_of_band_membership"), o.into_py(py))
|
(pyo3::intern!(py, "out_of_band_membership"), o.into_py(py))
|
||||||
|
@ -88,10 +91,13 @@ impl EventInternalMetadataData {
|
||||||
/// Converts from python key/values to the field.
|
/// Converts from python key/values to the field.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the key is a valid but unrecognized string.
|
/// Returns `None` if the key is a valid but unrecognized string.
|
||||||
fn from_python_pair(key: &PyAny, value: &PyAny) -> PyResult<Option<Self>> {
|
fn from_python_pair(
|
||||||
let key_str: &str = key.extract()?;
|
key: &Bound<'_, PyAny>,
|
||||||
|
value: &Bound<'_, PyAny>,
|
||||||
|
) -> PyResult<Option<Self>> {
|
||||||
|
let key_str: PyBackedStr = key.extract()?;
|
||||||
|
|
||||||
let e = match key_str {
|
let e = match &*key_str {
|
||||||
"out_of_band_membership" => EventInternalMetadataData::OutOfBandMembership(
|
"out_of_band_membership" => EventInternalMetadataData::OutOfBandMembership(
|
||||||
value
|
value
|
||||||
.extract()
|
.extract()
|
||||||
|
@ -198,6 +204,8 @@ pub struct EventInternalMetadata {
|
||||||
/// The stream ordering of this event. None, until it has been persisted.
|
/// The stream ordering of this event. None, until it has been persisted.
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
stream_ordering: Option<NonZeroI64>,
|
stream_ordering: Option<NonZeroI64>,
|
||||||
|
#[pyo3(get, set)]
|
||||||
|
instance_name: Option<String>,
|
||||||
|
|
||||||
/// whether this event is an outlier (ie, whether we have the state at that
|
/// whether this event is an outlier (ie, whether we have the state at that
|
||||||
/// point in the DAG)
|
/// point in the DAG)
|
||||||
|
@ -208,11 +216,11 @@ pub struct EventInternalMetadata {
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl EventInternalMetadata {
|
impl EventInternalMetadata {
|
||||||
#[new]
|
#[new]
|
||||||
fn new(dict: &PyDict) -> PyResult<Self> {
|
fn new(dict: &Bound<'_, PyDict>) -> PyResult<Self> {
|
||||||
let mut data = Vec::with_capacity(dict.len());
|
let mut data = Vec::with_capacity(dict.len());
|
||||||
|
|
||||||
for (key, value) in dict.iter() {
|
for (key, value) in dict.iter() {
|
||||||
match EventInternalMetadataData::from_python_pair(key, value) {
|
match EventInternalMetadataData::from_python_pair(&key, &value) {
|
||||||
Ok(Some(entry)) => data.push(entry),
|
Ok(Some(entry)) => data.push(entry),
|
||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -226,6 +234,7 @@ impl EventInternalMetadata {
|
||||||
Ok(EventInternalMetadata {
|
Ok(EventInternalMetadata {
|
||||||
data,
|
data,
|
||||||
stream_ordering: None,
|
stream_ordering: None,
|
||||||
|
instance_name: None,
|
||||||
outlier: false,
|
outlier: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -234,8 +243,11 @@ impl EventInternalMetadata {
|
||||||
self.clone()
|
self.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a dict holding the data stored in the `internal_metadata` column in the database.
|
||||||
|
///
|
||||||
|
/// Note that `outlier` and `stream_ordering` are stored in separate columns so are not returned here.
|
||||||
fn get_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
|
fn get_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
|
||||||
let dict = PyDict::new(py);
|
let dict = PyDict::new_bound(py);
|
||||||
|
|
||||||
for entry in &self.data {
|
for entry in &self.data {
|
||||||
let (key, value) = entry.to_python_pair(py);
|
let (key, value) = entry.to_python_pair(py);
|
||||||
|
|
|
@ -20,20 +20,23 @@
|
||||||
|
|
||||||
//! Classes for representing Events.
|
//! Classes for representing Events.
|
||||||
|
|
||||||
use pyo3::{types::PyModule, PyResult, Python};
|
use pyo3::{
|
||||||
|
types::{PyAnyMethods, PyModule, PyModuleMethods},
|
||||||
|
Bound, PyResult, Python,
|
||||||
|
};
|
||||||
|
|
||||||
mod internal_metadata;
|
mod internal_metadata;
|
||||||
|
|
||||||
/// Called when registering modules with python.
|
/// Called when registering modules with python.
|
||||||
pub fn register_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
let child_module = PyModule::new(py, "events")?;
|
let child_module = PyModule::new_bound(py, "events")?;
|
||||||
child_module.add_class::<internal_metadata::EventInternalMetadata>()?;
|
child_module.add_class::<internal_metadata::EventInternalMetadata>()?;
|
||||||
|
|
||||||
m.add_submodule(child_module)?;
|
m.add_submodule(&child_module)?;
|
||||||
|
|
||||||
// We need to manually add the module to sys.modules to make `from
|
// We need to manually add the module to sys.modules to make `from
|
||||||
// synapse.synapse_rust import events` work.
|
// synapse.synapse_rust import events` work.
|
||||||
py.import("sys")?
|
py.import_bound("sys")?
|
||||||
.getattr("modules")?
|
.getattr("modules")?
|
||||||
.set_item("synapse.synapse_rust.events", child_module)?;
|
.set_item("synapse.synapse_rust.events", child_module)?;
|
||||||
|
|
||||||
|
|
174
rust/src/http.rs
Normal file
174
rust/src/http.rs
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 New Vector, Ltd
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* See the GNU Affero General Public License for more details:
|
||||||
|
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
|
use headers::{Header, HeaderMapExt};
|
||||||
|
use http::{HeaderName, HeaderValue, Method, Request, Response, StatusCode, Uri};
|
||||||
|
use pyo3::{
|
||||||
|
exceptions::PyValueError,
|
||||||
|
types::{PyAnyMethods, PyBytes, PyBytesMethods, PySequence, PyTuple},
|
||||||
|
Bound, PyAny, PyResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::errors::SynapseError;
|
||||||
|
|
||||||
|
/// Read a file-like Python object by chunks
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if calling the `read` on the Python object failed
|
||||||
|
fn read_io_body(body: &Bound<'_, PyAny>, chunk_size: usize) -> PyResult<Bytes> {
|
||||||
|
let mut buf = BytesMut::new();
|
||||||
|
loop {
|
||||||
|
let bound = &body.call_method1("read", (chunk_size,))?;
|
||||||
|
let bytes: &Bound<'_, PyBytes> = bound.downcast()?;
|
||||||
|
if bytes.as_bytes().is_empty() {
|
||||||
|
return Ok(buf.into());
|
||||||
|
}
|
||||||
|
buf.put(bytes.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform a Twisted `IRequest` to an [`http::Request`]
|
||||||
|
///
|
||||||
|
/// It uses the following members of `IRequest`:
|
||||||
|
/// - `content`, which is expected to be a file-like object with a `read` method
|
||||||
|
/// - `uri`, which is expected to be a valid URI as `bytes`
|
||||||
|
/// - `method`, which is expected to be a valid HTTP method as `bytes`
|
||||||
|
/// - `requestHeaders`, which is expected to have a `getAllRawHeaders` method
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if the Python object doesn't properly implement `IRequest`
|
||||||
|
pub fn http_request_from_twisted(request: &Bound<'_, PyAny>) -> PyResult<Request<Bytes>> {
|
||||||
|
let content = request.getattr("content")?;
|
||||||
|
let body = read_io_body(&content, 4096)?;
|
||||||
|
|
||||||
|
let mut req = Request::new(body);
|
||||||
|
|
||||||
|
let bound = &request.getattr("uri")?;
|
||||||
|
let uri: &Bound<'_, PyBytes> = bound.downcast()?;
|
||||||
|
*req.uri_mut() =
|
||||||
|
Uri::try_from(uri.as_bytes()).map_err(|_| PyValueError::new_err("invalid uri"))?;
|
||||||
|
|
||||||
|
let bound = &request.getattr("method")?;
|
||||||
|
let method: &Bound<'_, PyBytes> = bound.downcast()?;
|
||||||
|
*req.method_mut() = Method::from_bytes(method.as_bytes())
|
||||||
|
.map_err(|_| PyValueError::new_err("invalid method"))?;
|
||||||
|
|
||||||
|
let headers_iter = request
|
||||||
|
.getattr("requestHeaders")?
|
||||||
|
.call_method0("getAllRawHeaders")?
|
||||||
|
.iter()?;
|
||||||
|
|
||||||
|
for header in headers_iter {
|
||||||
|
let header = header?;
|
||||||
|
let header: &Bound<'_, PyTuple> = header.downcast()?;
|
||||||
|
let bound = &header.get_item(0)?;
|
||||||
|
let name: &Bound<'_, PyBytes> = bound.downcast()?;
|
||||||
|
let name = HeaderName::from_bytes(name.as_bytes())
|
||||||
|
.map_err(|_| PyValueError::new_err("invalid header name"))?;
|
||||||
|
|
||||||
|
let bound = &header.get_item(1)?;
|
||||||
|
let values: &Bound<'_, PySequence> = bound.downcast()?;
|
||||||
|
for index in 0..values.len()? {
|
||||||
|
let bound = &values.get_item(index)?;
|
||||||
|
let value: &Bound<'_, PyBytes> = bound.downcast()?;
|
||||||
|
let value = HeaderValue::from_bytes(value.as_bytes())
|
||||||
|
.map_err(|_| PyValueError::new_err("invalid header value"))?;
|
||||||
|
req.headers_mut().append(name.clone(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send an [`http::Response`] through a Twisted `IRequest`
|
||||||
|
///
|
||||||
|
/// It uses the following members of `IRequest`:
|
||||||
|
///
|
||||||
|
/// - `responseHeaders`, which is expected to have a `addRawHeader(bytes, bytes)` method
|
||||||
|
/// - `setResponseCode(int)` method
|
||||||
|
/// - `write(bytes)` method
|
||||||
|
/// - `finish()` method
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if the Python object doesn't properly implement `IRequest`
|
||||||
|
pub fn http_response_to_twisted<B>(
|
||||||
|
request: &Bound<'_, PyAny>,
|
||||||
|
response: Response<B>,
|
||||||
|
) -> PyResult<()>
|
||||||
|
where
|
||||||
|
B: Buf,
|
||||||
|
{
|
||||||
|
let (parts, mut body) = response.into_parts();
|
||||||
|
|
||||||
|
request.call_method1("setResponseCode", (parts.status.as_u16(),))?;
|
||||||
|
|
||||||
|
let response_headers = request.getattr("responseHeaders")?;
|
||||||
|
for (name, value) in parts.headers.iter() {
|
||||||
|
response_headers.call_method1("addRawHeader", (name.as_str(), value.as_bytes()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
while body.remaining() != 0 {
|
||||||
|
let chunk = body.chunk();
|
||||||
|
request.call_method1("write", (chunk,))?;
|
||||||
|
body.advance(chunk.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
request.call_method0("finish")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An extension trait for [`HeaderMap`] that provides typed access to headers, and throws the
|
||||||
|
/// right python exceptions when the header is missing or fails to parse.
|
||||||
|
///
|
||||||
|
/// [`HeaderMap`]: headers::HeaderMap
|
||||||
|
pub trait HeaderMapPyExt: HeaderMapExt {
|
||||||
|
/// Get a header from the map, returning an error if it is missing or invalid.
|
||||||
|
fn typed_get_required<H>(&self) -> PyResult<H>
|
||||||
|
where
|
||||||
|
H: Header,
|
||||||
|
{
|
||||||
|
self.typed_get_optional::<H>()?.ok_or_else(|| {
|
||||||
|
SynapseError::new(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
format!("Missing required header: {}", H::name()),
|
||||||
|
"M_MISSING_PARAM",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a header from the map, returning `None` if it is missing and an error if it is invalid.
|
||||||
|
fn typed_get_optional<H>(&self) -> PyResult<Option<H>>
|
||||||
|
where
|
||||||
|
H: Header,
|
||||||
|
{
|
||||||
|
self.typed_try_get::<H>().map_err(|_| {
|
||||||
|
SynapseError::new(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
format!("Invalid header: {}", H::name()),
|
||||||
|
"M_INVALID_PARAM",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HeaderMapExt> HeaderMapPyExt for T {}
|
|
@ -3,8 +3,11 @@ use pyo3::prelude::*;
|
||||||
use pyo3_log::ResetHandle;
|
use pyo3_log::ResetHandle;
|
||||||
|
|
||||||
pub mod acl;
|
pub mod acl;
|
||||||
|
pub mod errors;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
pub mod http;
|
||||||
pub mod push;
|
pub mod push;
|
||||||
|
pub mod rendezvous;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref LOGGING_HANDLE: ResetHandle = pyo3_log::init();
|
static ref LOGGING_HANDLE: ResetHandle = pyo3_log::init();
|
||||||
|
@ -35,7 +38,7 @@ fn reset_logging_config() {
|
||||||
|
|
||||||
/// The entry point for defining the Python module.
|
/// The entry point for defining the Python module.
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
fn synapse_rust(py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
fn synapse_rust(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
|
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
|
||||||
m.add_function(wrap_pyfunction!(get_rust_file_digest, m)?)?;
|
m.add_function(wrap_pyfunction!(get_rust_file_digest, m)?)?;
|
||||||
m.add_function(wrap_pyfunction!(reset_logging_config, m)?)?;
|
m.add_function(wrap_pyfunction!(reset_logging_config, m)?)?;
|
||||||
|
@ -43,6 +46,7 @@ fn synapse_rust(py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||||
acl::register_module(py, m)?;
|
acl::register_module(py, m)?;
|
||||||
push::register_module(py, m)?;
|
push::register_module(py, m)?;
|
||||||
events::register_module(py, m)?;
|
events::register_module(py, m)?;
|
||||||
|
rendezvous::register_module(py, m)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,12 +304,12 @@ pub const BASE_APPEND_UNDERRIDE_RULES: &[PushRule] = &[
|
||||||
default_enabled: true,
|
default_enabled: true,
|
||||||
},
|
},
|
||||||
PushRule {
|
PushRule {
|
||||||
rule_id: Cow::Borrowed("global/underride/.m.rule.room_one_to_one"),
|
rule_id: Cow::Borrowed("global/underride/.m.rule.encrypted_room_one_to_one"),
|
||||||
priority_class: 1,
|
priority_class: 1,
|
||||||
conditions: Cow::Borrowed(&[
|
conditions: Cow::Borrowed(&[
|
||||||
Condition::Known(KnownCondition::EventMatch(EventMatchCondition {
|
Condition::Known(KnownCondition::EventMatch(EventMatchCondition {
|
||||||
key: Cow::Borrowed("type"),
|
key: Cow::Borrowed("type"),
|
||||||
pattern: Cow::Borrowed("m.room.message"),
|
pattern: Cow::Borrowed("m.room.encrypted"),
|
||||||
})),
|
})),
|
||||||
Condition::Known(KnownCondition::RoomMemberCount {
|
Condition::Known(KnownCondition::RoomMemberCount {
|
||||||
is: Some(Cow::Borrowed("2")),
|
is: Some(Cow::Borrowed("2")),
|
||||||
|
@ -320,12 +320,12 @@ pub const BASE_APPEND_UNDERRIDE_RULES: &[PushRule] = &[
|
||||||
default_enabled: true,
|
default_enabled: true,
|
||||||
},
|
},
|
||||||
PushRule {
|
PushRule {
|
||||||
rule_id: Cow::Borrowed("global/underride/.m.rule.encrypted_room_one_to_one"),
|
rule_id: Cow::Borrowed("global/underride/.m.rule.room_one_to_one"),
|
||||||
priority_class: 1,
|
priority_class: 1,
|
||||||
conditions: Cow::Borrowed(&[
|
conditions: Cow::Borrowed(&[
|
||||||
Condition::Known(KnownCondition::EventMatch(EventMatchCondition {
|
Condition::Known(KnownCondition::EventMatch(EventMatchCondition {
|
||||||
key: Cow::Borrowed("type"),
|
key: Cow::Borrowed("type"),
|
||||||
pattern: Cow::Borrowed("m.room.encrypted"),
|
pattern: Cow::Borrowed("m.room.message"),
|
||||||
})),
|
})),
|
||||||
Condition::Known(KnownCondition::RoomMemberCount {
|
Condition::Known(KnownCondition::RoomMemberCount {
|
||||||
is: Some(Cow::Borrowed("2")),
|
is: Some(Cow::Borrowed("2")),
|
||||||
|
|
|
@ -66,7 +66,7 @@ use log::warn;
|
||||||
use pyo3::exceptions::PyTypeError;
|
use pyo3::exceptions::PyTypeError;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::{PyBool, PyList, PyLong, PyString};
|
use pyo3::types::{PyBool, PyList, PyLong, PyString};
|
||||||
use pythonize::{depythonize, pythonize};
|
use pythonize::{depythonize_bound, pythonize};
|
||||||
use serde::de::Error as _;
|
use serde::de::Error as _;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
@ -78,19 +78,19 @@ pub mod evaluator;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
/// Called when registering modules with python.
|
/// Called when registering modules with python.
|
||||||
pub fn register_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
let child_module = PyModule::new(py, "push")?;
|
let child_module = PyModule::new_bound(py, "push")?;
|
||||||
child_module.add_class::<PushRule>()?;
|
child_module.add_class::<PushRule>()?;
|
||||||
child_module.add_class::<PushRules>()?;
|
child_module.add_class::<PushRules>()?;
|
||||||
child_module.add_class::<FilteredPushRules>()?;
|
child_module.add_class::<FilteredPushRules>()?;
|
||||||
child_module.add_class::<PushRuleEvaluator>()?;
|
child_module.add_class::<PushRuleEvaluator>()?;
|
||||||
child_module.add_function(wrap_pyfunction!(get_base_rule_ids, m)?)?;
|
child_module.add_function(wrap_pyfunction!(get_base_rule_ids, m)?)?;
|
||||||
|
|
||||||
m.add_submodule(child_module)?;
|
m.add_submodule(&child_module)?;
|
||||||
|
|
||||||
// We need to manually add the module to sys.modules to make `from
|
// We need to manually add the module to sys.modules to make `from
|
||||||
// synapse.synapse_rust import push` work.
|
// synapse.synapse_rust import push` work.
|
||||||
py.import("sys")?
|
py.import_bound("sys")?
|
||||||
.getattr("modules")?
|
.getattr("modules")?
|
||||||
.set_item("synapse.synapse_rust.push", child_module)?;
|
.set_item("synapse.synapse_rust.push", child_module)?;
|
||||||
|
|
||||||
|
@ -271,12 +271,12 @@ pub enum SimpleJsonValue {
|
||||||
|
|
||||||
impl<'source> FromPyObject<'source> for SimpleJsonValue {
|
impl<'source> FromPyObject<'source> for SimpleJsonValue {
|
||||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||||
if let Ok(s) = <PyString as pyo3::PyTryFrom>::try_from(ob) {
|
if let Ok(s) = ob.downcast::<PyString>() {
|
||||||
Ok(SimpleJsonValue::Str(Cow::Owned(s.to_string())))
|
Ok(SimpleJsonValue::Str(Cow::Owned(s.to_string())))
|
||||||
// A bool *is* an int, ensure we try bool first.
|
// A bool *is* an int, ensure we try bool first.
|
||||||
} else if let Ok(b) = <PyBool as pyo3::PyTryFrom>::try_from(ob) {
|
} else if let Ok(b) = ob.downcast::<PyBool>() {
|
||||||
Ok(SimpleJsonValue::Bool(b.extract()?))
|
Ok(SimpleJsonValue::Bool(b.extract()?))
|
||||||
} else if let Ok(i) = <PyLong as pyo3::PyTryFrom>::try_from(ob) {
|
} else if let Ok(i) = ob.downcast::<PyLong>() {
|
||||||
Ok(SimpleJsonValue::Int(i.extract()?))
|
Ok(SimpleJsonValue::Int(i.extract()?))
|
||||||
} else if ob.is_none() {
|
} else if ob.is_none() {
|
||||||
Ok(SimpleJsonValue::Null)
|
Ok(SimpleJsonValue::Null)
|
||||||
|
@ -299,7 +299,7 @@ pub enum JsonValue {
|
||||||
|
|
||||||
impl<'source> FromPyObject<'source> for JsonValue {
|
impl<'source> FromPyObject<'source> for JsonValue {
|
||||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||||
if let Ok(l) = <PyList as pyo3::PyTryFrom>::try_from(ob) {
|
if let Ok(l) = ob.downcast::<PyList>() {
|
||||||
match l.iter().map(SimpleJsonValue::extract).collect() {
|
match l.iter().map(SimpleJsonValue::extract).collect() {
|
||||||
Ok(a) => Ok(JsonValue::Array(a)),
|
Ok(a) => Ok(JsonValue::Array(a)),
|
||||||
Err(e) => Err(PyTypeError::new_err(format!(
|
Err(e) => Err(PyTypeError::new_err(format!(
|
||||||
|
@ -370,8 +370,8 @@ impl IntoPy<PyObject> for Condition {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'source> FromPyObject<'source> for Condition {
|
impl<'source> FromPyObject<'source> for Condition {
|
||||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
fn extract_bound(ob: &Bound<'source, PyAny>) -> PyResult<Self> {
|
||||||
Ok(depythonize(ob)?)
|
Ok(depythonize_bound(ob.clone())?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
327
rust/src/rendezvous/mod.rs
Normal file
327
rust/src/rendezvous/mod.rs
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
/*
|
||||||
|
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 New Vector, Ltd
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* See the GNU Affero General Public License for more details:
|
||||||
|
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
time::{Duration, SystemTime},
|
||||||
|
};
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use headers::{
|
||||||
|
AccessControlAllowOrigin, AccessControlExposeHeaders, CacheControl, ContentLength, ContentType,
|
||||||
|
HeaderMapExt, IfMatch, IfNoneMatch, Pragma,
|
||||||
|
};
|
||||||
|
use http::{header::ETAG, HeaderMap, Response, StatusCode, Uri};
|
||||||
|
use mime::Mime;
|
||||||
|
use pyo3::{
|
||||||
|
exceptions::PyValueError,
|
||||||
|
pyclass, pymethods,
|
||||||
|
types::{PyAnyMethods, PyModule, PyModuleMethods},
|
||||||
|
Bound, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
|
||||||
|
};
|
||||||
|
use ulid::Ulid;
|
||||||
|
|
||||||
|
use self::session::Session;
|
||||||
|
use crate::{
|
||||||
|
errors::{NotFoundError, SynapseError},
|
||||||
|
http::{http_request_from_twisted, http_response_to_twisted, HeaderMapPyExt},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod session;
|
||||||
|
|
||||||
|
// n.b. Because OPTIONS requests are handled by the Python code, we don't need to set Access-Control-Allow-Headers.
|
||||||
|
fn prepare_headers(headers: &mut HeaderMap, session: &Session) {
|
||||||
|
headers.typed_insert(AccessControlAllowOrigin::ANY);
|
||||||
|
headers.typed_insert(AccessControlExposeHeaders::from_iter([ETAG]));
|
||||||
|
headers.typed_insert(Pragma::no_cache());
|
||||||
|
headers.typed_insert(CacheControl::new().with_no_store());
|
||||||
|
headers.typed_insert(session.etag());
|
||||||
|
headers.typed_insert(session.expires());
|
||||||
|
headers.typed_insert(session.last_modified());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
struct RendezvousHandler {
|
||||||
|
base: Uri,
|
||||||
|
clock: PyObject,
|
||||||
|
sessions: BTreeMap<Ulid, Session>,
|
||||||
|
capacity: usize,
|
||||||
|
max_content_length: u64,
|
||||||
|
ttl: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RendezvousHandler {
|
||||||
|
/// Check the input headers of a request which sets data for a session, and return the content type.
|
||||||
|
fn check_input_headers(&self, headers: &HeaderMap) -> PyResult<Mime> {
|
||||||
|
let ContentLength(content_length) = headers.typed_get_required()?;
|
||||||
|
|
||||||
|
if content_length > self.max_content_length {
|
||||||
|
return Err(SynapseError::new(
|
||||||
|
StatusCode::PAYLOAD_TOO_LARGE,
|
||||||
|
"Payload too large".to_owned(),
|
||||||
|
"M_TOO_LARGE",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let content_type: ContentType = headers.typed_get_required()?;
|
||||||
|
|
||||||
|
// Content-Type must be text/plain
|
||||||
|
if content_type != ContentType::text() {
|
||||||
|
return Err(SynapseError::new(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"Content-Type must be text/plain".to_owned(),
|
||||||
|
"M_INVALID_PARAM",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(content_type.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evict expired sessions and remove the oldest sessions until we're under the capacity.
|
||||||
|
fn evict(&mut self, now: SystemTime) {
|
||||||
|
// First remove all the entries which expired
|
||||||
|
self.sessions.retain(|_, session| !session.expired(now));
|
||||||
|
|
||||||
|
// Then we remove the oldest entires until we're under the limit
|
||||||
|
while self.sessions.len() > self.capacity {
|
||||||
|
self.sessions.pop_first();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl RendezvousHandler {
|
||||||
|
#[new]
|
||||||
|
#[pyo3(signature = (homeserver, /, capacity=100, max_content_length=4*1024, eviction_interval=60*1000, ttl=60*1000))]
|
||||||
|
fn new(
|
||||||
|
py: Python<'_>,
|
||||||
|
homeserver: &Bound<'_, PyAny>,
|
||||||
|
capacity: usize,
|
||||||
|
max_content_length: u64,
|
||||||
|
eviction_interval: u64,
|
||||||
|
ttl: u64,
|
||||||
|
) -> PyResult<Py<Self>> {
|
||||||
|
let base: String = homeserver
|
||||||
|
.getattr("config")?
|
||||||
|
.getattr("server")?
|
||||||
|
.getattr("public_baseurl")?
|
||||||
|
.extract()?;
|
||||||
|
let base = Uri::try_from(format!("{base}_synapse/client/rendezvous"))
|
||||||
|
.map_err(|_| PyValueError::new_err("Invalid base URI"))?;
|
||||||
|
|
||||||
|
let clock = homeserver.call_method0("get_clock")?.to_object(py);
|
||||||
|
|
||||||
|
// Construct a Python object so that we can get a reference to the
|
||||||
|
// evict method and schedule it to run.
|
||||||
|
let self_ = Py::new(
|
||||||
|
py,
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
clock,
|
||||||
|
sessions: BTreeMap::new(),
|
||||||
|
capacity,
|
||||||
|
max_content_length,
|
||||||
|
ttl: Duration::from_millis(ttl),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let evict = self_.getattr(py, "_evict")?;
|
||||||
|
homeserver.call_method0("get_clock")?.call_method(
|
||||||
|
"looping_call",
|
||||||
|
(evict, eviction_interval),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(self_)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _evict(&mut self, py: Python<'_>) -> PyResult<()> {
|
||||||
|
let clock = self.clock.bind(py);
|
||||||
|
let now: u64 = clock.call_method0("time_msec")?.extract()?;
|
||||||
|
let now = SystemTime::UNIX_EPOCH + Duration::from_millis(now);
|
||||||
|
self.evict(now);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_post(&mut self, py: Python<'_>, twisted_request: &Bound<'_, PyAny>) -> PyResult<()> {
|
||||||
|
let request = http_request_from_twisted(twisted_request)?;
|
||||||
|
|
||||||
|
let content_type = self.check_input_headers(request.headers())?;
|
||||||
|
|
||||||
|
let clock = self.clock.bind(py);
|
||||||
|
let now: u64 = clock.call_method0("time_msec")?.extract()?;
|
||||||
|
let now = SystemTime::UNIX_EPOCH + Duration::from_millis(now);
|
||||||
|
|
||||||
|
// We trigger an immediate eviction if we're at 2x the capacity
|
||||||
|
if self.sessions.len() >= self.capacity * 2 {
|
||||||
|
self.evict(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a new ULID for the session from the current time.
|
||||||
|
let id = Ulid::from_datetime(now);
|
||||||
|
|
||||||
|
let uri = format!("{base}/{id}", base = self.base);
|
||||||
|
|
||||||
|
let body = request.into_body();
|
||||||
|
|
||||||
|
let session = Session::new(body, content_type, now, self.ttl);
|
||||||
|
|
||||||
|
let response = serde_json::json!({
|
||||||
|
"url": uri,
|
||||||
|
})
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let mut response = Response::new(response.as_bytes());
|
||||||
|
*response.status_mut() = StatusCode::CREATED;
|
||||||
|
response.headers_mut().typed_insert(ContentType::json());
|
||||||
|
prepare_headers(response.headers_mut(), &session);
|
||||||
|
http_response_to_twisted(twisted_request, response)?;
|
||||||
|
|
||||||
|
self.sessions.insert(id, session);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_get(
|
||||||
|
&mut self,
|
||||||
|
py: Python<'_>,
|
||||||
|
twisted_request: &Bound<'_, PyAny>,
|
||||||
|
id: &str,
|
||||||
|
) -> PyResult<()> {
|
||||||
|
let request = http_request_from_twisted(twisted_request)?;
|
||||||
|
|
||||||
|
let if_none_match: Option<IfNoneMatch> = request.headers().typed_get_optional()?;
|
||||||
|
|
||||||
|
let now: u64 = self.clock.call_method0(py, "time_msec")?.extract(py)?;
|
||||||
|
let now = SystemTime::UNIX_EPOCH + Duration::from_millis(now);
|
||||||
|
|
||||||
|
let id: Ulid = id.parse().map_err(|_| NotFoundError::new())?;
|
||||||
|
let session = self
|
||||||
|
.sessions
|
||||||
|
.get(&id)
|
||||||
|
.filter(|s| !s.expired(now))
|
||||||
|
.ok_or_else(NotFoundError::new)?;
|
||||||
|
|
||||||
|
if let Some(if_none_match) = if_none_match {
|
||||||
|
if !if_none_match.precondition_passes(&session.etag()) {
|
||||||
|
let mut response = Response::new(Bytes::new());
|
||||||
|
*response.status_mut() = StatusCode::NOT_MODIFIED;
|
||||||
|
prepare_headers(response.headers_mut(), session);
|
||||||
|
http_response_to_twisted(twisted_request, response)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut response = Response::new(session.data());
|
||||||
|
*response.status_mut() = StatusCode::OK;
|
||||||
|
let headers = response.headers_mut();
|
||||||
|
prepare_headers(headers, session);
|
||||||
|
headers.typed_insert(session.content_type());
|
||||||
|
headers.typed_insert(session.content_length());
|
||||||
|
http_response_to_twisted(twisted_request, response)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_put(
|
||||||
|
&mut self,
|
||||||
|
py: Python<'_>,
|
||||||
|
twisted_request: &Bound<'_, PyAny>,
|
||||||
|
id: &str,
|
||||||
|
) -> PyResult<()> {
|
||||||
|
let request = http_request_from_twisted(twisted_request)?;
|
||||||
|
|
||||||
|
let content_type = self.check_input_headers(request.headers())?;
|
||||||
|
|
||||||
|
let if_match: IfMatch = request.headers().typed_get_required()?;
|
||||||
|
|
||||||
|
let data = request.into_body();
|
||||||
|
|
||||||
|
let now: u64 = self.clock.call_method0(py, "time_msec")?.extract(py)?;
|
||||||
|
let now = SystemTime::UNIX_EPOCH + Duration::from_millis(now);
|
||||||
|
|
||||||
|
let id: Ulid = id.parse().map_err(|_| NotFoundError::new())?;
|
||||||
|
let session = self
|
||||||
|
.sessions
|
||||||
|
.get_mut(&id)
|
||||||
|
.filter(|s| !s.expired(now))
|
||||||
|
.ok_or_else(NotFoundError::new)?;
|
||||||
|
|
||||||
|
if !if_match.precondition_passes(&session.etag()) {
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
prepare_headers(&mut headers, session);
|
||||||
|
|
||||||
|
let mut additional_fields = HashMap::with_capacity(1);
|
||||||
|
additional_fields.insert(
|
||||||
|
String::from("org.matrix.msc4108.errcode"),
|
||||||
|
String::from("M_CONCURRENT_WRITE"),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(SynapseError::new(
|
||||||
|
StatusCode::PRECONDITION_FAILED,
|
||||||
|
"ETag does not match".to_owned(),
|
||||||
|
"M_UNKNOWN", // Would be M_CONCURRENT_WRITE
|
||||||
|
Some(additional_fields),
|
||||||
|
Some(headers),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
session.update(data, content_type, now);
|
||||||
|
|
||||||
|
let mut response = Response::new(Bytes::new());
|
||||||
|
*response.status_mut() = StatusCode::ACCEPTED;
|
||||||
|
prepare_headers(response.headers_mut(), session);
|
||||||
|
http_response_to_twisted(twisted_request, response)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_delete(&mut self, twisted_request: &Bound<'_, PyAny>, id: &str) -> PyResult<()> {
|
||||||
|
let _request = http_request_from_twisted(twisted_request)?;
|
||||||
|
|
||||||
|
let id: Ulid = id.parse().map_err(|_| NotFoundError::new())?;
|
||||||
|
let _session = self.sessions.remove(&id).ok_or_else(NotFoundError::new)?;
|
||||||
|
|
||||||
|
let mut response = Response::new(Bytes::new());
|
||||||
|
*response.status_mut() = StatusCode::NO_CONTENT;
|
||||||
|
response
|
||||||
|
.headers_mut()
|
||||||
|
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||||
|
http_response_to_twisted(twisted_request, response)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
let child_module = PyModule::new_bound(py, "rendezvous")?;
|
||||||
|
|
||||||
|
child_module.add_class::<RendezvousHandler>()?;
|
||||||
|
|
||||||
|
m.add_submodule(&child_module)?;
|
||||||
|
|
||||||
|
// We need to manually add the module to sys.modules to make `from
|
||||||
|
// synapse.synapse_rust import rendezvous` work.
|
||||||
|
py.import_bound("sys")?
|
||||||
|
.getattr("modules")?
|
||||||
|
.set_item("synapse.synapse_rust.rendezvous", child_module)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
91
rust/src/rendezvous/session.rs
Normal file
91
rust/src/rendezvous/session.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 New Vector, Ltd
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* See the GNU Affero General Public License for more details:
|
||||||
|
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
|
||||||
|
use bytes::Bytes;
|
||||||
|
use headers::{ContentLength, ContentType, ETag, Expires, LastModified};
|
||||||
|
use mime::Mime;
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
|
/// A single session, containing data, metadata, and expiry information.
|
||||||
|
pub struct Session {
|
||||||
|
hash: [u8; 32],
|
||||||
|
data: Bytes,
|
||||||
|
content_type: Mime,
|
||||||
|
last_modified: SystemTime,
|
||||||
|
expires: SystemTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Session {
|
||||||
|
/// Create a new session with the given data, content type, and time-to-live.
|
||||||
|
pub fn new(data: Bytes, content_type: Mime, now: SystemTime, ttl: Duration) -> Self {
|
||||||
|
let hash = Sha256::digest(&data).into();
|
||||||
|
Self {
|
||||||
|
hash,
|
||||||
|
data,
|
||||||
|
content_type,
|
||||||
|
expires: now + ttl,
|
||||||
|
last_modified: now,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the session has expired at the given time.
|
||||||
|
pub fn expired(&self, now: SystemTime) -> bool {
|
||||||
|
self.expires <= now
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the session with new data, content type, and last modified time.
|
||||||
|
pub fn update(&mut self, data: Bytes, content_type: Mime, now: SystemTime) {
|
||||||
|
self.hash = Sha256::digest(&data).into();
|
||||||
|
self.data = data;
|
||||||
|
self.content_type = content_type;
|
||||||
|
self.last_modified = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the Content-Type header of the session.
|
||||||
|
pub fn content_type(&self) -> ContentType {
|
||||||
|
self.content_type.clone().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the Content-Length header of the session.
|
||||||
|
pub fn content_length(&self) -> ContentLength {
|
||||||
|
ContentLength(self.data.len() as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the ETag header of the session.
|
||||||
|
pub fn etag(&self) -> ETag {
|
||||||
|
let encoded = URL_SAFE_NO_PAD.encode(self.hash);
|
||||||
|
// SAFETY: Base64 encoding is URL-safe, so ETag-safe
|
||||||
|
format!("\"{encoded}\"")
|
||||||
|
.parse()
|
||||||
|
.expect("base64-encoded hash should be URL-safe")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the Last-Modified header of the session.
|
||||||
|
pub fn last_modified(&self) -> LastModified {
|
||||||
|
self.last_modified.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the Expires header of the session.
|
||||||
|
pub fn expires(&self) -> Expires {
|
||||||
|
self.expires.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current data stored in the session.
|
||||||
|
pub fn data(&self) -> Bytes {
|
||||||
|
self.data.clone()
|
||||||
|
}
|
||||||
|
}
|
|
@ -214,7 +214,16 @@ fi
|
||||||
|
|
||||||
extra_test_args=()
|
extra_test_args=()
|
||||||
|
|
||||||
test_packages="./tests/csapi ./tests ./tests/msc3874 ./tests/msc3890 ./tests/msc3391 ./tests/msc3930 ./tests/msc3902"
|
test_packages=(
|
||||||
|
./tests/csapi
|
||||||
|
./tests
|
||||||
|
./tests/msc3874
|
||||||
|
./tests/msc3890
|
||||||
|
./tests/msc3391
|
||||||
|
./tests/msc3930
|
||||||
|
./tests/msc3902
|
||||||
|
./tests/msc3967
|
||||||
|
)
|
||||||
|
|
||||||
# Enable dirty runs, so tests will reuse the same container where possible.
|
# Enable dirty runs, so tests will reuse the same container where possible.
|
||||||
# This significantly speeds up tests, but increases the possibility of test pollution.
|
# This significantly speeds up tests, but increases the possibility of test pollution.
|
||||||
|
@ -278,7 +287,7 @@ fi
|
||||||
export PASS_SYNAPSE_LOG_TESTING=1
|
export PASS_SYNAPSE_LOG_TESTING=1
|
||||||
|
|
||||||
# Run the tests!
|
# Run the tests!
|
||||||
echo "Images built; running complement with ${extra_test_args[@]} $@ $test_packages"
|
echo "Images built; running complement with ${extra_test_args[@]} $@ ${test_packages[@]}"
|
||||||
cd "$COMPLEMENT_DIR"
|
cd "$COMPLEMENT_DIR"
|
||||||
|
|
||||||
go test -v -tags "synapse_blacklist" -count=1 "${extra_test_args[@]}" "$@" $test_packages
|
go test -v -tags "synapse_blacklist" -count=1 "${extra_test_args[@]}" "$@" "${test_packages[@]}"
|
||||||
|
|
|
@ -91,7 +91,6 @@ else
|
||||||
"synapse" "docker" "tests"
|
"synapse" "docker" "tests"
|
||||||
"scripts-dev"
|
"scripts-dev"
|
||||||
"contrib" "synmark" "stubs" ".ci"
|
"contrib" "synmark" "stubs" ".ci"
|
||||||
"dev-docs"
|
|
||||||
)
|
)
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -52,6 +52,7 @@ def request_registration(
|
||||||
user_type: Optional[str] = None,
|
user_type: Optional[str] = None,
|
||||||
_print: Callable[[str], None] = print,
|
_print: Callable[[str], None] = print,
|
||||||
exit: Callable[[int], None] = sys.exit,
|
exit: Callable[[int], None] = sys.exit,
|
||||||
|
exists_ok: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
url = "%s/_synapse/admin/v1/register" % (server_location.rstrip("/"),)
|
url = "%s/_synapse/admin/v1/register" % (server_location.rstrip("/"),)
|
||||||
|
|
||||||
|
@ -97,6 +98,10 @@ def request_registration(
|
||||||
r = requests.post(url, json=data)
|
r = requests.post(url, json=data)
|
||||||
|
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
|
response = r.json()
|
||||||
|
if exists_ok and response["errcode"] == "M_USER_IN_USE":
|
||||||
|
_print("User already exists. Skipping.")
|
||||||
|
return
|
||||||
_print("ERROR! Received %d %s" % (r.status_code, r.reason))
|
_print("ERROR! Received %d %s" % (r.status_code, r.reason))
|
||||||
if 400 <= r.status_code < 500:
|
if 400 <= r.status_code < 500:
|
||||||
try:
|
try:
|
||||||
|
@ -115,6 +120,7 @@ def register_new_user(
|
||||||
shared_secret: str,
|
shared_secret: str,
|
||||||
admin: Optional[bool],
|
admin: Optional[bool],
|
||||||
user_type: Optional[str],
|
user_type: Optional[str],
|
||||||
|
exists_ok: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
if not user:
|
if not user:
|
||||||
try:
|
try:
|
||||||
|
@ -154,7 +160,13 @@ def register_new_user(
|
||||||
admin = False
|
admin = False
|
||||||
|
|
||||||
request_registration(
|
request_registration(
|
||||||
user, password, server_location, shared_secret, bool(admin), user_type
|
user,
|
||||||
|
password,
|
||||||
|
server_location,
|
||||||
|
shared_secret,
|
||||||
|
bool(admin),
|
||||||
|
user_type,
|
||||||
|
exists_ok=exists_ok,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,10 +186,22 @@ def main() -> None:
|
||||||
help="Local part of the new user. Will prompt if omitted.",
|
help="Local part of the new user. Will prompt if omitted.",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
"--exists-ok",
|
||||||
|
action="store_true",
|
||||||
|
help="Do not fail if user already exists.",
|
||||||
|
)
|
||||||
|
password_group = parser.add_mutually_exclusive_group()
|
||||||
|
password_group.add_argument(
|
||||||
"-p",
|
"-p",
|
||||||
"--password",
|
"--password",
|
||||||
default=None,
|
default=None,
|
||||||
help="New password for user. Will prompt if omitted.",
|
help="New password for user. Will prompt for a password if "
|
||||||
|
"this flag and `--password-file` are both omitted.",
|
||||||
|
)
|
||||||
|
password_group.add_argument(
|
||||||
|
"--password-file",
|
||||||
|
default=None,
|
||||||
|
help="File containing the new password for user. If set, will override `--password`.",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-t",
|
"-t",
|
||||||
|
@ -185,6 +209,7 @@ def main() -> None:
|
||||||
default=None,
|
default=None,
|
||||||
help="User type as specified in synapse.api.constants.UserTypes",
|
help="User type as specified in synapse.api.constants.UserTypes",
|
||||||
)
|
)
|
||||||
|
|
||||||
admin_group = parser.add_mutually_exclusive_group()
|
admin_group = parser.add_mutually_exclusive_group()
|
||||||
admin_group.add_argument(
|
admin_group.add_argument(
|
||||||
"-a",
|
"-a",
|
||||||
|
@ -247,6 +272,11 @@ def main() -> None:
|
||||||
print(_NO_SHARED_SECRET_OPTS_ERROR, file=sys.stderr)
|
print(_NO_SHARED_SECRET_OPTS_ERROR, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.password_file:
|
||||||
|
password = _read_file(args.password_file, "password-file").strip()
|
||||||
|
else:
|
||||||
|
password = args.password
|
||||||
|
|
||||||
if args.server_url:
|
if args.server_url:
|
||||||
server_url = args.server_url
|
server_url = args.server_url
|
||||||
elif config is not None:
|
elif config is not None:
|
||||||
|
@ -270,7 +300,13 @@ def main() -> None:
|
||||||
admin = args.admin
|
admin = args.admin
|
||||||
|
|
||||||
register_new_user(
|
register_new_user(
|
||||||
args.user, args.password, server_url, secret, admin, args.user_type
|
args.user,
|
||||||
|
password,
|
||||||
|
server_url,
|
||||||
|
secret,
|
||||||
|
admin,
|
||||||
|
args.user_type,
|
||||||
|
exists_ok=args.exists_ok,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ from synapse.logging.context import (
|
||||||
)
|
)
|
||||||
from synapse.notifier import ReplicationNotifier
|
from synapse.notifier import ReplicationNotifier
|
||||||
from synapse.storage.database import DatabasePool, LoggingTransaction, make_conn
|
from synapse.storage.database import DatabasePool, LoggingTransaction, make_conn
|
||||||
from synapse.storage.databases.main import FilteringWorkerStore, PushRuleStore
|
from synapse.storage.databases.main import FilteringWorkerStore
|
||||||
from synapse.storage.databases.main.account_data import AccountDataWorkerStore
|
from synapse.storage.databases.main.account_data import AccountDataWorkerStore
|
||||||
from synapse.storage.databases.main.client_ips import ClientIpBackgroundUpdateStore
|
from synapse.storage.databases.main.client_ips import ClientIpBackgroundUpdateStore
|
||||||
from synapse.storage.databases.main.deviceinbox import DeviceInboxBackgroundUpdateStore
|
from synapse.storage.databases.main.deviceinbox import DeviceInboxBackgroundUpdateStore
|
||||||
|
@ -77,10 +77,8 @@ from synapse.storage.databases.main.media_repository import (
|
||||||
)
|
)
|
||||||
from synapse.storage.databases.main.presence import PresenceBackgroundUpdateStore
|
from synapse.storage.databases.main.presence import PresenceBackgroundUpdateStore
|
||||||
from synapse.storage.databases.main.profile import ProfileWorkerStore
|
from synapse.storage.databases.main.profile import ProfileWorkerStore
|
||||||
from synapse.storage.databases.main.pusher import (
|
from synapse.storage.databases.main.push_rule import PusherWorkerStore
|
||||||
PusherBackgroundUpdatesStore,
|
from synapse.storage.databases.main.pusher import PusherBackgroundUpdatesStore
|
||||||
PusherWorkerStore,
|
|
||||||
)
|
|
||||||
from synapse.storage.databases.main.receipts import ReceiptsBackgroundUpdateStore
|
from synapse.storage.databases.main.receipts import ReceiptsBackgroundUpdateStore
|
||||||
from synapse.storage.databases.main.registration import (
|
from synapse.storage.databases.main.registration import (
|
||||||
RegistrationBackgroundUpdateStore,
|
RegistrationBackgroundUpdateStore,
|
||||||
|
@ -129,7 +127,7 @@ BOOLEAN_COLUMNS = {
|
||||||
"redactions": ["have_censored"],
|
"redactions": ["have_censored"],
|
||||||
"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"],
|
"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"],
|
"per_user_experimental_features": ["enabled"],
|
||||||
|
@ -245,7 +243,6 @@ class Store(
|
||||||
AccountDataWorkerStore,
|
AccountDataWorkerStore,
|
||||||
FilteringWorkerStore,
|
FilteringWorkerStore,
|
||||||
ProfileWorkerStore,
|
ProfileWorkerStore,
|
||||||
PushRuleStore,
|
|
||||||
PusherWorkerStore,
|
PusherWorkerStore,
|
||||||
PusherBackgroundUpdatesStore,
|
PusherBackgroundUpdatesStore,
|
||||||
PresenceBackgroundUpdateStore,
|
PresenceBackgroundUpdateStore,
|
||||||
|
@ -780,22 +777,74 @@ class Porter:
|
||||||
await self._setup_events_stream_seqs()
|
await self._setup_events_stream_seqs()
|
||||||
await self._setup_sequence(
|
await self._setup_sequence(
|
||||||
"un_partial_stated_event_stream_sequence",
|
"un_partial_stated_event_stream_sequence",
|
||||||
("un_partial_stated_event_stream",),
|
[("un_partial_stated_event_stream", "stream_id")],
|
||||||
)
|
)
|
||||||
await self._setup_sequence(
|
await self._setup_sequence(
|
||||||
"device_inbox_sequence", ("device_inbox", "device_federation_outbox")
|
"device_inbox_sequence",
|
||||||
|
[
|
||||||
|
("device_inbox", "stream_id"),
|
||||||
|
("device_federation_outbox", "stream_id"),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
await self._setup_sequence(
|
await self._setup_sequence(
|
||||||
"account_data_sequence",
|
"account_data_sequence",
|
||||||
("room_account_data", "room_tags_revisions", "account_data"),
|
[
|
||||||
|
("room_account_data", "stream_id"),
|
||||||
|
("room_tags_revisions", "stream_id"),
|
||||||
|
("account_data", "stream_id"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
await self._setup_sequence(
|
||||||
|
"receipts_sequence",
|
||||||
|
[
|
||||||
|
("receipts_linearized", "stream_id"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
await self._setup_sequence(
|
||||||
|
"presence_stream_sequence",
|
||||||
|
[
|
||||||
|
("presence_stream", "stream_id"),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
await self._setup_sequence("receipts_sequence", ("receipts_linearized",))
|
|
||||||
await self._setup_sequence("presence_stream_sequence", ("presence_stream",))
|
|
||||||
await self._setup_auth_chain_sequence()
|
await self._setup_auth_chain_sequence()
|
||||||
await self._setup_sequence(
|
await self._setup_sequence(
|
||||||
"application_services_txn_id_seq",
|
"application_services_txn_id_seq",
|
||||||
("application_services_txns",),
|
[
|
||||||
"txn_id",
|
(
|
||||||
|
"application_services_txns",
|
||||||
|
"txn_id",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
await self._setup_sequence(
|
||||||
|
"device_lists_sequence",
|
||||||
|
[
|
||||||
|
("device_lists_stream", "stream_id"),
|
||||||
|
("user_signature_stream", "stream_id"),
|
||||||
|
("device_lists_outbound_pokes", "stream_id"),
|
||||||
|
("device_lists_changes_in_room", "stream_id"),
|
||||||
|
("device_lists_remote_pending", "stream_id"),
|
||||||
|
("device_lists_changes_converted_stream_position", "stream_id"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
await self._setup_sequence(
|
||||||
|
"e2e_cross_signing_keys_sequence",
|
||||||
|
[
|
||||||
|
("e2e_cross_signing_keys", "stream_id"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
await self._setup_sequence(
|
||||||
|
"push_rules_stream_sequence",
|
||||||
|
[
|
||||||
|
("push_rules_stream", "stream_id"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
await self._setup_sequence(
|
||||||
|
"pushers_sequence",
|
||||||
|
[
|
||||||
|
("pushers", "id"),
|
||||||
|
("deleted_pushers", "stream_id"),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Step 3. Get tables.
|
# Step 3. Get tables.
|
||||||
|
@ -1104,12 +1153,11 @@ class Porter:
|
||||||
async def _setup_sequence(
|
async def _setup_sequence(
|
||||||
self,
|
self,
|
||||||
sequence_name: str,
|
sequence_name: str,
|
||||||
stream_id_tables: Iterable[str],
|
stream_id_tables: Iterable[Tuple[str, str]],
|
||||||
column_name: str = "stream_id",
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set a sequence to the correct value."""
|
"""Set a sequence to the correct value."""
|
||||||
current_stream_ids = []
|
current_stream_ids = []
|
||||||
for stream_id_table in stream_id_tables:
|
for stream_id_table, column_name in stream_id_tables:
|
||||||
max_stream_id = cast(
|
max_stream_id = cast(
|
||||||
int,
|
int,
|
||||||
await self.sqlite_store.db_pool.simple_select_one_onecol(
|
await self.sqlite_store.db_pool.simple_select_one_onecol(
|
||||||
|
|
|
@ -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 = {INVITE, JOIN, KNOCK, LEAVE, BAN}
|
||||||
|
|
||||||
|
|
||||||
class PresenceState:
|
class PresenceState:
|
||||||
|
@ -234,6 +234,13 @@ class EventContentFields:
|
||||||
TO_DEVICE_MSGID: Final = "org.matrix.msgid"
|
TO_DEVICE_MSGID: Final = "org.matrix.msgid"
|
||||||
|
|
||||||
|
|
||||||
|
class EventUnsignedContentFields:
|
||||||
|
"""Fields found inside the 'unsigned' data on events"""
|
||||||
|
|
||||||
|
# Requesting user's membership, per MSC4115
|
||||||
|
MEMBERSHIP: Final = "membership"
|
||||||
|
|
||||||
|
|
||||||
class RoomTypes:
|
class RoomTypes:
|
||||||
"""Understood values of the room_type field of m.room.create events."""
|
"""Understood values of the room_type field of m.room.create events."""
|
||||||
|
|
||||||
|
|
|
@ -142,12 +142,12 @@ USER_FILTER_SCHEMA = {
|
||||||
|
|
||||||
@FormatChecker.cls_checks("matrix_room_id")
|
@FormatChecker.cls_checks("matrix_room_id")
|
||||||
def matrix_room_id_validator(room_id: object) -> bool:
|
def matrix_room_id_validator(room_id: object) -> bool:
|
||||||
return isinstance(room_id, str) and RoomID.is_valid(room_id)
|
return isinstance(room_id, str) and (RoomID.is_valid(room_id) or room_id == "*")
|
||||||
|
|
||||||
|
|
||||||
@FormatChecker.cls_checks("matrix_user_id")
|
@FormatChecker.cls_checks("matrix_user_id")
|
||||||
def matrix_user_id_validator(user_id: object) -> bool:
|
def matrix_user_id_validator(user_id: object) -> bool:
|
||||||
return isinstance(user_id, str) and UserID.is_valid(user_id)
|
return isinstance(user_id, str) and (UserID.is_valid(user_id) or user_id == "*")
|
||||||
|
|
||||||
|
|
||||||
class Filtering:
|
class Filtering:
|
||||||
|
|
|
@ -316,6 +316,10 @@ class Ratelimiter:
|
||||||
)
|
)
|
||||||
|
|
||||||
if not allowed:
|
if not allowed:
|
||||||
|
# We pause for a bit here to stop clients from "tight-looping" on
|
||||||
|
# retrying their request.
|
||||||
|
await self.clock.sleep(0.5)
|
||||||
|
|
||||||
raise LimitExceededError(
|
raise LimitExceededError(
|
||||||
limiter_name=self._limiter_name,
|
limiter_name=self._limiter_name,
|
||||||
retry_after_ms=int(1000 * (time_allowed - time_now_s)),
|
retry_after_ms=int(1000 * (time_allowed - time_now_s)),
|
||||||
|
|
|
@ -68,6 +68,7 @@ from synapse.config._base import format_config_error
|
||||||
from synapse.config.homeserver import HomeServerConfig
|
from synapse.config.homeserver import HomeServerConfig
|
||||||
from synapse.config.server import ListenerConfig, ManholeConfig, TCPListenerConfig
|
from synapse.config.server import ListenerConfig, ManholeConfig, TCPListenerConfig
|
||||||
from synapse.crypto import context_factory
|
from synapse.crypto import context_factory
|
||||||
|
from synapse.events.auto_accept_invites import InviteAutoAccepter
|
||||||
from synapse.events.presence_router import load_legacy_presence_router
|
from synapse.events.presence_router import load_legacy_presence_router
|
||||||
from synapse.handlers.auth import load_legacy_password_auth_providers
|
from synapse.handlers.auth import load_legacy_password_auth_providers
|
||||||
from synapse.http.site import SynapseSite
|
from synapse.http.site import SynapseSite
|
||||||
|
@ -582,6 +583,11 @@ async def start(hs: "HomeServer") -> None:
|
||||||
m = module(config, module_api)
|
m = module(config, module_api)
|
||||||
logger.info("Loaded module %s", m)
|
logger.info("Loaded module %s", m)
|
||||||
|
|
||||||
|
if hs.config.auto_accept_invites.enabled:
|
||||||
|
# Start the local auto_accept_invites module.
|
||||||
|
m = InviteAutoAccepter(hs.config.auto_accept_invites, module_api)
|
||||||
|
logger.info("Loaded local module %s", m)
|
||||||
|
|
||||||
load_legacy_spam_checkers(hs)
|
load_legacy_spam_checkers(hs)
|
||||||
load_legacy_third_party_event_rules(hs)
|
load_legacy_third_party_event_rules(hs)
|
||||||
load_legacy_presence_router(hs)
|
load_legacy_presence_router(hs)
|
||||||
|
@ -675,17 +681,17 @@ def setup_sentry(hs: "HomeServer") -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
# We set some default tags that give some context to this instance
|
# We set some default tags that give some context to this instance
|
||||||
with sentry_sdk.configure_scope() as scope:
|
global_scope = sentry_sdk.Scope.get_global_scope()
|
||||||
scope.set_tag("matrix_server_name", hs.config.server.server_name)
|
global_scope.set_tag("matrix_server_name", hs.config.server.server_name)
|
||||||
|
|
||||||
app = (
|
app = (
|
||||||
hs.config.worker.worker_app
|
hs.config.worker.worker_app
|
||||||
if hs.config.worker.worker_app
|
if hs.config.worker.worker_app
|
||||||
else "synapse.app.homeserver"
|
else "synapse.app.homeserver"
|
||||||
)
|
)
|
||||||
name = hs.get_instance_name()
|
name = hs.get_instance_name()
|
||||||
scope.set_tag("worker_app", app)
|
global_scope.set_tag("worker_app", app)
|
||||||
scope.set_tag("worker_name", name)
|
global_scope.set_tag("worker_name", name)
|
||||||
|
|
||||||
|
|
||||||
def setup_sdnotify(hs: "HomeServer") -> None:
|
def setup_sdnotify(hs: "HomeServer") -> None:
|
||||||
|
|
|
@ -23,6 +23,7 @@ from synapse.config import ( # noqa: F401
|
||||||
api,
|
api,
|
||||||
appservice,
|
appservice,
|
||||||
auth,
|
auth,
|
||||||
|
auto_accept_invites,
|
||||||
background_updates,
|
background_updates,
|
||||||
cache,
|
cache,
|
||||||
captcha,
|
captcha,
|
||||||
|
@ -35,6 +36,7 @@ from synapse.config import ( # noqa: F401
|
||||||
jwt,
|
jwt,
|
||||||
key,
|
key,
|
||||||
logger,
|
logger,
|
||||||
|
meow,
|
||||||
metrics,
|
metrics,
|
||||||
modules,
|
modules,
|
||||||
oembed,
|
oembed,
|
||||||
|
@ -91,6 +93,7 @@ class RootConfig:
|
||||||
voip: voip.VoipConfig
|
voip: voip.VoipConfig
|
||||||
registration: registration.RegistrationConfig
|
registration: registration.RegistrationConfig
|
||||||
account_validity: account_validity.AccountValidityConfig
|
account_validity: account_validity.AccountValidityConfig
|
||||||
|
meow: meow.MeowConfig
|
||||||
metrics: metrics.MetricsConfig
|
metrics: metrics.MetricsConfig
|
||||||
api: api.ApiConfig
|
api: api.ApiConfig
|
||||||
appservice: appservice.AppServiceConfig
|
appservice: appservice.AppServiceConfig
|
||||||
|
@ -120,6 +123,7 @@ class RootConfig:
|
||||||
federation: federation.FederationConfig
|
federation: federation.FederationConfig
|
||||||
retention: retention.RetentionConfig
|
retention: retention.RetentionConfig
|
||||||
background_updates: background_updates.BackgroundUpdateConfig
|
background_updates: background_updates.BackgroundUpdateConfig
|
||||||
|
auto_accept_invites: auto_accept_invites.AutoAcceptInvitesConfig
|
||||||
|
|
||||||
config_classes: List[Type["Config"]] = ...
|
config_classes: List[Type["Config"]] = ...
|
||||||
config_files: List[str]
|
config_files: List[str]
|
||||||
|
|
43
synapse/config/auto_accept_invites.py
Normal file
43
synapse/config/auto_accept_invites.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#
|
||||||
|
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024 New Vector, Ltd
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# See the GNU Affero General Public License for more details:
|
||||||
|
# <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||||
|
#
|
||||||
|
# Originally licensed under the Apache License, Version 2.0:
|
||||||
|
# <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||||
|
#
|
||||||
|
# [This file includes modifications made by New Vector Limited]
|
||||||
|
#
|
||||||
|
#
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from synapse.types import JsonDict
|
||||||
|
|
||||||
|
from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
|
class AutoAcceptInvitesConfig(Config):
|
||||||
|
section = "auto_accept_invites"
|
||||||
|
|
||||||
|
def read_config(self, config: JsonDict, **kwargs: Any) -> None:
|
||||||
|
auto_accept_invites_config = config.get("auto_accept_invites") or {}
|
||||||
|
|
||||||
|
self.enabled = auto_accept_invites_config.get("enabled", False)
|
||||||
|
|
||||||
|
self.accept_invites_only_for_direct_messages = auto_accept_invites_config.get(
|
||||||
|
"only_for_direct_messages", False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.accept_invites_only_from_local_users = auto_accept_invites_config.get(
|
||||||
|
"only_from_local_users", False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.worker_to_run_on = auto_accept_invites_config.get("worker_to_run_on")
|
|
@ -66,6 +66,17 @@ class CasConfig(Config):
|
||||||
|
|
||||||
self.cas_enable_registration = cas_config.get("enable_registration", True)
|
self.cas_enable_registration = cas_config.get("enable_registration", True)
|
||||||
|
|
||||||
|
self.cas_allow_numeric_ids = cas_config.get("allow_numeric_ids")
|
||||||
|
self.cas_numeric_ids_prefix = cas_config.get("numeric_ids_prefix")
|
||||||
|
if (
|
||||||
|
self.cas_numeric_ids_prefix is not None
|
||||||
|
and self.cas_numeric_ids_prefix.isalnum() is False
|
||||||
|
):
|
||||||
|
raise ConfigError(
|
||||||
|
"Only alphanumeric characters are allowed for numeric IDs prefix",
|
||||||
|
("cas_config", "numeric_ids_prefix"),
|
||||||
|
)
|
||||||
|
|
||||||
self.idp_name = cas_config.get("idp_name", "CAS")
|
self.idp_name = cas_config.get("idp_name", "CAS")
|
||||||
self.idp_icon = cas_config.get("idp_icon")
|
self.idp_icon = cas_config.get("idp_icon")
|
||||||
self.idp_brand = cas_config.get("idp_brand")
|
self.idp_brand = cas_config.get("idp_brand")
|
||||||
|
@ -77,6 +88,8 @@ class CasConfig(Config):
|
||||||
self.cas_displayname_attribute = None
|
self.cas_displayname_attribute = None
|
||||||
self.cas_required_attributes = []
|
self.cas_required_attributes = []
|
||||||
self.cas_enable_registration = False
|
self.cas_enable_registration = False
|
||||||
|
self.cas_allow_numeric_ids = False
|
||||||
|
self.cas_numeric_ids_prefix = "u"
|
||||||
|
|
||||||
|
|
||||||
# CAS uses a legacy required attributes mapping, not the one provided by
|
# CAS uses a legacy required attributes mapping, not the one provided by
|
||||||
|
|
|
@ -52,6 +52,7 @@ DEFAULT_SUBJECTS = {
|
||||||
"invite_from_person_to_space": "[%(app)s] %(person)s has invited you to join the %(space)s space on %(app)s...",
|
"invite_from_person_to_space": "[%(app)s] %(person)s has invited you to join the %(space)s space on %(app)s...",
|
||||||
"password_reset": "[%(server_name)s] Password reset",
|
"password_reset": "[%(server_name)s] Password reset",
|
||||||
"email_validation": "[%(server_name)s] Validate your email",
|
"email_validation": "[%(server_name)s] Validate your email",
|
||||||
|
"email_already_in_use": "[%(server_name)s] Email already in use",
|
||||||
}
|
}
|
||||||
|
|
||||||
LEGACY_TEMPLATE_DIR_WARNING = """
|
LEGACY_TEMPLATE_DIR_WARNING = """
|
||||||
|
@ -76,6 +77,7 @@ class EmailSubjectConfig:
|
||||||
invite_from_person_to_space: str
|
invite_from_person_to_space: str
|
||||||
password_reset: str
|
password_reset: str
|
||||||
email_validation: str
|
email_validation: str
|
||||||
|
email_already_in_use: str
|
||||||
|
|
||||||
|
|
||||||
class EmailConfig(Config):
|
class EmailConfig(Config):
|
||||||
|
@ -180,6 +182,12 @@ class EmailConfig(Config):
|
||||||
registration_template_text = email_config.get(
|
registration_template_text = email_config.get(
|
||||||
"registration_template_text", "registration.txt"
|
"registration_template_text", "registration.txt"
|
||||||
)
|
)
|
||||||
|
already_in_use_template_html = email_config.get(
|
||||||
|
"already_in_use_template_html", "already_in_use.html"
|
||||||
|
)
|
||||||
|
already_in_use_template_text = email_config.get(
|
||||||
|
"already_in_use_template_html", "already_in_use.txt"
|
||||||
|
)
|
||||||
add_threepid_template_html = email_config.get(
|
add_threepid_template_html = email_config.get(
|
||||||
"add_threepid_template_html", "add_threepid.html"
|
"add_threepid_template_html", "add_threepid.html"
|
||||||
)
|
)
|
||||||
|
@ -215,6 +223,8 @@ class EmailConfig(Config):
|
||||||
self.email_password_reset_template_text,
|
self.email_password_reset_template_text,
|
||||||
self.email_registration_template_html,
|
self.email_registration_template_html,
|
||||||
self.email_registration_template_text,
|
self.email_registration_template_text,
|
||||||
|
self.email_already_in_use_template_html,
|
||||||
|
self.email_already_in_use_template_text,
|
||||||
self.email_add_threepid_template_html,
|
self.email_add_threepid_template_html,
|
||||||
self.email_add_threepid_template_text,
|
self.email_add_threepid_template_text,
|
||||||
self.email_password_reset_template_confirmation_html,
|
self.email_password_reset_template_confirmation_html,
|
||||||
|
@ -230,6 +240,8 @@ class EmailConfig(Config):
|
||||||
password_reset_template_text,
|
password_reset_template_text,
|
||||||
registration_template_html,
|
registration_template_html,
|
||||||
registration_template_text,
|
registration_template_text,
|
||||||
|
already_in_use_template_html,
|
||||||
|
already_in_use_template_text,
|
||||||
add_threepid_template_html,
|
add_threepid_template_html,
|
||||||
add_threepid_template_text,
|
add_threepid_template_text,
|
||||||
"password_reset_confirmation.html",
|
"password_reset_confirmation.html",
|
||||||
|
|
|
@ -332,6 +332,9 @@ class ExperimentalConfig(Config):
|
||||||
# MSC3391: Removing account data.
|
# MSC3391: Removing account data.
|
||||||
self.msc3391_enabled = experimental.get("msc3391_enabled", False)
|
self.msc3391_enabled = experimental.get("msc3391_enabled", False)
|
||||||
|
|
||||||
|
# MSC3575 (Sliding Sync API endpoints)
|
||||||
|
self.msc3575_enabled: bool = experimental.get("msc3575_enabled", False)
|
||||||
|
|
||||||
# MSC3773: Thread notifications
|
# MSC3773: Thread notifications
|
||||||
self.msc3773_enabled: bool = experimental.get("msc3773_enabled", False)
|
self.msc3773_enabled: bool = experimental.get("msc3773_enabled", False)
|
||||||
|
|
||||||
|
@ -390,14 +393,6 @@ class ExperimentalConfig(Config):
|
||||||
# MSC3391: Removing account data.
|
# MSC3391: Removing account data.
|
||||||
self.msc3391_enabled = experimental.get("msc3391_enabled", False)
|
self.msc3391_enabled = experimental.get("msc3391_enabled", False)
|
||||||
|
|
||||||
# MSC3967: Do not require UIA when first uploading cross signing keys
|
|
||||||
self.msc3967_enabled = experimental.get("msc3967_enabled", False)
|
|
||||||
|
|
||||||
# MSC3981: Recurse relations
|
|
||||||
self.msc3981_recurse_relations = experimental.get(
|
|
||||||
"msc3981_recurse_relations", False
|
|
||||||
)
|
|
||||||
|
|
||||||
# MSC3861: Matrix architecture change to delegate authentication via OIDC
|
# MSC3861: Matrix architecture change to delegate authentication via OIDC
|
||||||
try:
|
try:
|
||||||
self.msc3861 = MSC3861(**experimental.get("msc3861", {}))
|
self.msc3861 = MSC3861(**experimental.get("msc3861", {}))
|
||||||
|
@ -409,11 +404,6 @@ class ExperimentalConfig(Config):
|
||||||
# Check that none of the other config options conflict with MSC3861 when enabled
|
# Check that none of the other config options conflict with MSC3861 when enabled
|
||||||
self.msc3861.check_config_conflicts(self.root)
|
self.msc3861.check_config_conflicts(self.root)
|
||||||
|
|
||||||
# MSC4010: Do not allow setting m.push_rules account data.
|
|
||||||
self.msc4010_push_rules_account_data = experimental.get(
|
|
||||||
"msc4010_push_rules_account_data", False
|
|
||||||
)
|
|
||||||
|
|
||||||
self.msc4028_push_encrypted_events = experimental.get(
|
self.msc4028_push_encrypted_events = experimental.get(
|
||||||
"msc4028_push_encrypted_events", False
|
"msc4028_push_encrypted_events", False
|
||||||
)
|
)
|
||||||
|
@ -421,3 +411,38 @@ class ExperimentalConfig(Config):
|
||||||
self.msc4069_profile_inhibit_propagation = experimental.get(
|
self.msc4069_profile_inhibit_propagation = experimental.get(
|
||||||
"msc4069_profile_inhibit_propagation", False
|
"msc4069_profile_inhibit_propagation", False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code
|
||||||
|
self.msc4108_enabled = experimental.get("msc4108_enabled", False)
|
||||||
|
|
||||||
|
self.msc4108_delegation_endpoint: Optional[str] = experimental.get(
|
||||||
|
"msc4108_delegation_endpoint", None
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.msc4108_enabled or self.msc4108_delegation_endpoint is not None
|
||||||
|
) and not self.msc3861.enabled:
|
||||||
|
raise ConfigError(
|
||||||
|
"MSC4108 requires MSC3861 to be enabled",
|
||||||
|
("experimental", "msc4108_delegation_endpoint"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.msc4108_delegation_endpoint is not None and self.msc4108_enabled:
|
||||||
|
raise ConfigError(
|
||||||
|
"You cannot have MSC4108 both enabled and delegated at the same time",
|
||||||
|
("experimental", "msc4108_delegation_endpoint"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.msc3823_account_suspension = experimental.get(
|
||||||
|
"msc3823_account_suspension", False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.msc3916_authenticated_media_enabled = experimental.get(
|
||||||
|
"msc3916_authenticated_media_enabled", False
|
||||||
|
)
|
||||||
|
|
||||||
|
# MSC4151: Report room API (Client-Server API)
|
||||||
|
self.msc4151_enabled: bool = experimental.get("msc4151_enabled", False)
|
||||||
|
|
||||||
|
# MSC4156: Migrate server_name to via
|
||||||
|
self.msc4156_enabled: bool = experimental.get("msc4156_enabled", False)
|
||||||
|
|
|
@ -42,6 +42,10 @@ class FederationConfig(Config):
|
||||||
for domain in federation_domain_whitelist:
|
for domain in federation_domain_whitelist:
|
||||||
self.federation_domain_whitelist[domain] = True
|
self.federation_domain_whitelist[domain] = True
|
||||||
|
|
||||||
|
self.federation_whitelist_endpoint_enabled = config.get(
|
||||||
|
"federation_whitelist_endpoint_enabled", False
|
||||||
|
)
|
||||||
|
|
||||||
federation_metrics_domains = config.get("federation_metrics_domains") or []
|
federation_metrics_domains = config.get("federation_metrics_domains") or []
|
||||||
validate_config(
|
validate_config(
|
||||||
_METRICS_FOR_DOMAINS_SCHEMA,
|
_METRICS_FOR_DOMAINS_SCHEMA,
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
from ._base import RootConfig
|
from ._base import RootConfig
|
||||||
|
from .meow import MeowConfig
|
||||||
from .account_validity import AccountValidityConfig
|
from .account_validity import AccountValidityConfig
|
||||||
from .api import ApiConfig
|
from .api import ApiConfig
|
||||||
from .appservice import AppServiceConfig
|
from .appservice import AppServiceConfig
|
||||||
from .auth import AuthConfig
|
from .auth import AuthConfig
|
||||||
|
from .auto_accept_invites import AutoAcceptInvitesConfig
|
||||||
from .background_updates import BackgroundUpdateConfig
|
from .background_updates import BackgroundUpdateConfig
|
||||||
from .cache import CacheConfig
|
from .cache import CacheConfig
|
||||||
from .captcha import CaptchaConfig
|
from .captcha import CaptchaConfig
|
||||||
|
@ -64,6 +66,7 @@ from .workers import WorkerConfig
|
||||||
|
|
||||||
class HomeServerConfig(RootConfig):
|
class HomeServerConfig(RootConfig):
|
||||||
config_classes = [
|
config_classes = [
|
||||||
|
MeowConfig,
|
||||||
ModulesConfig,
|
ModulesConfig,
|
||||||
ServerConfig,
|
ServerConfig,
|
||||||
RetentionConfig,
|
RetentionConfig,
|
||||||
|
@ -105,4 +108,5 @@ class HomeServerConfig(RootConfig):
|
||||||
RedisConfig,
|
RedisConfig,
|
||||||
ExperimentalConfig,
|
ExperimentalConfig,
|
||||||
BackgroundUpdateConfig,
|
BackgroundUpdateConfig,
|
||||||
|
AutoAcceptInvitesConfig,
|
||||||
]
|
]
|
||||||
|
|
33
synapse/config/meow.py
Normal file
33
synapse/config/meow.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2020 Maunium
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
|
class MeowConfig(Config):
|
||||||
|
"""Meow Configuration
|
||||||
|
Configuration for disabling dumb limits in Synapse
|
||||||
|
"""
|
||||||
|
|
||||||
|
section = "meow"
|
||||||
|
|
||||||
|
def read_config(self, config, **kwargs):
|
||||||
|
meow_config = config.get("meow", {})
|
||||||
|
self.validation_override = set(meow_config.get("validation_override", []))
|
||||||
|
self.filter_override = set(meow_config.get("filter_override", []))
|
||||||
|
self.timestamp_override = set(meow_config.get("timestamp_override", []))
|
||||||
|
self.admin_api_register_invalid = meow_config.get(
|
||||||
|
"admin_api_register_invalid", True
|
||||||
|
)
|
|
@ -218,3 +218,13 @@ class RatelimitConfig(Config):
|
||||||
"rc_media_create",
|
"rc_media_create",
|
||||||
defaults={"per_second": 10, "burst_count": 50},
|
defaults={"per_second": 10, "burst_count": 50},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.remote_media_downloads = RatelimitSettings(
|
||||||
|
key="rc_remote_media_downloads",
|
||||||
|
per_second=self.parse_size(
|
||||||
|
config.get("remote_media_download_per_second", "87K")
|
||||||
|
),
|
||||||
|
burst_count=self.parse_size(
|
||||||
|
config.get("remote_media_download_burst_count", "500M")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
|
@ -54,10 +54,8 @@ THUMBNAIL_SIZE_YAML = """\
|
||||||
THUMBNAIL_SUPPORTED_MEDIA_FORMAT_MAP = {
|
THUMBNAIL_SUPPORTED_MEDIA_FORMAT_MAP = {
|
||||||
"image/jpeg": "jpeg",
|
"image/jpeg": "jpeg",
|
||||||
"image/jpg": "jpeg",
|
"image/jpg": "jpeg",
|
||||||
"image/webp": "jpeg",
|
"image/webp": "webp",
|
||||||
# Thumbnails can only be jpeg or png. We choose png thumbnails for gif
|
"image/gif": "webp",
|
||||||
# because it can have transparency.
|
|
||||||
"image/gif": "png",
|
|
||||||
"image/png": "png",
|
"image/png": "png",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +107,10 @@ def parse_thumbnail_requirements(
|
||||||
requirement.append(
|
requirement.append(
|
||||||
ThumbnailRequirement(width, height, method, "image/png")
|
ThumbnailRequirement(width, height, method, "image/png")
|
||||||
)
|
)
|
||||||
|
elif thumbnail_format == "webp":
|
||||||
|
requirement.append(
|
||||||
|
ThumbnailRequirement(width, height, method, "image/webp")
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Unknown thumbnail mapping from %s to %s. This is a Synapse problem, please report!"
|
"Unknown thumbnail mapping from %s to %s. This is a Synapse problem, please report!"
|
||||||
|
|
|
@ -156,6 +156,8 @@ class WriterLocations:
|
||||||
can only be a single instance.
|
can only be a single instance.
|
||||||
presence: The instances that write to the presence stream. Currently
|
presence: The instances that write to the presence stream. Currently
|
||||||
can only be a single instance.
|
can only be a single instance.
|
||||||
|
push_rules: The instances that write to the push stream. Currently
|
||||||
|
can only be a single instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
events: List[str] = attr.ib(
|
events: List[str] = attr.ib(
|
||||||
|
@ -182,6 +184,10 @@ class WriterLocations:
|
||||||
default=["master"],
|
default=["master"],
|
||||||
converter=_instance_to_list_converter,
|
converter=_instance_to_list_converter,
|
||||||
)
|
)
|
||||||
|
push_rules: List[str] = attr.ib(
|
||||||
|
default=["master"],
|
||||||
|
converter=_instance_to_list_converter,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@attr.s(auto_attribs=True)
|
||||||
|
@ -341,6 +347,7 @@ class WorkerConfig(Config):
|
||||||
"account_data",
|
"account_data",
|
||||||
"receipts",
|
"receipts",
|
||||||
"presence",
|
"presence",
|
||||||
|
"push_rules",
|
||||||
):
|
):
|
||||||
instances = _instance_to_list_converter(getattr(self.writers, stream))
|
instances = _instance_to_list_converter(getattr(self.writers, stream))
|
||||||
for instance in instances:
|
for instance in instances:
|
||||||
|
@ -378,6 +385,11 @@ class WorkerConfig(Config):
|
||||||
"Must only specify one instance to handle `presence` messages."
|
"Must only specify one instance to handle `presence` messages."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if len(self.writers.push_rules) != 1:
|
||||||
|
raise ConfigError(
|
||||||
|
"Must only specify one instance to handle `push` messages."
|
||||||
|
)
|
||||||
|
|
||||||
self.events_shard_config = RoutableShardedWorkerHandlingConfig(
|
self.events_shard_config = RoutableShardedWorkerHandlingConfig(
|
||||||
self.writers.events
|
self.writers.events
|
||||||
)
|
)
|
||||||
|
|
196
synapse/events/auto_accept_invites.py
Normal file
196
synapse/events/auto_accept_invites.py
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
#
|
||||||
|
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
|
#
|
||||||
|
# Copyright 2021 The Matrix.org Foundation C.I.C
|
||||||
|
# Copyright (C) 2024 New Vector, Ltd
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# See the GNU Affero General Public License for more details:
|
||||||
|
# <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||||
|
#
|
||||||
|
# Originally licensed under the Apache License, Version 2.0:
|
||||||
|
# <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||||
|
#
|
||||||
|
# [This file includes modifications made by New Vector Limited]
|
||||||
|
#
|
||||||
|
#
|
||||||
|
import logging
|
||||||
|
from http import HTTPStatus
|
||||||
|
from typing import Any, Dict, Tuple
|
||||||
|
|
||||||
|
from synapse.api.constants import AccountDataTypes, EventTypes, Membership
|
||||||
|
from synapse.api.errors import SynapseError
|
||||||
|
from synapse.config.auto_accept_invites import AutoAcceptInvitesConfig
|
||||||
|
from synapse.module_api import EventBase, ModuleApi, run_as_background_process
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class InviteAutoAccepter:
|
||||||
|
def __init__(self, config: AutoAcceptInvitesConfig, api: ModuleApi):
|
||||||
|
# Keep a reference to the Module API.
|
||||||
|
self._api = api
|
||||||
|
self._config = config
|
||||||
|
|
||||||
|
if not self._config.enabled:
|
||||||
|
return
|
||||||
|
|
||||||
|
should_run_on_this_worker = config.worker_to_run_on == self._api.worker_name
|
||||||
|
|
||||||
|
if not should_run_on_this_worker:
|
||||||
|
logger.info(
|
||||||
|
"Not accepting invites on this worker (configured: %r, here: %r)",
|
||||||
|
config.worker_to_run_on,
|
||||||
|
self._api.worker_name,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Accepting invites on this worker (here: %r)", self._api.worker_name
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register the callback.
|
||||||
|
self._api.register_third_party_rules_callbacks(
|
||||||
|
on_new_event=self.on_new_event,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def on_new_event(self, event: EventBase, *args: Any) -> None:
|
||||||
|
"""Listens for new events, and if the event is an invite for a local user then
|
||||||
|
automatically accepts it.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event: The incoming event.
|
||||||
|
"""
|
||||||
|
# Check if the event is an invite for a local user.
|
||||||
|
is_invite_for_local_user = (
|
||||||
|
event.type == EventTypes.Member
|
||||||
|
and event.is_state()
|
||||||
|
and event.membership == Membership.INVITE
|
||||||
|
and self._api.is_mine(event.state_key)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only accept invites for direct messages if the configuration mandates it.
|
||||||
|
is_direct_message = event.content.get("is_direct", False)
|
||||||
|
is_allowed_by_direct_message_rules = (
|
||||||
|
not self._config.accept_invites_only_for_direct_messages
|
||||||
|
or is_direct_message is True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only accept invites from remote users if the configuration mandates it.
|
||||||
|
is_from_local_user = self._api.is_mine(event.sender)
|
||||||
|
is_allowed_by_local_user_rules = (
|
||||||
|
not self._config.accept_invites_only_from_local_users
|
||||||
|
or is_from_local_user is True
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
is_invite_for_local_user
|
||||||
|
and is_allowed_by_direct_message_rules
|
||||||
|
and is_allowed_by_local_user_rules
|
||||||
|
):
|
||||||
|
# Make the user join the room. We run this as a background process to circumvent a race condition
|
||||||
|
# that occurs when responding to invites over federation (see https://github.com/matrix-org/synapse-auto-accept-invite/issues/12)
|
||||||
|
run_as_background_process(
|
||||||
|
"retry_make_join",
|
||||||
|
self._retry_make_join,
|
||||||
|
event.state_key,
|
||||||
|
event.state_key,
|
||||||
|
event.room_id,
|
||||||
|
"join",
|
||||||
|
bg_start_span=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_direct_message:
|
||||||
|
# Mark this room as a direct message!
|
||||||
|
await self._mark_room_as_direct_message(
|
||||||
|
event.state_key, event.sender, event.room_id
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _mark_room_as_direct_message(
|
||||||
|
self, user_id: str, dm_user_id: str, room_id: str
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Marks a room (`room_id`) as a direct message with the counterparty `dm_user_id`
|
||||||
|
from the perspective of the user `user_id`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: the user for whom the membership is changing
|
||||||
|
dm_user_id: the user performing the membership change
|
||||||
|
room_id: room id of the room the user is invited to
|
||||||
|
"""
|
||||||
|
|
||||||
|
# This is a dict of User IDs to tuples of Room IDs
|
||||||
|
# (get_global will return a frozendict of tuples as it freezes the data,
|
||||||
|
# but we should accept either frozen or unfrozen variants.)
|
||||||
|
# Be careful: we convert the outer frozendict into a dict here,
|
||||||
|
# but the contents of the dict are still frozen (tuples in lieu of lists,
|
||||||
|
# etc.)
|
||||||
|
dm_map: Dict[str, Tuple[str, ...]] = dict(
|
||||||
|
await self._api.account_data_manager.get_global(
|
||||||
|
user_id, AccountDataTypes.DIRECT
|
||||||
|
)
|
||||||
|
or {}
|
||||||
|
)
|
||||||
|
|
||||||
|
if dm_user_id not in dm_map:
|
||||||
|
dm_map[dm_user_id] = (room_id,)
|
||||||
|
else:
|
||||||
|
dm_rooms_for_user = dm_map[dm_user_id]
|
||||||
|
assert isinstance(dm_rooms_for_user, (tuple, list))
|
||||||
|
|
||||||
|
dm_map[dm_user_id] = tuple(dm_rooms_for_user) + (room_id,)
|
||||||
|
|
||||||
|
await self._api.account_data_manager.put_global(
|
||||||
|
user_id, AccountDataTypes.DIRECT, dm_map
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _retry_make_join(
|
||||||
|
self, sender: str, target: str, room_id: str, new_membership: str
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
A function to retry sending the `make_join` request with an increasing backoff. This is
|
||||||
|
implemented to work around a race condition when receiving invites over federation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sender: the user performing the membership change
|
||||||
|
target: the user for whom the membership is changing
|
||||||
|
room_id: room id of the room to join to
|
||||||
|
new_membership: the type of membership event (in this case will be "join")
|
||||||
|
"""
|
||||||
|
|
||||||
|
sleep = 0
|
||||||
|
retries = 0
|
||||||
|
join_event = None
|
||||||
|
|
||||||
|
while retries < 5:
|
||||||
|
try:
|
||||||
|
await self._api.sleep(sleep)
|
||||||
|
join_event = await self._api.update_room_membership(
|
||||||
|
sender=sender,
|
||||||
|
target=target,
|
||||||
|
room_id=room_id,
|
||||||
|
new_membership=new_membership,
|
||||||
|
)
|
||||||
|
except SynapseError as e:
|
||||||
|
if e.code == HTTPStatus.FORBIDDEN:
|
||||||
|
logger.debug(
|
||||||
|
f"Update_room_membership was forbidden. This can sometimes be expected for remote invites. Exception: {e}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.warn(
|
||||||
|
f"Update_room_membership raised the following unexpected (SynapseError) exception: {e}"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warn(
|
||||||
|
f"Update_room_membership raised the following unexpected exception: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
|
sleep = 2**retries
|
||||||
|
retries += 1
|
||||||
|
|
||||||
|
if join_event is not None:
|
||||||
|
break
|
|
@ -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
|
from . import EventBase, make_event_from_dict
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.handlers.relations import BundledAggregations
|
from synapse.handlers.relations import BundledAggregations
|
||||||
|
@ -82,17 +82,15 @@ def prune_event(event: EventBase) -> EventBase:
|
||||||
"""
|
"""
|
||||||
pruned_event_dict = prune_event_dict(event.room_version, event.get_dict())
|
pruned_event_dict = prune_event_dict(event.room_version, event.get_dict())
|
||||||
|
|
||||||
from . import make_event_from_dict
|
|
||||||
|
|
||||||
pruned_event = make_event_from_dict(
|
pruned_event = make_event_from_dict(
|
||||||
pruned_event_dict, event.room_version, event.internal_metadata.get_dict()
|
pruned_event_dict, event.room_version, event.internal_metadata.get_dict()
|
||||||
)
|
)
|
||||||
|
|
||||||
# copy the internal fields
|
# Copy the bits of `internal_metadata` that aren't returned by `get_dict`
|
||||||
pruned_event.internal_metadata.stream_ordering = (
|
pruned_event.internal_metadata.stream_ordering = (
|
||||||
event.internal_metadata.stream_ordering
|
event.internal_metadata.stream_ordering
|
||||||
)
|
)
|
||||||
|
pruned_event.internal_metadata.instance_name = event.internal_metadata.instance_name
|
||||||
pruned_event.internal_metadata.outlier = event.internal_metadata.outlier
|
pruned_event.internal_metadata.outlier = event.internal_metadata.outlier
|
||||||
|
|
||||||
# Mark the event as redacted
|
# Mark the event as redacted
|
||||||
|
@ -101,6 +99,30 @@ def prune_event(event: EventBase) -> EventBase:
|
||||||
return pruned_event
|
return pruned_event
|
||||||
|
|
||||||
|
|
||||||
|
def clone_event(event: EventBase) -> EventBase:
|
||||||
|
"""Take a copy of the event.
|
||||||
|
|
||||||
|
This is mostly useful because it does a *shallow* copy of the `unsigned` data,
|
||||||
|
which means it can then be updated without corrupting the in-memory cache. Note that
|
||||||
|
other properties of the event, such as `content`, are *not* (currently) copied here.
|
||||||
|
"""
|
||||||
|
# XXX: We rely on at least one of `event.get_dict()` and `make_event_from_dict()`
|
||||||
|
# making a copy of `unsigned`. Currently, both do, though I don't really know why.
|
||||||
|
# Still, as long as they do, there's not much point doing yet another copy here.
|
||||||
|
new_event = make_event_from_dict(
|
||||||
|
event.get_dict(), event.room_version, event.internal_metadata.get_dict()
|
||||||
|
)
|
||||||
|
|
||||||
|
# Copy the bits of `internal_metadata` that aren't returned by `get_dict`.
|
||||||
|
new_event.internal_metadata.stream_ordering = (
|
||||||
|
event.internal_metadata.stream_ordering
|
||||||
|
)
|
||||||
|
new_event.internal_metadata.instance_name = event.internal_metadata.instance_name
|
||||||
|
new_event.internal_metadata.outlier = event.internal_metadata.outlier
|
||||||
|
|
||||||
|
return new_event
|
||||||
|
|
||||||
|
|
||||||
def prune_event_dict(room_version: RoomVersion, event_dict: JsonDict) -> JsonDict:
|
def prune_event_dict(room_version: RoomVersion, event_dict: JsonDict) -> JsonDict:
|
||||||
"""Redacts the event_dict in the same way as `prune_event`, except it
|
"""Redacts the event_dict in the same way as `prune_event`, except it
|
||||||
operates on dicts rather than event objects
|
operates on dicts rather than event objects
|
||||||
|
@ -485,6 +507,11 @@ def serialize_event(
|
||||||
):
|
):
|
||||||
d["unsigned"]["transaction_id"] = txn_id
|
d["unsigned"]["transaction_id"] = txn_id
|
||||||
|
|
||||||
|
# Beeper: include internal stream ordering as HS order unsigned hint
|
||||||
|
stream_ordering = getattr(e.internal_metadata, "stream_ordering", None)
|
||||||
|
if stream_ordering:
|
||||||
|
d["unsigned"]["com.beeper.hs.order"] = stream_ordering
|
||||||
|
|
||||||
# invite_room_state and knock_room_state are a list of stripped room state events
|
# invite_room_state and knock_room_state are a list of stripped room state events
|
||||||
# that are meant to provide metadata about a room to an invitee/knocker. They are
|
# that are meant to provide metadata about a room to an invitee/knocker. They are
|
||||||
# intended to only be included in specific circumstances, such as down sync, and
|
# intended to only be included in specific circumstances, such as down sync, and
|
||||||
|
|
|
@ -47,9 +47,9 @@ from synapse.events.utils import (
|
||||||
validate_canonicaljson,
|
validate_canonicaljson,
|
||||||
)
|
)
|
||||||
from synapse.http.servlet import validate_json_object
|
from synapse.http.servlet import validate_json_object
|
||||||
from synapse.rest.models import RequestBodyModel
|
|
||||||
from synapse.storage.controllers.state import server_acl_evaluator_from_event
|
from synapse.storage.controllers.state import server_acl_evaluator_from_event
|
||||||
from synapse.types import EventID, JsonDict, RoomID, StrCollection, UserID
|
from synapse.types import EventID, JsonDict, RoomID, StrCollection, UserID
|
||||||
|
from synapse.types.rest import RequestBodyModel
|
||||||
|
|
||||||
|
|
||||||
class EventValidator:
|
class EventValidator:
|
||||||
|
@ -64,7 +64,7 @@ class EventValidator:
|
||||||
event: The event to validate.
|
event: The event to validate.
|
||||||
config: The homeserver's configuration.
|
config: The homeserver's configuration.
|
||||||
"""
|
"""
|
||||||
self.validate_builder(event)
|
self.validate_builder(event, config)
|
||||||
|
|
||||||
if event.format_version == EventFormatVersions.ROOM_V1_V2:
|
if event.format_version == EventFormatVersions.ROOM_V1_V2:
|
||||||
EventID.from_string(event.event_id)
|
EventID.from_string(event.event_id)
|
||||||
|
@ -95,6 +95,12 @@ class EventValidator:
|
||||||
# Note that only the client controlled portion of the event is
|
# Note that only the client controlled portion of the event is
|
||||||
# checked, since we trust the portions of the event we created.
|
# checked, since we trust the portions of the event we created.
|
||||||
validate_canonicaljson(event.content)
|
validate_canonicaljson(event.content)
|
||||||
|
if not 0 < event.origin_server_ts < 2**53:
|
||||||
|
raise SynapseError(400, "Event timestamp is out of range")
|
||||||
|
|
||||||
|
# meow: allow specific users to send potentially dangerous events.
|
||||||
|
if event.sender in config.meow.validation_override:
|
||||||
|
return
|
||||||
|
|
||||||
if event.type == EventTypes.Aliases:
|
if event.type == EventTypes.Aliases:
|
||||||
if "aliases" in event.content:
|
if "aliases" in event.content:
|
||||||
|
@ -193,7 +199,9 @@ class EventValidator:
|
||||||
errcode=Codes.BAD_JSON,
|
errcode=Codes.BAD_JSON,
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_builder(self, event: Union[EventBase, EventBuilder]) -> None:
|
def validate_builder(
|
||||||
|
self, event: Union[EventBase, EventBuilder], config: HomeServerConfig
|
||||||
|
) -> None:
|
||||||
"""Validates that the builder/event has roughly the right format. Only
|
"""Validates that the builder/event has roughly the right format. Only
|
||||||
checks values that we expect a proto event to have, rather than all the
|
checks values that we expect a proto event to have, rather than all the
|
||||||
fields an event would have
|
fields an event would have
|
||||||
|
@ -211,6 +219,10 @@ class EventValidator:
|
||||||
RoomID.from_string(event.room_id)
|
RoomID.from_string(event.room_id)
|
||||||
UserID.from_string(event.sender)
|
UserID.from_string(event.sender)
|
||||||
|
|
||||||
|
# meow: allow specific users to send so-called invalid events
|
||||||
|
if event.sender in config.meow.validation_override:
|
||||||
|
return
|
||||||
|
|
||||||
if event.type == EventTypes.Message:
|
if event.type == EventTypes.Message:
|
||||||
strings = ["body", "msgtype"]
|
strings = ["body", "msgtype"]
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ from synapse.api.errors import (
|
||||||
SynapseError,
|
SynapseError,
|
||||||
UnsupportedRoomVersionError,
|
UnsupportedRoomVersionError,
|
||||||
)
|
)
|
||||||
|
from synapse.api.ratelimiting import Ratelimiter
|
||||||
from synapse.api.room_versions import (
|
from synapse.api.room_versions import (
|
||||||
KNOWN_ROOM_VERSIONS,
|
KNOWN_ROOM_VERSIONS,
|
||||||
EventFormatVersions,
|
EventFormatVersions,
|
||||||
|
@ -1877,6 +1878,8 @@ class FederationClient(FederationBase):
|
||||||
output_stream: BinaryIO,
|
output_stream: BinaryIO,
|
||||||
max_size: int,
|
max_size: int,
|
||||||
max_timeout_ms: int,
|
max_timeout_ms: int,
|
||||||
|
download_ratelimiter: Ratelimiter,
|
||||||
|
ip_address: str,
|
||||||
) -> Tuple[int, Dict[bytes, List[bytes]]]:
|
) -> Tuple[int, Dict[bytes, List[bytes]]]:
|
||||||
try:
|
try:
|
||||||
return await self.transport_layer.download_media_v3(
|
return await self.transport_layer.download_media_v3(
|
||||||
|
@ -1885,6 +1888,8 @@ class FederationClient(FederationBase):
|
||||||
output_stream=output_stream,
|
output_stream=output_stream,
|
||||||
max_size=max_size,
|
max_size=max_size,
|
||||||
max_timeout_ms=max_timeout_ms,
|
max_timeout_ms=max_timeout_ms,
|
||||||
|
download_ratelimiter=download_ratelimiter,
|
||||||
|
ip_address=ip_address,
|
||||||
)
|
)
|
||||||
except HttpResponseException as e:
|
except HttpResponseException as e:
|
||||||
# If an error is received that is due to an unrecognised endpoint,
|
# If an error is received that is due to an unrecognised endpoint,
|
||||||
|
@ -1905,6 +1910,8 @@ class FederationClient(FederationBase):
|
||||||
output_stream=output_stream,
|
output_stream=output_stream,
|
||||||
max_size=max_size,
|
max_size=max_size,
|
||||||
max_timeout_ms=max_timeout_ms,
|
max_timeout_ms=max_timeout_ms,
|
||||||
|
download_ratelimiter=download_ratelimiter,
|
||||||
|
ip_address=ip_address,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -546,7 +546,25 @@ class FederationServer(FederationBase):
|
||||||
edu_type=edu_dict["edu_type"],
|
edu_type=edu_dict["edu_type"],
|
||||||
content=edu_dict["content"],
|
content=edu_dict["content"],
|
||||||
)
|
)
|
||||||
await self.registry.on_edu(edu.edu_type, origin, edu.content)
|
try:
|
||||||
|
await self.registry.on_edu(edu.edu_type, origin, edu.content)
|
||||||
|
except Exception:
|
||||||
|
# If there was an error handling the EDU, we must reject the
|
||||||
|
# transaction.
|
||||||
|
#
|
||||||
|
# Some EDU types (notably, to-device messages) are, despite their name,
|
||||||
|
# expected to be reliable; if we weren't able to do something with it,
|
||||||
|
# we have to tell the sender that, and the only way the protocol gives
|
||||||
|
# us to do so is by sending an HTTP error back on the transaction.
|
||||||
|
#
|
||||||
|
# We log the exception now, and then raise a new SynapseError to cause
|
||||||
|
# the transaction to be failed.
|
||||||
|
logger.exception("Error handling EDU of type %s", edu.edu_type)
|
||||||
|
raise SynapseError(500, f"Error handing EDU of type {edu.edu_type}")
|
||||||
|
|
||||||
|
# TODO: if the first EDU fails, we should probably abort the whole
|
||||||
|
# thing rather than carrying on with the rest of them. That would
|
||||||
|
# probably be best done inside `concurrently_execute`.
|
||||||
|
|
||||||
await concurrently_execute(
|
await concurrently_execute(
|
||||||
_process_edu,
|
_process_edu,
|
||||||
|
@ -656,7 +674,7 @@ class FederationServer(FederationBase):
|
||||||
# This is in addition to the HS-level rate limiting applied by
|
# This is in addition to the HS-level rate limiting applied by
|
||||||
# BaseFederationServlet.
|
# BaseFederationServlet.
|
||||||
# type-ignore: mypy doesn't seem able to deduce the type of the limiter(!?)
|
# type-ignore: mypy doesn't seem able to deduce the type of the limiter(!?)
|
||||||
await self._room_member_handler._join_rate_per_room_limiter.ratelimit( # type: ignore[has-type]
|
await self._room_member_handler._join_rate_per_room_limiter.ratelimit(
|
||||||
requester=None,
|
requester=None,
|
||||||
key=room_id,
|
key=room_id,
|
||||||
update=False,
|
update=False,
|
||||||
|
@ -699,7 +717,7 @@ class FederationServer(FederationBase):
|
||||||
SynapseTags.SEND_JOIN_RESPONSE_IS_PARTIAL_STATE,
|
SynapseTags.SEND_JOIN_RESPONSE_IS_PARTIAL_STATE,
|
||||||
caller_supports_partial_state,
|
caller_supports_partial_state,
|
||||||
)
|
)
|
||||||
await self._room_member_handler._join_rate_per_room_limiter.ratelimit( # type: ignore[has-type]
|
await self._room_member_handler._join_rate_per_room_limiter.ratelimit(
|
||||||
requester=None,
|
requester=None,
|
||||||
key=room_id,
|
key=room_id,
|
||||||
update=False,
|
update=False,
|
||||||
|
@ -1414,12 +1432,7 @@ class FederationHandlerRegistry:
|
||||||
handler = self.edu_handlers.get(edu_type)
|
handler = self.edu_handlers.get(edu_type)
|
||||||
if handler:
|
if handler:
|
||||||
with start_active_span_from_edu(content, "handle_edu"):
|
with start_active_span_from_edu(content, "handle_edu"):
|
||||||
try:
|
await handler(origin, content)
|
||||||
await handler(origin, content)
|
|
||||||
except SynapseError as e:
|
|
||||||
logger.info("Failed to handle edu %r: %r", edu_type, e)
|
|
||||||
except Exception:
|
|
||||||
logger.exception("Failed to handle edu %r", edu_type)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if we can route it somewhere else that isn't us
|
# Check if we can route it somewhere else that isn't us
|
||||||
|
@ -1428,17 +1441,12 @@ class FederationHandlerRegistry:
|
||||||
# Pick an instance randomly so that we don't overload one.
|
# Pick an instance randomly so that we don't overload one.
|
||||||
route_to = random.choice(instances)
|
route_to = random.choice(instances)
|
||||||
|
|
||||||
try:
|
await self._send_edu(
|
||||||
await self._send_edu(
|
instance_name=route_to,
|
||||||
instance_name=route_to,
|
edu_type=edu_type,
|
||||||
edu_type=edu_type,
|
origin=origin,
|
||||||
origin=origin,
|
content=content,
|
||||||
content=content,
|
)
|
||||||
)
|
|
||||||
except SynapseError as e:
|
|
||||||
logger.info("Failed to handle edu %r: %r", edu_type, e)
|
|
||||||
except Exception:
|
|
||||||
logger.exception("Failed to handle edu %r", edu_type)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Oh well, let's just log and move on.
|
# Oh well, let's just log and move on.
|
||||||
|
|
|
@ -43,6 +43,7 @@ import ijson
|
||||||
|
|
||||||
from synapse.api.constants import Direction, Membership
|
from synapse.api.constants import Direction, Membership
|
||||||
from synapse.api.errors import Codes, HttpResponseException, SynapseError
|
from synapse.api.errors import Codes, HttpResponseException, SynapseError
|
||||||
|
from synapse.api.ratelimiting import Ratelimiter
|
||||||
from synapse.api.room_versions import RoomVersion
|
from synapse.api.room_versions import RoomVersion
|
||||||
from synapse.api.urls import (
|
from synapse.api.urls import (
|
||||||
FEDERATION_UNSTABLE_PREFIX,
|
FEDERATION_UNSTABLE_PREFIX,
|
||||||
|
@ -819,6 +820,8 @@ class TransportLayerClient:
|
||||||
output_stream: BinaryIO,
|
output_stream: BinaryIO,
|
||||||
max_size: int,
|
max_size: int,
|
||||||
max_timeout_ms: int,
|
max_timeout_ms: int,
|
||||||
|
download_ratelimiter: Ratelimiter,
|
||||||
|
ip_address: str,
|
||||||
) -> Tuple[int, Dict[bytes, List[bytes]]]:
|
) -> Tuple[int, Dict[bytes, List[bytes]]]:
|
||||||
path = f"/_matrix/media/r0/download/{destination}/{media_id}"
|
path = f"/_matrix/media/r0/download/{destination}/{media_id}"
|
||||||
|
|
||||||
|
@ -834,6 +837,8 @@ class TransportLayerClient:
|
||||||
"allow_remote": "false",
|
"allow_remote": "false",
|
||||||
"timeout_ms": str(max_timeout_ms),
|
"timeout_ms": str(max_timeout_ms),
|
||||||
},
|
},
|
||||||
|
download_ratelimiter=download_ratelimiter,
|
||||||
|
ip_address=ip_address,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def download_media_v3(
|
async def download_media_v3(
|
||||||
|
@ -843,6 +848,8 @@ class TransportLayerClient:
|
||||||
output_stream: BinaryIO,
|
output_stream: BinaryIO,
|
||||||
max_size: int,
|
max_size: int,
|
||||||
max_timeout_ms: int,
|
max_timeout_ms: int,
|
||||||
|
download_ratelimiter: Ratelimiter,
|
||||||
|
ip_address: str,
|
||||||
) -> Tuple[int, Dict[bytes, List[bytes]]]:
|
) -> Tuple[int, Dict[bytes, List[bytes]]]:
|
||||||
path = f"/_matrix/media/v3/download/{destination}/{media_id}"
|
path = f"/_matrix/media/v3/download/{destination}/{media_id}"
|
||||||
|
|
||||||
|
@ -862,6 +869,8 @@ class TransportLayerClient:
|
||||||
"allow_redirect": "true",
|
"allow_redirect": "true",
|
||||||
},
|
},
|
||||||
follow_redirects=True,
|
follow_redirects=True,
|
||||||
|
download_ratelimiter=download_ratelimiter,
|
||||||
|
ip_address=ip_address,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ from synapse.federation.transport.server.federation import (
|
||||||
FEDERATION_SERVLET_CLASSES,
|
FEDERATION_SERVLET_CLASSES,
|
||||||
FederationAccountStatusServlet,
|
FederationAccountStatusServlet,
|
||||||
FederationUnstableClientKeysClaimServlet,
|
FederationUnstableClientKeysClaimServlet,
|
||||||
|
FederationUnstableMediaDownloadServlet,
|
||||||
)
|
)
|
||||||
from synapse.http.server import HttpServer, JsonResource
|
from synapse.http.server import HttpServer, JsonResource
|
||||||
from synapse.http.servlet import (
|
from synapse.http.servlet import (
|
||||||
|
@ -315,6 +316,13 @@ def register_servlets(
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if servletclass == FederationUnstableMediaDownloadServlet:
|
||||||
|
if (
|
||||||
|
not hs.config.server.enable_media_repo
|
||||||
|
or not hs.config.experimental.msc3916_authenticated_media_enabled
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
servletclass(
|
servletclass(
|
||||||
hs=hs,
|
hs=hs,
|
||||||
authenticator=authenticator,
|
authenticator=authenticator,
|
||||||
|
|
|
@ -180,7 +180,11 @@ def _parse_auth_header(header_bytes: bytes) -> Tuple[str, str, str, Optional[str
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
header_str = header_bytes.decode("utf-8")
|
header_str = header_bytes.decode("utf-8")
|
||||||
params = re.split(" +", header_str)[1].split(",")
|
space_or_tab = "[ \t]"
|
||||||
|
params = re.split(
|
||||||
|
rf"{space_or_tab}*,{space_or_tab}*",
|
||||||
|
re.split(r"^X-Matrix +", header_str, maxsplit=1)[1],
|
||||||
|
)
|
||||||
param_dict: Dict[str, str] = {
|
param_dict: Dict[str, str] = {
|
||||||
k.lower(): v for k, v in [param.split("=", maxsplit=1) for param in params]
|
k.lower(): v for k, v in [param.split("=", maxsplit=1) for param in params]
|
||||||
}
|
}
|
||||||
|
@ -356,13 +360,29 @@ class BaseFederationServlet:
|
||||||
"request"
|
"request"
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
if (
|
||||||
|
func.__self__.__class__.__name__ # type: ignore
|
||||||
|
== "FederationUnstableMediaDownloadServlet"
|
||||||
|
):
|
||||||
|
response = await func(
|
||||||
|
origin, content, request, *args, **kwargs
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
response = await func(
|
||||||
|
origin, content, request.args, *args, **kwargs
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if (
|
||||||
|
func.__self__.__class__.__name__ # type: ignore
|
||||||
|
== "FederationUnstableMediaDownloadServlet"
|
||||||
|
):
|
||||||
|
response = await func(
|
||||||
|
origin, content, request, *args, **kwargs
|
||||||
|
)
|
||||||
|
else:
|
||||||
response = await func(
|
response = await func(
|
||||||
origin, content, request.args, *args, **kwargs
|
origin, content, request.args, *args, **kwargs
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
response = await func(
|
|
||||||
origin, content, request.args, *args, **kwargs
|
|
||||||
)
|
|
||||||
finally:
|
finally:
|
||||||
# if we used the origin's context as the parent, add a new span using
|
# if we used the origin's context as the parent, add a new span using
|
||||||
# the servlet span as a parent, so that we have a link
|
# the servlet span as a parent, so that we have a link
|
||||||
|
|
|
@ -44,10 +44,13 @@ from synapse.federation.transport.server._base import (
|
||||||
)
|
)
|
||||||
from synapse.http.servlet import (
|
from synapse.http.servlet import (
|
||||||
parse_boolean_from_args,
|
parse_boolean_from_args,
|
||||||
|
parse_integer,
|
||||||
parse_integer_from_args,
|
parse_integer_from_args,
|
||||||
parse_string_from_args,
|
parse_string_from_args,
|
||||||
parse_strings_from_args,
|
parse_strings_from_args,
|
||||||
)
|
)
|
||||||
|
from synapse.http.site import SynapseRequest
|
||||||
|
from synapse.media._base import DEFAULT_MAX_TIMEOUT_MS, MAXIMUM_ALLOWED_MAX_TIMEOUT_MS
|
||||||
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
|
||||||
|
@ -787,6 +790,43 @@ class FederationAccountStatusServlet(BaseFederationServerServlet):
|
||||||
return 200, {"account_statuses": statuses, "failures": failures}
|
return 200, {"account_statuses": statuses, "failures": failures}
|
||||||
|
|
||||||
|
|
||||||
|
class FederationUnstableMediaDownloadServlet(BaseFederationServerServlet):
|
||||||
|
"""
|
||||||
|
Implementation of new federation media `/download` 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/download/(?P<media_id>[^/]*)"
|
||||||
|
PREFIX = FEDERATION_UNSTABLE_PREFIX + "/org.matrix.msc3916"
|
||||||
|
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()
|
||||||
|
|
||||||
|
async def on_GET(
|
||||||
|
self,
|
||||||
|
origin: Optional[str],
|
||||||
|
content: Literal[None],
|
||||||
|
request: SynapseRequest,
|
||||||
|
media_id: str,
|
||||||
|
) -> None:
|
||||||
|
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)
|
||||||
|
await self.media_repo.get_local_media(
|
||||||
|
request, media_id, None, max_timeout_ms, federation=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
FEDERATION_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = (
|
FEDERATION_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = (
|
||||||
FederationSendServlet,
|
FederationSendServlet,
|
||||||
FederationEventServlet,
|
FederationEventServlet,
|
||||||
|
@ -818,4 +858,5 @@ FEDERATION_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = (
|
||||||
FederationV1SendKnockServlet,
|
FederationV1SendKnockServlet,
|
||||||
FederationMakeKnockServlet,
|
FederationMakeKnockServlet,
|
||||||
FederationAccountStatusServlet,
|
FederationAccountStatusServlet,
|
||||||
|
FederationUnstableMediaDownloadServlet,
|
||||||
)
|
)
|
||||||
|
|
|
@ -125,13 +125,7 @@ class AdminHandler:
|
||||||
# Get all rooms the user is in or has been in
|
# Get all rooms the user is in or has been in
|
||||||
rooms = await self._store.get_rooms_for_local_user_where_membership_is(
|
rooms = await self._store.get_rooms_for_local_user_where_membership_is(
|
||||||
user_id,
|
user_id,
|
||||||
membership_list=(
|
membership_list=Membership.LIST,
|
||||||
Membership.JOIN,
|
|
||||||
Membership.LEAVE,
|
|
||||||
Membership.BAN,
|
|
||||||
Membership.INVITE,
|
|
||||||
Membership.KNOCK,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# We only try and fetch events for rooms the user has been in. If
|
# We only try and fetch events for rooms the user has been in. If
|
||||||
|
@ -178,7 +172,7 @@ class AdminHandler:
|
||||||
if room.membership == Membership.JOIN:
|
if room.membership == Membership.JOIN:
|
||||||
stream_ordering = self._store.get_room_max_stream_ordering()
|
stream_ordering = self._store.get_room_max_stream_ordering()
|
||||||
else:
|
else:
|
||||||
stream_ordering = room.stream_ordering
|
stream_ordering = room.event_pos.stream
|
||||||
|
|
||||||
from_key = RoomStreamToken(topological=0, stream=0)
|
from_key = RoomStreamToken(topological=0, stream=0)
|
||||||
to_key = RoomStreamToken(stream=stream_ordering)
|
to_key = RoomStreamToken(stream=stream_ordering)
|
||||||
|
@ -217,7 +211,9 @@ class AdminHandler:
|
||||||
)
|
)
|
||||||
|
|
||||||
events = await filter_events_for_client(
|
events = await filter_events_for_client(
|
||||||
self._storage_controllers, user_id, events
|
self._storage_controllers,
|
||||||
|
user_id,
|
||||||
|
events,
|
||||||
)
|
)
|
||||||
|
|
||||||
writer.write_events(room_id, events)
|
writer.write_events(room_id, events)
|
||||||
|
|
|
@ -78,6 +78,8 @@ class CasHandler:
|
||||||
self._cas_displayname_attribute = hs.config.cas.cas_displayname_attribute
|
self._cas_displayname_attribute = hs.config.cas.cas_displayname_attribute
|
||||||
self._cas_required_attributes = hs.config.cas.cas_required_attributes
|
self._cas_required_attributes = hs.config.cas.cas_required_attributes
|
||||||
self._cas_enable_registration = hs.config.cas.cas_enable_registration
|
self._cas_enable_registration = hs.config.cas.cas_enable_registration
|
||||||
|
self._cas_allow_numeric_ids = hs.config.cas.cas_allow_numeric_ids
|
||||||
|
self._cas_numeric_ids_prefix = hs.config.cas.cas_numeric_ids_prefix
|
||||||
|
|
||||||
self._http_client = hs.get_proxied_http_client()
|
self._http_client = hs.get_proxied_http_client()
|
||||||
|
|
||||||
|
@ -188,6 +190,9 @@ class CasHandler:
|
||||||
for child in root[0]:
|
for child in root[0]:
|
||||||
if child.tag.endswith("user"):
|
if child.tag.endswith("user"):
|
||||||
user = child.text
|
user = child.text
|
||||||
|
# if numeric user IDs are allowed and username is numeric then we add the prefix so Synapse can handle it
|
||||||
|
if self._cas_allow_numeric_ids and user is not None and user.isdigit():
|
||||||
|
user = f"{self._cas_numeric_ids_prefix}{user}"
|
||||||
if child.tag.endswith("attributes"):
|
if child.tag.endswith("attributes"):
|
||||||
for attribute in child:
|
for attribute in child:
|
||||||
# ElementTree library expands the namespace in
|
# ElementTree library expands the namespace in
|
||||||
|
|
|
@ -261,11 +261,22 @@ class DeactivateAccountHandler:
|
||||||
user = UserID.from_string(user_id)
|
user = UserID.from_string(user_id)
|
||||||
|
|
||||||
rooms_for_user = await self.store.get_rooms_for_user(user_id)
|
rooms_for_user = await self.store.get_rooms_for_user(user_id)
|
||||||
|
requester = create_requester(user, authenticated_entity=self._server_name)
|
||||||
|
should_erase = await self.store.is_user_erased(user_id)
|
||||||
|
|
||||||
for room_id in rooms_for_user:
|
for room_id in rooms_for_user:
|
||||||
logger.info("User parter parting %r from %r", user_id, room_id)
|
logger.info("User parter parting %r from %r", user_id, room_id)
|
||||||
try:
|
try:
|
||||||
|
# Before parting the user, redact all membership events if requested
|
||||||
|
if should_erase:
|
||||||
|
event_ids = await self.store.get_membership_event_ids_for_user(
|
||||||
|
user_id, room_id
|
||||||
|
)
|
||||||
|
for event_id in event_ids:
|
||||||
|
await self.store.expire_event(event_id)
|
||||||
|
|
||||||
await self._room_member_handler.update_membership(
|
await self._room_member_handler.update_membership(
|
||||||
create_requester(user, authenticated_entity=self._server_name),
|
requester,
|
||||||
user,
|
user,
|
||||||
room_id,
|
room_id,
|
||||||
"leave",
|
"leave",
|
||||||
|
|
|
@ -159,20 +159,32 @@ class DeviceWorkerHandler:
|
||||||
|
|
||||||
@cancellable
|
@cancellable
|
||||||
async def get_device_changes_in_shared_rooms(
|
async def get_device_changes_in_shared_rooms(
|
||||||
self, user_id: str, room_ids: StrCollection, from_token: StreamToken
|
self,
|
||||||
|
user_id: str,
|
||||||
|
room_ids: StrCollection,
|
||||||
|
from_token: StreamToken,
|
||||||
|
now_token: Optional[StreamToken] = None,
|
||||||
) -> Set[str]:
|
) -> Set[str]:
|
||||||
"""Get the set of users whose devices have changed who share a room with
|
"""Get the set of users whose devices have changed who share a room with
|
||||||
the given user.
|
the given user.
|
||||||
"""
|
"""
|
||||||
|
now_device_lists_key = self.store.get_device_stream_token()
|
||||||
|
if now_token:
|
||||||
|
now_device_lists_key = now_token.device_list_key
|
||||||
|
|
||||||
changed_users = await self.store.get_device_list_changes_in_rooms(
|
changed_users = await self.store.get_device_list_changes_in_rooms(
|
||||||
room_ids, from_token.device_list_key
|
room_ids,
|
||||||
|
from_token.device_list_key,
|
||||||
|
now_device_lists_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
if changed_users is not None:
|
if changed_users is not None:
|
||||||
# We also check if the given user has changed their device. If
|
# We also check if the given user has changed their device. If
|
||||||
# they're in no rooms then the above query won't include them.
|
# they're in no rooms then the above query won't include them.
|
||||||
changed = await self.store.get_users_whose_devices_changed(
|
changed = await self.store.get_users_whose_devices_changed(
|
||||||
from_token.device_list_key, [user_id]
|
from_token.device_list_key,
|
||||||
|
[user_id],
|
||||||
|
to_key=now_device_lists_key,
|
||||||
)
|
)
|
||||||
changed_users.update(changed)
|
changed_users.update(changed)
|
||||||
return changed_users
|
return changed_users
|
||||||
|
@ -190,7 +202,9 @@ class DeviceWorkerHandler:
|
||||||
tracked_users.add(user_id)
|
tracked_users.add(user_id)
|
||||||
|
|
||||||
changed = await self.store.get_users_whose_devices_changed(
|
changed = await self.store.get_users_whose_devices_changed(
|
||||||
from_token.device_list_key, tracked_users
|
from_token.device_list_key,
|
||||||
|
tracked_users,
|
||||||
|
to_key=now_device_lists_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
|
@ -892,6 +906,13 @@ class DeviceHandler(DeviceWorkerHandler):
|
||||||
context=opentracing_context,
|
context=opentracing_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await self.store.mark_redundant_device_lists_pokes(
|
||||||
|
user_id=user_id,
|
||||||
|
device_id=device_id,
|
||||||
|
room_id=room_id,
|
||||||
|
converted_upto_stream_id=stream_id,
|
||||||
|
)
|
||||||
|
|
||||||
# Notify replication that we've updated the device list stream.
|
# Notify replication that we've updated the device list stream.
|
||||||
self.notifier.notify_replication()
|
self.notifier.notify_replication()
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,9 @@ class DeviceMessageHandler:
|
||||||
"""
|
"""
|
||||||
Handle receiving to-device messages from remote homeservers.
|
Handle receiving to-device messages from remote homeservers.
|
||||||
|
|
||||||
|
Note that any errors thrown from this method will cause the federation /send
|
||||||
|
request to receive an error response.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
origin: The remote homeserver.
|
origin: The remote homeserver.
|
||||||
content: The JSON dictionary containing the to-device messages.
|
content: The JSON dictionary containing the to-device messages.
|
||||||
|
@ -233,6 +236,13 @@ class DeviceMessageHandler:
|
||||||
local_messages = {}
|
local_messages = {}
|
||||||
remote_messages: Dict[str, Dict[str, Dict[str, JsonDict]]] = {}
|
remote_messages: Dict[str, Dict[str, Dict[str, JsonDict]]] = {}
|
||||||
for user_id, by_device in messages.items():
|
for user_id, by_device in messages.items():
|
||||||
|
if not UserID.is_valid(user_id):
|
||||||
|
logger.warning(
|
||||||
|
"Ignoring attempt to send device message to invalid user: %r",
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
# add an opentracing log entry for each message
|
# add an opentracing log entry for each message
|
||||||
for device_id, message_content in by_device.items():
|
for device_id, message_content in by_device.items():
|
||||||
log_kv(
|
log_kv(
|
||||||
|
|
|
@ -80,9 +80,11 @@ class DirectoryHandler:
|
||||||
) -> None:
|
) -> None:
|
||||||
# general association creation for both human users and app services
|
# general association creation for both human users and app services
|
||||||
|
|
||||||
for wchar in string.whitespace:
|
# meow: allow specific users to include anything in room aliases
|
||||||
if wchar in room_alias.localpart:
|
if creator not in self.config.meow.validation_override:
|
||||||
raise SynapseError(400, "Invalid characters in room alias")
|
for wchar in string.whitespace:
|
||||||
|
if wchar in room_alias.localpart:
|
||||||
|
raise SynapseError(400, "Invalid characters in room alias")
|
||||||
|
|
||||||
if ":" in room_alias.localpart:
|
if ":" in room_alias.localpart:
|
||||||
raise SynapseError(400, "Invalid character in room alias localpart: ':'.")
|
raise SynapseError(400, "Invalid character in room alias localpart: ':'.")
|
||||||
|
@ -127,7 +129,10 @@ class DirectoryHandler:
|
||||||
user_id = requester.user.to_string()
|
user_id = requester.user.to_string()
|
||||||
room_alias_str = room_alias.to_string()
|
room_alias_str = room_alias.to_string()
|
||||||
|
|
||||||
if len(room_alias_str) > MAX_ALIAS_LENGTH:
|
if (
|
||||||
|
user_id not in self.hs.config.meow.validation_override
|
||||||
|
and len(room_alias_str) > MAX_ALIAS_LENGTH
|
||||||
|
):
|
||||||
raise SynapseError(
|
raise SynapseError(
|
||||||
400,
|
400,
|
||||||
"Can't create aliases longer than %s characters" % MAX_ALIAS_LENGTH,
|
"Can't create aliases longer than %s characters" % MAX_ALIAS_LENGTH,
|
||||||
|
@ -169,7 +174,7 @@ class DirectoryHandler:
|
||||||
|
|
||||||
if not self.config.roomdirectory.is_alias_creation_allowed(
|
if not self.config.roomdirectory.is_alias_creation_allowed(
|
||||||
user_id, room_id, room_alias_str
|
user_id, room_id, room_alias_str
|
||||||
):
|
) and not is_admin:
|
||||||
# Let's just return a generic message, as there may be all sorts of
|
# Let's just return a generic message, as there may be all sorts of
|
||||||
# reasons why we said no. TODO: Allow configurable error messages
|
# reasons why we said no. TODO: Allow configurable error messages
|
||||||
# per alias creation rule?
|
# per alias creation rule?
|
||||||
|
@ -505,7 +510,7 @@ class DirectoryHandler:
|
||||||
|
|
||||||
if not self.config.roomdirectory.is_publishing_room_allowed(
|
if not self.config.roomdirectory.is_publishing_room_allowed(
|
||||||
user_id, room_id, room_aliases
|
user_id, room_id, room_aliases
|
||||||
):
|
) and not await self.auth.is_server_admin(requester):
|
||||||
# Let's just return a generic message, as there may be all sorts of
|
# Let's just return a generic message, as there may be all sorts of
|
||||||
# reasons why we said no. TODO: Allow configurable error messages
|
# reasons why we said no. TODO: Allow configurable error messages
|
||||||
# per alias creation rule?
|
# per alias creation rule?
|
||||||
|
|
|
@ -35,6 +35,7 @@ from synapse.api.errors import CodeMessageException, Codes, NotFoundError, Synap
|
||||||
from synapse.handlers.device import DeviceHandler
|
from synapse.handlers.device import DeviceHandler
|
||||||
from synapse.logging.context import make_deferred_yieldable, run_in_background
|
from synapse.logging.context import make_deferred_yieldable, run_in_background
|
||||||
from synapse.logging.opentracing import log_kv, set_tag, tag_args, trace
|
from synapse.logging.opentracing import log_kv, set_tag, tag_args, trace
|
||||||
|
from synapse.replication.http.devices import ReplicationUploadKeysForUserRestServlet
|
||||||
from synapse.types import (
|
from synapse.types import (
|
||||||
JsonDict,
|
JsonDict,
|
||||||
JsonMapping,
|
JsonMapping,
|
||||||
|
@ -45,7 +46,10 @@ from synapse.types import (
|
||||||
from synapse.util import json_decoder
|
from synapse.util import json_decoder
|
||||||
from synapse.util.async_helpers import Linearizer, concurrently_execute
|
from synapse.util.async_helpers import Linearizer, concurrently_execute
|
||||||
from synapse.util.cancellation import cancellable
|
from synapse.util.cancellation import cancellable
|
||||||
from synapse.util.retryutils import NotRetryingDestination
|
from synapse.util.retryutils import (
|
||||||
|
NotRetryingDestination,
|
||||||
|
filter_destinations_by_retry_limiter,
|
||||||
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
@ -53,6 +57,9 @@ if TYPE_CHECKING:
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
ONE_TIME_KEY_UPLOAD = "one_time_key_upload_lock"
|
||||||
|
|
||||||
|
|
||||||
class E2eKeysHandler:
|
class E2eKeysHandler:
|
||||||
def __init__(self, hs: "HomeServer"):
|
def __init__(self, hs: "HomeServer"):
|
||||||
self.config = hs.config
|
self.config = hs.config
|
||||||
|
@ -62,6 +69,7 @@ class E2eKeysHandler:
|
||||||
self._appservice_handler = hs.get_application_service_handler()
|
self._appservice_handler = hs.get_application_service_handler()
|
||||||
self.is_mine = hs.is_mine
|
self.is_mine = hs.is_mine
|
||||||
self.clock = hs.get_clock()
|
self.clock = hs.get_clock()
|
||||||
|
self._worker_lock_handler = hs.get_worker_locks_handler()
|
||||||
|
|
||||||
federation_registry = hs.get_federation_registry()
|
federation_registry = hs.get_federation_registry()
|
||||||
|
|
||||||
|
@ -82,6 +90,12 @@ class E2eKeysHandler:
|
||||||
edu_updater.incoming_signing_key_update,
|
edu_updater.incoming_signing_key_update,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.device_key_uploader = self.upload_device_keys_for_user
|
||||||
|
else:
|
||||||
|
self.device_key_uploader = (
|
||||||
|
ReplicationUploadKeysForUserRestServlet.make_client(hs)
|
||||||
|
)
|
||||||
|
|
||||||
# doesn't really work as part of the generic query API, because the
|
# doesn't really work as part of the generic query API, because the
|
||||||
# query request requires an object POST, but we abuse the
|
# query request requires an object POST, but we abuse the
|
||||||
# "query handler" interface.
|
# "query handler" interface.
|
||||||
|
@ -145,6 +159,11 @@ class E2eKeysHandler:
|
||||||
remote_queries = {}
|
remote_queries = {}
|
||||||
|
|
||||||
for user_id, device_ids in device_keys_query.items():
|
for user_id, device_ids in device_keys_query.items():
|
||||||
|
if not UserID.is_valid(user_id):
|
||||||
|
# Ignore invalid user IDs, which is the same behaviour as if
|
||||||
|
# the user existed but had no keys.
|
||||||
|
continue
|
||||||
|
|
||||||
# we use UserID.from_string to catch invalid user ids
|
# we use UserID.from_string to catch invalid user ids
|
||||||
if self.is_mine(UserID.from_string(user_id)):
|
if self.is_mine(UserID.from_string(user_id)):
|
||||||
local_query[user_id] = device_ids
|
local_query[user_id] = device_ids
|
||||||
|
@ -259,10 +278,8 @@ class E2eKeysHandler:
|
||||||
"%d destinations to query devices for", len(remote_queries_not_in_cache)
|
"%d destinations to query devices for", len(remote_queries_not_in_cache)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _query(
|
async def _query(destination: str) -> None:
|
||||||
destination_queries: Tuple[str, Dict[str, Iterable[str]]]
|
queries = remote_queries_not_in_cache[destination]
|
||||||
) -> None:
|
|
||||||
destination, queries = destination_queries
|
|
||||||
return await self._query_devices_for_destination(
|
return await self._query_devices_for_destination(
|
||||||
results,
|
results,
|
||||||
cross_signing_keys,
|
cross_signing_keys,
|
||||||
|
@ -272,9 +289,20 @@ class E2eKeysHandler:
|
||||||
timeout,
|
timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Only try and fetch keys for destinations that are not marked as
|
||||||
|
# down.
|
||||||
|
filtered_destinations = await filter_destinations_by_retry_limiter(
|
||||||
|
remote_queries_not_in_cache.keys(),
|
||||||
|
self.clock,
|
||||||
|
self.store,
|
||||||
|
# Let's give an arbitrary grace period for those hosts that are
|
||||||
|
# only recently down
|
||||||
|
retry_due_within_ms=60 * 1000,
|
||||||
|
)
|
||||||
|
|
||||||
await concurrently_execute(
|
await concurrently_execute(
|
||||||
_query,
|
_query,
|
||||||
remote_queries_not_in_cache.items(),
|
filtered_destinations,
|
||||||
10,
|
10,
|
||||||
delay_cancellation=True,
|
delay_cancellation=True,
|
||||||
)
|
)
|
||||||
|
@ -775,36 +803,17 @@ class E2eKeysHandler:
|
||||||
"one_time_keys": A mapping from algorithm to number of keys for that
|
"one_time_keys": A mapping from algorithm to number of keys for that
|
||||||
algorithm, including those previously persisted.
|
algorithm, including those previously persisted.
|
||||||
"""
|
"""
|
||||||
# This can only be called from the main process.
|
|
||||||
assert isinstance(self.device_handler, DeviceHandler)
|
|
||||||
|
|
||||||
time_now = self.clock.time_msec()
|
time_now = self.clock.time_msec()
|
||||||
|
|
||||||
# TODO: Validate the JSON to make sure it has the right keys.
|
# TODO: Validate the JSON to make sure it has the right keys.
|
||||||
device_keys = keys.get("device_keys", None)
|
device_keys = keys.get("device_keys", None)
|
||||||
if device_keys:
|
if device_keys:
|
||||||
logger.info(
|
await self.device_key_uploader(
|
||||||
"Updating device_keys for device %r for user %s at %d",
|
user_id=user_id,
|
||||||
device_id,
|
device_id=device_id,
|
||||||
user_id,
|
keys={"device_keys": device_keys},
|
||||||
time_now,
|
|
||||||
)
|
)
|
||||||
log_kv(
|
|
||||||
{
|
|
||||||
"message": "Updating device_keys for user.",
|
|
||||||
"user_id": user_id,
|
|
||||||
"device_id": device_id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# TODO: Sign the JSON with the server key
|
|
||||||
changed = await self.store.set_e2e_device_keys(
|
|
||||||
user_id, device_id, time_now, device_keys
|
|
||||||
)
|
|
||||||
if changed:
|
|
||||||
# Only notify about device updates *if* the keys actually changed
|
|
||||||
await self.device_handler.notify_device_update(user_id, [device_id])
|
|
||||||
else:
|
|
||||||
log_kv({"message": "Not updating device_keys for user", "user_id": user_id})
|
|
||||||
one_time_keys = keys.get("one_time_keys", None)
|
one_time_keys = keys.get("one_time_keys", None)
|
||||||
if one_time_keys:
|
if one_time_keys:
|
||||||
log_kv(
|
log_kv(
|
||||||
|
@ -840,6 +849,49 @@ class E2eKeysHandler:
|
||||||
{"message": "Did not update fallback_keys", "reason": "no keys given"}
|
{"message": "Did not update fallback_keys", "reason": "no keys given"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
result = await self.store.count_e2e_one_time_keys(user_id, device_id)
|
||||||
|
|
||||||
|
set_tag("one_time_key_counts", str(result))
|
||||||
|
return {"one_time_key_counts": result}
|
||||||
|
|
||||||
|
@tag_args
|
||||||
|
async def upload_device_keys_for_user(
|
||||||
|
self, user_id: str, device_id: str, keys: JsonDict
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
user_id: user whose keys are being uploaded.
|
||||||
|
device_id: device whose keys are being uploaded.
|
||||||
|
device_keys: the `device_keys` of an /keys/upload request.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# This can only be called from the main process.
|
||||||
|
assert isinstance(self.device_handler, DeviceHandler)
|
||||||
|
|
||||||
|
time_now = self.clock.time_msec()
|
||||||
|
|
||||||
|
device_keys = keys["device_keys"]
|
||||||
|
logger.info(
|
||||||
|
"Updating device_keys for device %r for user %s at %d",
|
||||||
|
device_id,
|
||||||
|
user_id,
|
||||||
|
time_now,
|
||||||
|
)
|
||||||
|
log_kv(
|
||||||
|
{
|
||||||
|
"message": "Updating device_keys for user.",
|
||||||
|
"user_id": user_id,
|
||||||
|
"device_id": device_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# TODO: Sign the JSON with the server key
|
||||||
|
changed = await self.store.set_e2e_device_keys(
|
||||||
|
user_id, device_id, time_now, device_keys
|
||||||
|
)
|
||||||
|
if changed:
|
||||||
|
# Only notify about device updates *if* the keys actually changed
|
||||||
|
await self.device_handler.notify_device_update(user_id, [device_id])
|
||||||
|
|
||||||
# the device should have been registered already, but it may have been
|
# the device should have been registered already, but it may have been
|
||||||
# deleted due to a race with a DELETE request. Or we may be using an
|
# deleted due to a race with a DELETE request. Or we may be using an
|
||||||
# old access_token without an associated device_id. Either way, we
|
# old access_token without an associated device_id. Either way, we
|
||||||
|
@ -847,53 +899,56 @@ class E2eKeysHandler:
|
||||||
# keys without a corresponding device.
|
# keys without a corresponding device.
|
||||||
await self.device_handler.check_device_registered(user_id, device_id)
|
await self.device_handler.check_device_registered(user_id, device_id)
|
||||||
|
|
||||||
result = await self.store.count_e2e_one_time_keys(user_id, device_id)
|
|
||||||
|
|
||||||
set_tag("one_time_key_counts", str(result))
|
|
||||||
return {"one_time_key_counts": result}
|
|
||||||
|
|
||||||
async def _upload_one_time_keys_for_user(
|
async def _upload_one_time_keys_for_user(
|
||||||
self, user_id: str, device_id: str, time_now: int, one_time_keys: JsonDict
|
self, user_id: str, device_id: str, time_now: int, one_time_keys: JsonDict
|
||||||
) -> None:
|
) -> None:
|
||||||
logger.info(
|
# We take out a lock so that we don't have to worry about a client
|
||||||
"Adding one_time_keys %r for device %r for user %r at %d",
|
# sending duplicate requests.
|
||||||
one_time_keys.keys(),
|
lock_key = f"{user_id}_{device_id}"
|
||||||
device_id,
|
async with self._worker_lock_handler.acquire_lock(
|
||||||
user_id,
|
ONE_TIME_KEY_UPLOAD, lock_key
|
||||||
time_now,
|
):
|
||||||
)
|
logger.info(
|
||||||
|
"Adding one_time_keys %r for device %r for user %r at %d",
|
||||||
|
one_time_keys.keys(),
|
||||||
|
device_id,
|
||||||
|
user_id,
|
||||||
|
time_now,
|
||||||
|
)
|
||||||
|
|
||||||
# make a list of (alg, id, key) tuples
|
# make a list of (alg, id, key) tuples
|
||||||
key_list = []
|
key_list = []
|
||||||
for key_id, key_obj in one_time_keys.items():
|
for key_id, key_obj in one_time_keys.items():
|
||||||
algorithm, key_id = key_id.split(":")
|
algorithm, key_id = key_id.split(":")
|
||||||
key_list.append((algorithm, key_id, key_obj))
|
key_list.append((algorithm, key_id, key_obj))
|
||||||
|
|
||||||
# First we check if we have already persisted any of the keys.
|
# First we check if we have already persisted any of the keys.
|
||||||
existing_key_map = await self.store.get_e2e_one_time_keys(
|
existing_key_map = await self.store.get_e2e_one_time_keys(
|
||||||
user_id, device_id, [k_id for _, k_id, _ in key_list]
|
user_id, device_id, [k_id for _, k_id, _ in key_list]
|
||||||
)
|
)
|
||||||
|
|
||||||
new_keys = [] # Keys that we need to insert. (alg, id, json) tuples.
|
new_keys = [] # Keys that we need to insert. (alg, id, json) tuples.
|
||||||
for algorithm, key_id, key in key_list:
|
for algorithm, key_id, key in key_list:
|
||||||
ex_json = existing_key_map.get((algorithm, key_id), None)
|
ex_json = existing_key_map.get((algorithm, key_id), None)
|
||||||
if ex_json:
|
if ex_json:
|
||||||
if not _one_time_keys_match(ex_json, key):
|
if not _one_time_keys_match(ex_json, key):
|
||||||
raise SynapseError(
|
raise SynapseError(
|
||||||
400,
|
400,
|
||||||
(
|
(
|
||||||
"One time key %s:%s already exists. "
|
"One time key %s:%s already exists. "
|
||||||
"Old key: %s; new key: %r"
|
"Old key: %s; new key: %r"
|
||||||
|
)
|
||||||
|
% (algorithm, key_id, ex_json, key),
|
||||||
)
|
)
|
||||||
% (algorithm, key_id, ex_json, key),
|
else:
|
||||||
|
new_keys.append(
|
||||||
|
(algorithm, key_id, encode_canonical_json(key).decode("ascii"))
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
new_keys.append(
|
|
||||||
(algorithm, key_id, encode_canonical_json(key).decode("ascii"))
|
|
||||||
)
|
|
||||||
|
|
||||||
log_kv({"message": "Inserting new one_time_keys.", "keys": new_keys})
|
log_kv({"message": "Inserting new one_time_keys.", "keys": new_keys})
|
||||||
await self.store.add_e2e_one_time_keys(user_id, device_id, time_now, new_keys)
|
await self.store.add_e2e_one_time_keys(
|
||||||
|
user_id, device_id, time_now, new_keys
|
||||||
|
)
|
||||||
|
|
||||||
async def upload_signing_keys_for_user(
|
async def upload_signing_keys_for_user(
|
||||||
self, user_id: str, keys: JsonDict
|
self, user_id: str, keys: JsonDict
|
||||||
|
@ -1476,6 +1531,42 @@ class E2eKeysHandler:
|
||||||
else:
|
else:
|
||||||
return exists, self.clock.time_msec() < ts_replacable_without_uia_before
|
return exists, self.clock.time_msec() < ts_replacable_without_uia_before
|
||||||
|
|
||||||
|
async def has_different_keys(self, user_id: str, body: JsonDict) -> bool:
|
||||||
|
"""
|
||||||
|
Check if a key provided in `body` differs from the same key stored in the DB. Returns
|
||||||
|
true on the first difference. If a key exists in `body` but does not exist in the DB,
|
||||||
|
returns True. If `body` has no keys, this always returns False.
|
||||||
|
Note by 'key' we mean Matrix key rather than JSON key.
|
||||||
|
|
||||||
|
The purpose of this function is to detect whether or not we need to apply UIA checks.
|
||||||
|
We must apply UIA checks if any key in the database is being overwritten. If a key is
|
||||||
|
being inserted for the first time, or if the key exactly matches what is in the database,
|
||||||
|
then no UIA check needs to be performed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The user who sent the `body`.
|
||||||
|
body: The JSON request body from POST /keys/device_signing/upload
|
||||||
|
Returns:
|
||||||
|
True if any key in `body` has a different value in the database.
|
||||||
|
"""
|
||||||
|
# Ensure that each key provided in the request body exactly matches the one we have stored.
|
||||||
|
# The first time we see the DB having a different key to the matching request key, bail.
|
||||||
|
# Note: we do not care if the DB has a key which the request does not specify, as we only
|
||||||
|
# care about *replacements* or *insertions* (i.e UPSERT)
|
||||||
|
req_body_key_to_db_key = {
|
||||||
|
"master_key": "master",
|
||||||
|
"self_signing_key": "self_signing",
|
||||||
|
"user_signing_key": "user_signing",
|
||||||
|
}
|
||||||
|
for req_body_key, db_key in req_body_key_to_db_key.items():
|
||||||
|
if req_body_key in body:
|
||||||
|
existing_key = await self.store.get_e2e_cross_signing_key(
|
||||||
|
user_id, db_key
|
||||||
|
)
|
||||||
|
if existing_key != body[req_body_key]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _check_cross_signing_key(
|
def _check_cross_signing_key(
|
||||||
key: JsonDict, user_id: str, key_type: str, signing_key: Optional[VerifyKey] = None
|
key: JsonDict, user_id: str, key_type: str, signing_key: Optional[VerifyKey] = None
|
||||||
|
|
|
@ -247,6 +247,12 @@ class E2eRoomKeysHandler:
|
||||||
if current_room_key:
|
if current_room_key:
|
||||||
if self._should_replace_room_key(current_room_key, room_key):
|
if self._should_replace_room_key(current_room_key, room_key):
|
||||||
log_kv({"message": "Replacing room key."})
|
log_kv({"message": "Replacing room key."})
|
||||||
|
logger.debug(
|
||||||
|
"Replacing room key. room=%s session=%s user=%s",
|
||||||
|
room_id,
|
||||||
|
session_id,
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
# updates are done one at a time in the DB, so send
|
# updates are done one at a time in the DB, so send
|
||||||
# updates right away rather than batching them up,
|
# updates right away rather than batching them up,
|
||||||
# like we do with the inserts
|
# like we do with the inserts
|
||||||
|
@ -256,6 +262,12 @@ class E2eRoomKeysHandler:
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
log_kv({"message": "Not replacing room_key."})
|
log_kv({"message": "Not replacing room_key."})
|
||||||
|
logger.debug(
|
||||||
|
"Not replacing room key. room=%s session=%s user=%s",
|
||||||
|
room_id,
|
||||||
|
session_id,
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
log_kv(
|
log_kv(
|
||||||
{
|
{
|
||||||
|
@ -265,6 +277,12 @@ class E2eRoomKeysHandler:
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
log_kv({"message": "Replacing room key."})
|
log_kv({"message": "Replacing room key."})
|
||||||
|
logger.debug(
|
||||||
|
"Inserting new room key. room=%s session=%s user=%s",
|
||||||
|
room_id,
|
||||||
|
session_id,
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
to_insert.append((room_id, session_id, room_key))
|
to_insert.append((room_id, session_id, room_key))
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,10 @@ class EventHandler:
|
||||||
is_peeking = not is_user_in_room
|
is_peeking = not is_user_in_room
|
||||||
|
|
||||||
filtered = await filter_events_for_client(
|
filtered = await filter_events_for_client(
|
||||||
self._storage_controllers, user.to_string(), [event], is_peeking=is_peeking
|
self._storage_controllers,
|
||||||
|
user.to_string(),
|
||||||
|
[event],
|
||||||
|
is_peeking=is_peeking,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not filtered:
|
if not filtered:
|
||||||
|
|
|
@ -1452,7 +1452,7 @@ class FederationHandler:
|
||||||
room_version_obj, event_dict
|
room_version_obj, event_dict
|
||||||
)
|
)
|
||||||
|
|
||||||
EventValidator().validate_builder(builder)
|
EventValidator().validate_builder(builder, self.hs.config)
|
||||||
|
|
||||||
# Try several times, it could fail with PartialStateConflictError
|
# Try several times, it could fail with PartialStateConflictError
|
||||||
# in send_membership_event, cf comment in except block.
|
# in send_membership_event, cf comment in except block.
|
||||||
|
@ -1617,7 +1617,7 @@ class FederationHandler:
|
||||||
builder = self.event_builder_factory.for_room_version(
|
builder = self.event_builder_factory.for_room_version(
|
||||||
room_version_obj, event_dict
|
room_version_obj, event_dict
|
||||||
)
|
)
|
||||||
EventValidator().validate_builder(builder)
|
EventValidator().validate_builder(builder, self.hs.config)
|
||||||
|
|
||||||
(
|
(
|
||||||
event,
|
event,
|
||||||
|
|
|
@ -199,7 +199,7 @@ class InitialSyncHandler:
|
||||||
)
|
)
|
||||||
elif event.membership == Membership.LEAVE:
|
elif event.membership == Membership.LEAVE:
|
||||||
room_end_token = RoomStreamToken(
|
room_end_token = RoomStreamToken(
|
||||||
stream=event.stream_ordering,
|
stream=event.event_pos.stream,
|
||||||
)
|
)
|
||||||
deferred_room_state = run_in_background(
|
deferred_room_state = run_in_background(
|
||||||
self._state_storage_controller.get_state_for_events,
|
self._state_storage_controller.get_state_for_events,
|
||||||
|
@ -221,7 +221,9 @@ class InitialSyncHandler:
|
||||||
).addErrback(unwrapFirstError)
|
).addErrback(unwrapFirstError)
|
||||||
|
|
||||||
messages = await filter_events_for_client(
|
messages = await filter_events_for_client(
|
||||||
self._storage_controllers, user_id, messages
|
self._storage_controllers,
|
||||||
|
user_id,
|
||||||
|
messages,
|
||||||
)
|
)
|
||||||
|
|
||||||
start_token = now_token.copy_and_replace(StreamKeyType.ROOM, token)
|
start_token = now_token.copy_and_replace(StreamKeyType.ROOM, token)
|
||||||
|
|
|
@ -201,7 +201,7 @@ class MessageHandler:
|
||||||
|
|
||||||
if at_token:
|
if at_token:
|
||||||
last_event_id = (
|
last_event_id = (
|
||||||
await self.store.get_last_event_in_room_before_stream_ordering(
|
await self.store.get_last_event_id_in_room_before_stream_ordering(
|
||||||
room_id,
|
room_id,
|
||||||
end_token=at_token.room_key,
|
end_token=at_token.room_key,
|
||||||
)
|
)
|
||||||
|
@ -496,13 +496,6 @@ class EventCreationHandler:
|
||||||
|
|
||||||
self.room_prejoin_state_types = self.hs.config.api.room_prejoin_state
|
self.room_prejoin_state_types = self.hs.config.api.room_prejoin_state
|
||||||
|
|
||||||
self.membership_types_to_include_profile_data_in = {
|
|
||||||
Membership.JOIN,
|
|
||||||
Membership.KNOCK,
|
|
||||||
}
|
|
||||||
if self.hs.config.server.include_profile_data_on_invite:
|
|
||||||
self.membership_types_to_include_profile_data_in.add(Membership.INVITE)
|
|
||||||
|
|
||||||
self.send_event = ReplicationSendEventRestServlet.make_client(hs)
|
self.send_event = ReplicationSendEventRestServlet.make_client(hs)
|
||||||
self.send_events = ReplicationSendEventsRestServlet.make_client(hs)
|
self.send_events = ReplicationSendEventsRestServlet.make_client(hs)
|
||||||
|
|
||||||
|
@ -594,8 +587,6 @@ class EventCreationHandler:
|
||||||
Creates an FrozenEvent object, filling out auth_events, prev_events,
|
Creates an FrozenEvent object, filling out auth_events, prev_events,
|
||||||
etc.
|
etc.
|
||||||
|
|
||||||
Adds display names to Join membership events.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
requester
|
requester
|
||||||
event_dict: An entire event
|
event_dict: An entire event
|
||||||
|
@ -651,6 +642,17 @@ class EventCreationHandler:
|
||||||
"""
|
"""
|
||||||
await self.auth_blocking.check_auth_blocking(requester=requester)
|
await self.auth_blocking.check_auth_blocking(requester=requester)
|
||||||
|
|
||||||
|
if event_dict["type"] == EventTypes.Message:
|
||||||
|
requester_suspended = await self.store.get_user_suspended_status(
|
||||||
|
requester.user.to_string()
|
||||||
|
)
|
||||||
|
if requester_suspended:
|
||||||
|
raise SynapseError(
|
||||||
|
403,
|
||||||
|
"Sending messages while account is suspended is not allowed.",
|
||||||
|
Codes.USER_ACCOUNT_SUSPENDED,
|
||||||
|
)
|
||||||
|
|
||||||
if event_dict["type"] == EventTypes.Create and event_dict["state_key"] == "":
|
if event_dict["type"] == EventTypes.Create and event_dict["state_key"] == "":
|
||||||
room_version_id = event_dict["content"]["room_version"]
|
room_version_id = event_dict["content"]["room_version"]
|
||||||
maybe_room_version_obj = KNOWN_ROOM_VERSIONS.get(room_version_id)
|
maybe_room_version_obj = KNOWN_ROOM_VERSIONS.get(room_version_id)
|
||||||
|
@ -670,30 +672,7 @@ class EventCreationHandler:
|
||||||
room_version_obj, event_dict
|
room_version_obj, event_dict
|
||||||
)
|
)
|
||||||
|
|
||||||
self.validator.validate_builder(builder)
|
self.validator.validate_builder(builder, self.config)
|
||||||
|
|
||||||
if builder.type == EventTypes.Member:
|
|
||||||
membership = builder.content.get("membership", None)
|
|
||||||
target = UserID.from_string(builder.state_key)
|
|
||||||
|
|
||||||
if membership in self.membership_types_to_include_profile_data_in:
|
|
||||||
# If event doesn't include a display name, add one.
|
|
||||||
profile = self.profile_handler
|
|
||||||
content = builder.content
|
|
||||||
|
|
||||||
try:
|
|
||||||
if "displayname" not in content:
|
|
||||||
displayname = await profile.get_displayname(target)
|
|
||||||
if displayname is not None:
|
|
||||||
content["displayname"] = displayname
|
|
||||||
if "avatar_url" not in content:
|
|
||||||
avatar_url = await profile.get_avatar_url(target)
|
|
||||||
if avatar_url is not None:
|
|
||||||
content["avatar_url"] = avatar_url
|
|
||||||
except Exception as e:
|
|
||||||
logger.info(
|
|
||||||
"Failed to get profile information for %r: %s", target, e
|
|
||||||
)
|
|
||||||
|
|
||||||
is_exempt = await self._is_exempt_from_privacy_policy(builder, requester)
|
is_exempt = await self._is_exempt_from_privacy_policy(builder, requester)
|
||||||
if require_consent and not is_exempt:
|
if require_consent and not is_exempt:
|
||||||
|
@ -1352,6 +1331,8 @@ class EventCreationHandler:
|
||||||
Raises:
|
Raises:
|
||||||
SynapseError if the event is invalid.
|
SynapseError if the event is invalid.
|
||||||
"""
|
"""
|
||||||
|
if event.sender in self.config.meow.validation_override:
|
||||||
|
return
|
||||||
|
|
||||||
relation = relation_from_event(event)
|
relation = relation_from_event(event)
|
||||||
if not relation:
|
if not relation:
|
||||||
|
@ -1583,6 +1564,7 @@ class EventCreationHandler:
|
||||||
# stream_ordering entry manually (as it was persisted on
|
# stream_ordering entry manually (as it was persisted on
|
||||||
# another worker).
|
# another worker).
|
||||||
event.internal_metadata.stream_ordering = stream_id
|
event.internal_metadata.stream_ordering = stream_id
|
||||||
|
event.internal_metadata.instance_name = writer_instance
|
||||||
|
|
||||||
return event
|
return event
|
||||||
|
|
||||||
|
@ -1770,7 +1752,8 @@ class EventCreationHandler:
|
||||||
|
|
||||||
await self._maybe_kick_guest_users(event, context)
|
await self._maybe_kick_guest_users(event, context)
|
||||||
|
|
||||||
if event.type == EventTypes.CanonicalAlias:
|
validation_override = event.sender in self.config.meow.validation_override
|
||||||
|
if event.type == EventTypes.CanonicalAlias and not validation_override:
|
||||||
# Validate a newly added alias or newly added alt_aliases.
|
# Validate a newly added alias or newly added alt_aliases.
|
||||||
|
|
||||||
original_alias = None
|
original_alias = None
|
||||||
|
@ -2125,7 +2108,7 @@ class EventCreationHandler:
|
||||||
builder = self.event_builder_factory.for_room_version(
|
builder = self.event_builder_factory.for_room_version(
|
||||||
original_event.room_version, third_party_result
|
original_event.room_version, third_party_result
|
||||||
)
|
)
|
||||||
self.validator.validate_builder(builder)
|
self.validator.validate_builder(builder, self.config)
|
||||||
except SynapseError as e:
|
except SynapseError as e:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Third party rules module created an invalid event: " + e.msg,
|
"Third party rules module created an invalid event: " + e.msg,
|
||||||
|
|
|
@ -27,7 +27,6 @@ from synapse.api.constants import Direction, EventTypes, Membership
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import SynapseError
|
||||||
from synapse.api.filtering import Filter
|
from synapse.api.filtering import Filter
|
||||||
from synapse.events.utils import SerializeEventConfig
|
from synapse.events.utils import SerializeEventConfig
|
||||||
from synapse.handlers.room import ShutdownRoomParams, ShutdownRoomResponse
|
|
||||||
from synapse.handlers.worker_lock import NEW_EVENT_DURING_PURGE_LOCK_NAME
|
from synapse.handlers.worker_lock import NEW_EVENT_DURING_PURGE_LOCK_NAME
|
||||||
from synapse.logging.opentracing import trace
|
from synapse.logging.opentracing import trace
|
||||||
from synapse.metrics.background_process_metrics import run_as_background_process
|
from synapse.metrics.background_process_metrics import run_as_background_process
|
||||||
|
@ -41,6 +40,7 @@ from synapse.types import (
|
||||||
StreamKeyType,
|
StreamKeyType,
|
||||||
TaskStatus,
|
TaskStatus,
|
||||||
)
|
)
|
||||||
|
from synapse.types.handlers import ShutdownRoomParams, ShutdownRoomResponse
|
||||||
from synapse.types.state import StateFilter
|
from synapse.types.state import StateFilter
|
||||||
from synapse.util.async_helpers import ReadWriteLock
|
from synapse.util.async_helpers import ReadWriteLock
|
||||||
from synapse.visibility import filter_events_for_client
|
from synapse.visibility import filter_events_for_client
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#
|
#
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
from typing import TYPE_CHECKING, Optional, Union
|
from typing import TYPE_CHECKING, List, Optional, Union
|
||||||
|
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
AuthError,
|
AuthError,
|
||||||
|
@ -64,8 +64,10 @@ class ProfileHandler:
|
||||||
self.user_directory_handler = hs.get_user_directory_handler()
|
self.user_directory_handler = hs.get_user_directory_handler()
|
||||||
self.request_ratelimiter = hs.get_request_ratelimiter()
|
self.request_ratelimiter = hs.get_request_ratelimiter()
|
||||||
|
|
||||||
self.max_avatar_size = hs.config.server.max_avatar_size
|
self.max_avatar_size: Optional[int] = hs.config.server.max_avatar_size
|
||||||
self.allowed_avatar_mimetypes = hs.config.server.allowed_avatar_mimetypes
|
self.allowed_avatar_mimetypes: Optional[List[str]] = (
|
||||||
|
hs.config.server.allowed_avatar_mimetypes
|
||||||
|
)
|
||||||
|
|
||||||
self._is_mine_server_name = hs.is_mine_server_name
|
self._is_mine_server_name = hs.is_mine_server_name
|
||||||
|
|
||||||
|
@ -337,6 +339,12 @@ class ProfileHandler:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.max_avatar_size:
|
if self.max_avatar_size:
|
||||||
|
if media_info.media_length is None:
|
||||||
|
logger.warning(
|
||||||
|
"Forbidding avatar change to %s: unknown media size",
|
||||||
|
mxc,
|
||||||
|
)
|
||||||
|
return False
|
||||||
# Ensure avatar does not exceed max allowed avatar size
|
# Ensure avatar does not exceed max allowed avatar size
|
||||||
if media_info.media_length > self.max_avatar_size:
|
if media_info.media_length > self.max_avatar_size:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
|
|
@ -20,11 +20,12 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from synapse.api.constants import ReceiptTypes
|
from synapse.api.constants import ReceiptTypes
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import SynapseError
|
||||||
from synapse.util.async_helpers import Linearizer
|
from synapse.util.async_helpers import Linearizer
|
||||||
|
from synapse.types import JsonDict
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
@ -39,7 +40,11 @@ class ReadMarkerHandler:
|
||||||
self.read_marker_linearizer = Linearizer(name="read_marker")
|
self.read_marker_linearizer = Linearizer(name="read_marker")
|
||||||
|
|
||||||
async def received_client_read_marker(
|
async def received_client_read_marker(
|
||||||
self, room_id: str, user_id: str, event_id: str
|
self,
|
||||||
|
room_id: str,
|
||||||
|
user_id: str,
|
||||||
|
event_id: str,
|
||||||
|
extra_content: Optional[JsonDict] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Updates the read marker for a given user in a given room if the event ID given
|
"""Updates the read marker for a given user in a given room if the event ID given
|
||||||
is ahead in the stream relative to the current read marker.
|
is ahead in the stream relative to the current read marker.
|
||||||
|
@ -71,7 +76,7 @@ class ReadMarkerHandler:
|
||||||
should_update = event_ordering > old_event_ordering
|
should_update = event_ordering > old_event_ordering
|
||||||
|
|
||||||
if should_update:
|
if should_update:
|
||||||
content = {"event_id": event_id}
|
content = {"event_id": event_id, **(extra_content or {})}
|
||||||
await self.account_data_handler.add_account_data_to_room(
|
await self.account_data_handler.add_account_data_to_room(
|
||||||
user_id, room_id, ReceiptTypes.FULLY_READ, content
|
user_id, room_id, ReceiptTypes.FULLY_READ, content
|
||||||
)
|
)
|
||||||
|
|
|
@ -181,6 +181,7 @@ class ReceiptsHandler:
|
||||||
user_id: UserID,
|
user_id: UserID,
|
||||||
event_id: str,
|
event_id: str,
|
||||||
thread_id: Optional[str],
|
thread_id: Optional[str],
|
||||||
|
extra_content: Optional[JsonDict] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Called when a client tells us a local user has read up to the given
|
"""Called when a client tells us a local user has read up to the given
|
||||||
event_id in the room.
|
event_id in the room.
|
||||||
|
@ -197,7 +198,7 @@ class ReceiptsHandler:
|
||||||
user_id=user_id.to_string(),
|
user_id=user_id.to_string(),
|
||||||
event_ids=[event_id],
|
event_ids=[event_id],
|
||||||
thread_id=thread_id,
|
thread_id=thread_id,
|
||||||
data={"ts": int(self.clock.time_msec())},
|
data={"ts": int(self.clock.time_msec()), **(extra_content or {})},
|
||||||
)
|
)
|
||||||
|
|
||||||
is_new = await self._handle_new_receipts([receipt])
|
is_new = await self._handle_new_receipts([receipt])
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user