mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-07-05 05:11:39 +00:00
Compare commits
147 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61a2e236b6 | |||
| d4fdf87daa | |||
| 513259a837 | |||
| 0f14a91bf3 | |||
| d557ed9a2c | |||
| cad2bb659b | |||
| 4ee69f9061 | |||
| 9812067c39 | |||
| 10136d4f78 | |||
| d6d0694387 | |||
| 0db74089c1 | |||
| efe37dab12 | |||
| 1f16468dac | |||
| 00bdffb783 | |||
| ed83d8fbb4 | |||
| 50f22cbf10 | |||
| 476f5249ce | |||
| d4eff0256c | |||
| 23aa0f5005 | |||
| 0594892921 | |||
| 5c16cb60fd | |||
| 809a9429dc | |||
| 707587aa15 | |||
| a10c709f1a | |||
| 7407435334 | |||
| cb7c678a34 | |||
| 0db01a6763 | |||
| d7b066c03a | |||
| 5c2afd9f0d | |||
| 886aaf0e17 | |||
| 86a33b5eb3 | |||
| 01dcb9cf9d | |||
| 5effc3411e | |||
| d67000f0bc | |||
| f294361eb2 | |||
| d1eeefed77 | |||
| 1c347841ce | |||
| a73973197d | |||
| 74e0d87c0d | |||
| 3691cccf11 | |||
| 86368b46b0 | |||
| 71a44bf7ea | |||
| 81b865f258 | |||
| b0d90e614c | |||
| e6f8e6c868 | |||
| db460e0a59 | |||
| 810b40536b | |||
| 577b33599f | |||
| 573f2e4892 | |||
| 8aa93c7839 | |||
| 9aed687ee1 | |||
| c3df2e4570 | |||
| 8c178c32f9 | |||
| e8d409c6ed | |||
| b50c7027f5 | |||
| 0ed101e973 | |||
| 2d7a35310f | |||
| cf1b3c6d4b | |||
| 07f7d6f071 | |||
| 7d945bbd5d | |||
| 42039b2090 | |||
| dd7ca6b12e | |||
| b1c6be012a | |||
| 835308628e | |||
| c1486f425e | |||
| c80896dcb0 | |||
| 77b12692bb | |||
| 57237e831a | |||
| d62c48ebf7 | |||
| e2e85b962a | |||
| 788697d563 | |||
| 64ecd762be | |||
| 5cb0db6f31 | |||
| 58e41d48c7 | |||
| 67466b015b | |||
| 0ea68f27a2 | |||
| a3e57dbab4 | |||
| 7ece15bb1a | |||
| 336b32dead | |||
| 1faa09b6ce | |||
| d7a51c7107 | |||
| 30c9d6d2df | |||
| 74841b6711 | |||
| dabbdc7517 | |||
| 793d399477 | |||
| 15d69aefbb | |||
| 77b1652f4a | |||
| 5f9594363d | |||
| 5cba4b126f | |||
| d8a7f7c7ca | |||
| d3fca86dec | |||
| 5f88abf341 | |||
| 416814094c | |||
| 5b8799e71f | |||
| cc5349ee57 | |||
| 7b68572b2e | |||
| 057eb9f644 | |||
| 253603edbc | |||
| b771b9d160 | |||
| eb829c2951 | |||
| d32b39181a | |||
| 72b99a1f84 | |||
| ae37f218a2 | |||
| 40cecca103 | |||
| 2a80a82f74 | |||
| fbf4eac2dc | |||
| 4784010702 | |||
| 1c88854a54 | |||
| e0fe71c708 | |||
| 0f0dcb4f58 | |||
| 367c42ad28 | |||
| c8e0f7ebd3 | |||
| fdc9aec534 | |||
| 5f9cc83b18 | |||
| 47051af392 | |||
| c1a6e649da | |||
| 1d172be503 | |||
| f01e119890 | |||
| 4d27a935d6 | |||
| 512a96f832 | |||
| 6715f63acc | |||
| 3764faeefc | |||
| 5d4b7bfea3 | |||
| 4df08779e3 | |||
| 6b835a327d | |||
| 7dd61cd560 | |||
| d9535eccf1 | |||
| a97f91e079 | |||
| f0401b4fc7 | |||
| cda64b880a | |||
| 1f6cab9e2e | |||
| afa80576f4 | |||
| 5a63eb729c | |||
| 27da50136e | |||
| db724b67ff | |||
| 14a0d2f538 | |||
| 3b9932e09c | |||
| 02409c06b8 | |||
| bb51db0d7d | |||
| 834f2caffe | |||
| 202786c46b | |||
| 035bfea93c | |||
| 185f8c42dc | |||
| d5fc81d39e | |||
| 1cd0228d87 | |||
| 4968d4c8b7 | |||
| bb6ec1f352 |
@@ -44,7 +44,7 @@ runs:
|
||||
|
||||
- name: Login to builtin registry
|
||||
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
|
||||
with:
|
||||
registry: ${{ env.BUILTIN_REGISTRY }}
|
||||
username: ${{ inputs.registry_user }}
|
||||
@@ -52,7 +52,7 @@ runs:
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
with:
|
||||
# Use persistent BuildKit if BUILDKIT_ENDPOINT is set (e.g. tcp://buildkit:8125)
|
||||
driver: ${{ env.BUILDKIT_ENDPOINT != '' && 'remote' || 'docker-container' }}
|
||||
@@ -61,7 +61,7 @@ runs:
|
||||
- name: Extract metadata (tags) for Docker
|
||||
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
|
||||
id: meta
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
|
||||
with:
|
||||
flavor: |
|
||||
latest=auto
|
||||
|
||||
@@ -67,7 +67,7 @@ runs:
|
||||
uses: ./.forgejo/actions/rust-toolchain
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
with:
|
||||
# Use persistent BuildKit if BUILDKIT_ENDPOINT is set (e.g. tcp://buildkit:8125)
|
||||
driver: ${{ env.BUILDKIT_ENDPOINT != '' && 'remote' || 'docker-container' }}
|
||||
@@ -75,11 +75,11 @@ runs:
|
||||
|
||||
- name: Set up QEMU
|
||||
if: ${{ env.BUILDKIT_ENDPOINT == '' }}
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
|
||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4
|
||||
|
||||
- name: Login to builtin registry
|
||||
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
|
||||
with:
|
||||
registry: ${{ env.BUILTIN_REGISTRY }}
|
||||
username: ${{ inputs.registry_user }}
|
||||
@@ -87,7 +87,7 @@ runs:
|
||||
|
||||
- name: Extract metadata (labels, annotations) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
|
||||
with:
|
||||
images: ${{ inputs.images }}
|
||||
# default labels & annotations: https://github.com/docker/metadata-action/blob/master/src/meta.ts#L509
|
||||
|
||||
@@ -71,7 +71,7 @@ runs:
|
||||
|
||||
- name: Install timelord-cli and git-warp-time
|
||||
if: steps.check-binaries.outputs.need-install == 'true'
|
||||
uses: https://github.com/taiki-e/install-action@3771e22aa892e03fd35585fae288baad1755695c # v2
|
||||
uses: https://github.com/taiki-e/install-action@e49978b799e49ff429d162b7a30601a569ab6538 # v2
|
||||
with:
|
||||
tool: git-warp-time,timelord-cli@3.0.1
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
registry_password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push Docker image by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
||||
with:
|
||||
context: .
|
||||
file: "docker/Dockerfile"
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
registry_password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push max-perf Docker image by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
||||
with:
|
||||
context: .
|
||||
file: "docker/Dockerfile"
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
name: Renovate
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/renovatebot/renovate:43.181.0@sha256:aa64263a30f1ef92a661f1c3421ab13f0268131ffd5bfc8e16bdd98977a67eed
|
||||
image: ghcr.io/renovatebot/renovate:43.214.6@sha256:fd228b92f067204e444ddea1ec2fefb007592f9a46845e966f9334d5bd4bb52c
|
||||
options: --tmpfs /tmp:exec
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
github: [JadedBlueEyes, nexy7574, gingershaped]
|
||||
github: [JadedBlueEyes, timedoutuk, gingershaped]
|
||||
custom:
|
||||
- https://timedout.uk/donate.html
|
||||
- https://jade.ellis.link/sponsors
|
||||
|
||||
@@ -24,7 +24,7 @@ repos:
|
||||
- id: check-added-large-files
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.46.2
|
||||
rev: v1.47.2
|
||||
hooks:
|
||||
- id: typos
|
||||
- id: typos
|
||||
|
||||
Generated
+280
-92
@@ -253,9 +253,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-rs"
|
||||
@@ -530,15 +530,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
|
||||
checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.2"
|
||||
version = "3.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
@@ -678,9 +678,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
version = "0.4.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -827,7 +827,7 @@ dependencies = [
|
||||
"opentelemetry-otlp",
|
||||
"opentelemetry_sdk",
|
||||
"parking_lot",
|
||||
"reqwest 0.13.3",
|
||||
"reqwest 0.13.4",
|
||||
"rustls",
|
||||
"sentry",
|
||||
"sentry-tower",
|
||||
@@ -893,7 +893,7 @@ dependencies = [
|
||||
"lettre",
|
||||
"log",
|
||||
"rand 0.10.1",
|
||||
"reqwest 0.13.3",
|
||||
"reqwest 0.13.4",
|
||||
"ruma",
|
||||
"ruminuwuity",
|
||||
"serde",
|
||||
@@ -955,7 +955,7 @@ dependencies = [
|
||||
"rand 0.10.1",
|
||||
"rand_core 0.6.4",
|
||||
"regex",
|
||||
"reqwest 0.13.3",
|
||||
"reqwest 0.13.4",
|
||||
"ruma",
|
||||
"sanitize-filename",
|
||||
"serde",
|
||||
@@ -1066,7 +1066,7 @@ dependencies = [
|
||||
"either",
|
||||
"futures",
|
||||
"governor",
|
||||
"hickory-resolver",
|
||||
"hickory-resolver 0.25.2",
|
||||
"http",
|
||||
"image",
|
||||
"ipaddress",
|
||||
@@ -1080,7 +1080,7 @@ dependencies = [
|
||||
"recaptcha-verify",
|
||||
"regex",
|
||||
"reqwest 0.12.28",
|
||||
"reqwest 0.13.3",
|
||||
"reqwest 0.13.4",
|
||||
"ruma",
|
||||
"ruminuwuity",
|
||||
"rustyline-async",
|
||||
@@ -1088,6 +1088,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde-saphyr",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sha2 0.11.0",
|
||||
"termimad",
|
||||
"tokio",
|
||||
@@ -1107,18 +1108,29 @@ dependencies = [
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"base64 0.22.1",
|
||||
"conduwuit_api",
|
||||
"conduwuit_build_metadata",
|
||||
"conduwuit_core",
|
||||
"conduwuit_database",
|
||||
"conduwuit_service",
|
||||
"form_urlencoded",
|
||||
"futures",
|
||||
"lettre",
|
||||
"memory-serve",
|
||||
"rand 0.10.1",
|
||||
"recaptcha-verify",
|
||||
"reqwest 0.12.28",
|
||||
"ruma",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"thiserror",
|
||||
"tower-http",
|
||||
"tower-sec-fetch",
|
||||
"tower-sessions",
|
||||
"tower-sessions-core",
|
||||
"tracing",
|
||||
"url",
|
||||
"validator",
|
||||
]
|
||||
|
||||
@@ -1212,6 +1224,16 @@ dependencies = [
|
||||
"crossterm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.1"
|
||||
@@ -1402,9 +1424,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d765eb1c0bda10d31e0ea185f5ee15da532d60b0912d2bd1441783439e749c5"
|
||||
checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a"
|
||||
dependencies = [
|
||||
"link-section",
|
||||
"linktime-proc-macro",
|
||||
@@ -1526,6 +1548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1604,9 +1627,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dtor"
|
||||
version = "1.0.3"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2137ce22f50d4c43ce098daf41c904cc700de1ce8bc2daf53ed4e702180a464"
|
||||
checksum = "6d738e43aa64edab57c983d56de890d65fea7dc05605490c74451ce721dfd84b"
|
||||
dependencies = [
|
||||
"linktime-proc-macro",
|
||||
]
|
||||
@@ -1643,9 +1666,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -1709,7 +1732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1926,9 +1949,9 @@ checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "3.0.3"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
||||
checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
@@ -2183,6 +2206,30 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hickory-net"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2295ed2f9c31e471e1428a8f88a3f0e1f4b27c15049592138d1eebe9c35b183"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cfg-if",
|
||||
"data-encoding",
|
||||
"futures-channel",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hickory-proto 0.26.1",
|
||||
"idna",
|
||||
"ipnet",
|
||||
"jni",
|
||||
"rand 0.10.1",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hickory-proto"
|
||||
version = "0.25.2"
|
||||
@@ -2209,6 +2256,26 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hickory-proto"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bab31817bfb44672a252e97fe81cd0c18d1b2cf892108922f6818820df8c643"
|
||||
dependencies = [
|
||||
"data-encoding",
|
||||
"idna",
|
||||
"ipnet",
|
||||
"jni",
|
||||
"once_cell",
|
||||
"prefix-trie",
|
||||
"rand 0.10.1",
|
||||
"ring",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hickory-resolver"
|
||||
version = "0.25.2"
|
||||
@@ -2217,7 +2284,7 @@ checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
"hickory-proto",
|
||||
"hickory-proto 0.25.2",
|
||||
"ipconfig",
|
||||
"moka",
|
||||
"once_cell",
|
||||
@@ -2231,6 +2298,32 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hickory-resolver"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d58d28879ceecde6607729660c2667a081ccdc082e082675042793960f178c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
"hickory-net",
|
||||
"hickory-proto 0.26.1",
|
||||
"ipconfig",
|
||||
"ipnet",
|
||||
"jni",
|
||||
"moka",
|
||||
"ndk-context",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"rand 0.10.1",
|
||||
"resolv-conf",
|
||||
"smallvec",
|
||||
"system-configuration",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.13.0"
|
||||
@@ -2267,9 +2360,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
||||
checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"itoa",
|
||||
@@ -2336,9 +2429,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.9.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
|
||||
checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -2600,6 +2693,9 @@ name = "ipnet"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
@@ -2686,9 +2782,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.98"
|
||||
version = "0.3.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
|
||||
checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
@@ -2828,9 +2924,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "link-section"
|
||||
version = "0.17.2"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d1e908a416d6e9f725743b84a36feea40c4c131e805fbc26d61f9f451f36080"
|
||||
checksum = "014e440054ce8170890229eeef5bcda955305e056ec713de40ed366944483f09"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
@@ -2840,9 +2936,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linktime-proc-macro"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44cd706ff0d503ee32b2071166510ca27e281228de10cd3aa8d35ff94560f81"
|
||||
checksum = "8c7b0a3383c2a1002d11349c92c85a666a5fb679e96c79d782cf0dbe557fd6ee"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
@@ -2873,9 +2969,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
version = "0.4.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
|
||||
|
||||
[[package]]
|
||||
name = "loole"
|
||||
@@ -3011,9 +3107,9 @@ checksum = "1b7a5041e12946f8b7d3f5a9d96383a19d694b9335457c522be7815b9abafb02"
|
||||
|
||||
[[package]]
|
||||
name = "minicbor-serde"
|
||||
version = "0.6.2"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80047f75e28e3b38f6ab2ec3c2c7669f6b411fa6f8424e1a90a3fd784b19a3f4"
|
||||
checksum = "293c7245401f035e2dcc4b12ebdb5c9d8847247fc79fe1b5b0a0d58d7275324c"
|
||||
dependencies = [
|
||||
"minicbor",
|
||||
"serde",
|
||||
@@ -3083,6 +3179,12 @@ dependencies = [
|
||||
"pxfm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-context"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.6"
|
||||
@@ -3138,7 +3240,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3438,7 +3540,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"opentelemetry",
|
||||
"reqwest 0.13.3",
|
||||
"reqwest 0.13.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3453,7 +3555,7 @@ dependencies = [
|
||||
"opentelemetry-proto",
|
||||
"opentelemetry_sdk",
|
||||
"prost",
|
||||
"reqwest 0.13.3",
|
||||
"reqwest 0.13.4",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tonic",
|
||||
@@ -3475,9 +3577,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry_sdk"
|
||||
version = "0.32.0"
|
||||
version = "0.32.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368afaed344110f40b179bb8fbe54bc52d98f9bd2b281799ef32487c2650c956"
|
||||
checksum = "9b59f80e1ac4d5ff7a2db8fb6c80badb7f0f3f858211fba08dd9aaec750894f9"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-executor",
|
||||
@@ -3723,6 +3825,17 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "prefix-trie"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cf6e3177f0684016a5c209b00882e15f8bdd3f3bb48f0491df10cd102d0c6e7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"ipnet",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
@@ -3820,9 +3933,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.13.3"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad"
|
||||
checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"memchr",
|
||||
@@ -4084,9 +4197,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.13.3"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0"
|
||||
checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@@ -4096,7 +4209,7 @@ dependencies = [
|
||||
"h2",
|
||||
"h3",
|
||||
"h3-quinn",
|
||||
"hickory-resolver",
|
||||
"hickory-resolver 0.26.1",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
@@ -4150,8 +4263,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.15.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"js_int",
|
||||
@@ -4169,8 +4282,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4181,8 +4294,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.23.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.24.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"assign",
|
||||
@@ -4203,8 +4316,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.19.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64 0.22.1",
|
||||
@@ -4236,8 +4349,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.33.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.34.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"indexmap",
|
||||
@@ -4257,8 +4370,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"headers",
|
||||
@@ -4281,7 +4394,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.12.1"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror",
|
||||
@@ -4289,8 +4402,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.19.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"cfg-if",
|
||||
@@ -4305,8 +4418,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4317,8 +4430,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.20.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.21.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
@@ -4333,8 +4446,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruma-state-res"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
version = "0.17.0"
|
||||
source = "git+https://github.com/gingershaped/ruwuma.git?rev=a0178c4e5e1729d27cf2f1c4dacf77b763987749#a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4415,7 +4528,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4462,7 +4575,7 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
|
||||
dependencies = [
|
||||
"core-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
"jni",
|
||||
"log",
|
||||
@@ -4474,7 +4587,7 @@ dependencies = [
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4570,7 +4683,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
@@ -4604,7 +4717,7 @@ checksum = "931a20b0da02350676e3d6d3c9028d58eaa448cf42a866712eec5845a505421e"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"httpdate",
|
||||
"reqwest 0.13.3",
|
||||
"reqwest 0.13.4",
|
||||
"sentry-backtrace",
|
||||
"sentry-contexts",
|
||||
"sentry-core",
|
||||
@@ -4747,7 +4860,7 @@ checksum = "dcc7fe48e34d02a97bc8e6253b8b91e5a47fe2c47eaacb5149cefbb69922eaf0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"annotate-snippets",
|
||||
"base64 0.22.1",
|
||||
"base64 0.21.7",
|
||||
"encoding_rs_io",
|
||||
"getrandom 0.3.4",
|
||||
"granit-parser",
|
||||
@@ -4793,9 +4906,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -4817,9 +4930,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_regex"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
|
||||
checksum = "bafc8d0c5330cecff10f16b459b479fd9acaa5b4acd7167301414e21b0057012"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -5027,7 +5140,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5129,6 +5242,27 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tagptr"
|
||||
version = "0.2.0"
|
||||
@@ -5543,6 +5677,22 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-cookies"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "151b5a3e3c45df17466454bb74e9ecedecc955269bdedbf4d150dfa393b55a36"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"cookie",
|
||||
"futures-util",
|
||||
"http",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.11"
|
||||
@@ -5591,6 +5741,44 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "518dca34b74a17cadfcee06e616a09d2bd0c3984eff1769e1e76d58df978fc78"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"http",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower-cookies",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tower-sessions-core",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-sessions-core"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "568531ec3dfcf3ffe493de1958ae5662a0284ac5d767476ecdb6a34ff8c6b06c"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"base64 0.22.1",
|
||||
"futures",
|
||||
"http",
|
||||
"parking_lot",
|
||||
"rand 0.9.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
@@ -5905,9 +6093,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.121"
|
||||
version = "0.2.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
|
||||
checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -5918,9 +6106,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.71"
|
||||
version = "0.4.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8"
|
||||
checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -5928,9 +6116,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.121"
|
||||
version = "0.2.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
|
||||
checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -5938,9 +6126,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.121"
|
||||
version = "0.2.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
|
||||
checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
@@ -5951,9 +6139,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.121"
|
||||
version = "0.2.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
|
||||
checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -6007,9 +6195,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.98"
|
||||
version = "0.3.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa"
|
||||
checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -6086,7 +6274,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+8
-4
@@ -343,8 +343,8 @@ version = "1.1.1"
|
||||
# Used for matrix spec type definitions and helpers
|
||||
[workspace.dependencies.ruma]
|
||||
# version = "0.14.1"
|
||||
git = "https://github.com/ruma/ruma.git"
|
||||
rev = "9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||
git = "https://github.com/gingershaped/ruwuma.git"
|
||||
rev = "a0178c4e5e1729d27cf2f1c4dacf77b763987749"
|
||||
features = [
|
||||
"appservice-api-c",
|
||||
"client-api",
|
||||
@@ -373,12 +373,13 @@ features = [
|
||||
"unstable-msc4195",
|
||||
"unstable-msc4203",
|
||||
"unstable-msc4310",
|
||||
"unstable-msc4373",
|
||||
"unstable-msc4380",
|
||||
"unstable-msc4143",
|
||||
"unstable-msc4293",
|
||||
"unstable-msc4406",
|
||||
"unstable-msc4439",
|
||||
"unstable-msc4466",
|
||||
"unstable-msc4484",
|
||||
"unstable-extensible-events",
|
||||
]
|
||||
|
||||
@@ -534,7 +535,7 @@ version = "2.1.1"
|
||||
features = ["std"]
|
||||
|
||||
[workspace.dependencies.minicbor-serde]
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
features = ["std"]
|
||||
|
||||
[workspace.dependencies.maplit]
|
||||
@@ -559,6 +560,9 @@ features = ["std"]
|
||||
[workspace.dependencies.nonzero_ext]
|
||||
version = "0.3.0"
|
||||
|
||||
[workspace.dependencies.serde_urlencoded]
|
||||
version = "0.7.1"
|
||||
|
||||
#
|
||||
# Patches
|
||||
#
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Users may now be forbidden from deactivating their own accounts with the new `allow_deactivation` config option. Contributed by @ginger.
|
||||
@@ -0,0 +1 @@
|
||||
Added support for authenticating clients using the new OAuth 2.0 login API. Contributed by @ginger.
|
||||
@@ -0,0 +1 @@
|
||||
Added support for MSC4466, which allows clients to customize how changes to a user's global profile are propagated. Contributed by @ginger.
|
||||
@@ -0,0 +1 @@
|
||||
Devices which set their presence as "offline" will no longer be considered for presence updates. Contributed by @timedout.
|
||||
@@ -0,0 +1 @@
|
||||
Updated [MSC4284: Policy Servers](https://github.com/matrix-org/matrix-spec-proposals/pull/4284) implementation to support the newly stabilised proposal. Contributed by @nex.
|
||||
@@ -0,0 +1 @@
|
||||
Added config option for default room ACLs. Contributed by @eve.
|
||||
@@ -0,0 +1,9 @@
|
||||
Implemented event rejection, which should resolve and prevent future netsplits of the kinds observed
|
||||
within some Continuwuity rooms.
|
||||
Also resolved several bugs related to both soft-failing events, and event backfilling, which should
|
||||
improve state resolution stability.
|
||||
The `!admin debug get-pdu` command was updated to disambiguate event acceptance status, and
|
||||
`!admin debug show-auth-chain` was added to visually display event auth chains, which may assist
|
||||
developers in debugging strangely complex events.
|
||||
|
||||
Contributed by @nex.
|
||||
@@ -0,0 +1 @@
|
||||
Fixed several bugs in the `POST /_matrix/client/v3/rooms/{roomId}/upgrade` endpoint. Contributed by @nex.
|
||||
@@ -0,0 +1 @@
|
||||
Added full support for [MSC4168: Update `m.space.*` state on room upgrade](https://github.com/matrix-org/matrix-spec-proposals/pull/4168). Contributed by @nex.
|
||||
@@ -0,0 +1 @@
|
||||
Remove support for MSC4373, as the MSC is now closed. Contributed by @vel.
|
||||
+77
-33
@@ -372,21 +372,18 @@
|
||||
#
|
||||
#federation_timeout = 60
|
||||
|
||||
# MSC4284 Policy server request timeout (seconds). Generally policy
|
||||
# Policy server request timeout (seconds). Generally policy
|
||||
# servers should respond near instantly, however may slow down under
|
||||
# load. If a policy server doesn't respond in a short amount of time, the
|
||||
# room it is configured in may become unusable if this limit is set too
|
||||
# high. 10 seconds is a good default, however dropping this to 3-5 seconds
|
||||
# can be acceptable.
|
||||
# high. 30 seconds is a good default, however lower values may be
|
||||
# acceptable if temporary send failures are an okay trade-off.
|
||||
#
|
||||
# Please be aware that policy requests are *NOT* currently re-tried, so if
|
||||
# a spam check request fails, the event will be assumed to be not spam,
|
||||
# which in some cases may result in spam being sent to or received from
|
||||
# the room that would typically be prevented.
|
||||
#
|
||||
# About policy servers: https://matrix.org/blog/2025/04/introducing-policy-servers/
|
||||
# (Stabilized in Matrix v1.18)
|
||||
#
|
||||
#policy_server_request_timeout = 10
|
||||
#policy_server_request_timeout = 30
|
||||
|
||||
# Federation client idle connection pool timeout (seconds).
|
||||
#
|
||||
@@ -524,17 +521,15 @@
|
||||
#
|
||||
#recaptcha_private_site_key =
|
||||
|
||||
# Policy documents, such as terms and conditions or a privacy policy,
|
||||
# which users must agree to when registering an account.
|
||||
# Controls whether users are allowed to deactivate their own accounts
|
||||
# through the account management panel or their Matrix clients. Server
|
||||
# admins can always deactivate users using the relevant admin commands.
|
||||
#
|
||||
# Example:
|
||||
# ```ignore
|
||||
# [global.registration_terms.privacy_policy]
|
||||
# en = { name = "Privacy Policy", url = "https://homeserver.example/en/privacy_policy.html" }
|
||||
# es = { name = "Política de Privacidad", url = "https://homeserver.example/es/privacy_policy.html" }
|
||||
# ```
|
||||
# Note that, in some jurisdictions, you may be legally required to honor
|
||||
# users who request to deactivate their accounts if you set this option
|
||||
# to `false`.
|
||||
#
|
||||
#registration_terms = {}
|
||||
#allow_deactivation = true
|
||||
|
||||
# Controls whether encrypted rooms and events are allowed.
|
||||
#
|
||||
@@ -624,6 +619,30 @@
|
||||
#
|
||||
#default_room_version = "12"
|
||||
|
||||
# A default allow value for the Access Control List when creating a room.
|
||||
#
|
||||
# If a list is provided, new rooms will be created with
|
||||
# a m.room.server_acl event. Only servers which match one of the patterns
|
||||
# in the list will be permitted to participate in the room.
|
||||
#
|
||||
# ACLs in existing rooms will not be updated automatically. This is not
|
||||
# a substitute for moderation bots.
|
||||
#
|
||||
#default_room_acl_allow =
|
||||
|
||||
# A default deny value for the Access Control List when creating a room.
|
||||
#
|
||||
# If a list is provided, new rooms will be created with
|
||||
# a m.room.server_acl event. Servers which match one of the patterns
|
||||
# in the list will be NOT permitted to participate in the room.
|
||||
#
|
||||
# This config cannot be used if the default_room_acl_allow config is used.
|
||||
#
|
||||
# ACLs in existing rooms will not be updated automatically. This is not
|
||||
# a substitute for moderation bots.
|
||||
#
|
||||
#default_room_acl_deny =
|
||||
|
||||
# Enable OpenTelemetry OTLP tracing export. This replaces the deprecated
|
||||
# Jaeger exporter. Traces will be sent via OTLP to a collector (such as
|
||||
# Jaeger) that supports the OpenTelemetry Protocol.
|
||||
@@ -1570,19 +1589,6 @@
|
||||
#
|
||||
#block_non_admin_invites = false
|
||||
|
||||
# Enable or disable making requests to MSC4284 Policy Servers.
|
||||
# It is recommended you keep this enabled unless you experience frequent
|
||||
# connectivity issues, such as in a restricted networking environment.
|
||||
#
|
||||
#enable_msc4284_policy_servers = true
|
||||
|
||||
# Enable running locally generated events through configured MSC4284
|
||||
# policy servers. You may wish to disable this if your server is
|
||||
# single-user for a slight speed benefit in some rooms, but otherwise
|
||||
# should leave it enabled.
|
||||
#
|
||||
#policy_server_check_own_events = true
|
||||
|
||||
# Allow admins to enter commands in rooms other than "#admins" (admin
|
||||
# room) by prefixing your message with "\!admin" or "\\!admin" followed up
|
||||
# a normal continuwuity admin command. The reply will be publicly visible
|
||||
@@ -1849,6 +1855,11 @@
|
||||
#
|
||||
#support_page =
|
||||
|
||||
# The ed25519 public key for the policy server available at this server's
|
||||
# name. Must be unpadded base64.
|
||||
#
|
||||
#policy_server_public_key =
|
||||
|
||||
# Role string for server support contacts, to be served as part of the
|
||||
# MSC1929 server support endpoint at /.well-known/matrix/support.
|
||||
#
|
||||
@@ -1959,8 +1970,10 @@
|
||||
#
|
||||
#sender =
|
||||
|
||||
# Whether to require that users provide an email address when they
|
||||
# register.
|
||||
# Whether to allow public registration with an email address.
|
||||
#
|
||||
# Note that, if this option is enabled, anyone will be able to register an
|
||||
# account with just an email address.
|
||||
#
|
||||
# If either this option or `require_email_for_token_registration` are set,
|
||||
# users will not be allowed to remove their email address.
|
||||
@@ -1968,6 +1981,37 @@
|
||||
#require_email_for_registration = false
|
||||
|
||||
# Whether to require that users who register with a registration token
|
||||
# provide an email address.
|
||||
# provide an email address. This option is independent of
|
||||
# `require_email_for_registration`.
|
||||
#
|
||||
#require_email_for_token_registration = false
|
||||
|
||||
#[global.registration_terms]
|
||||
|
||||
# The language code to provide to clients along with the policy documents.
|
||||
#
|
||||
#language = "en"
|
||||
|
||||
# Policy documents, such as terms and conditions or a privacy policy,
|
||||
# which users must agree to when registering an account.
|
||||
#
|
||||
# Example:
|
||||
# ```ignore
|
||||
# [global.registration_terms.documents]
|
||||
# privacy_policy = { name = "Privacy Policy", url = "https://homeserver.example/en/privacy_policy.html" }
|
||||
# ```
|
||||
#
|
||||
#documents = {}
|
||||
|
||||
#[global.oauth]
|
||||
|
||||
# The compatibility mode to use for OAuth.
|
||||
#
|
||||
# - "disabled": OAuth will be unavailable. Users will only be able to log
|
||||
# in using legacy authentication.
|
||||
# - "hybrid": OAuth and legacy authentication will both be available. Some
|
||||
# clients may only use one or the other.
|
||||
# - "exclusive": Only OAuth will be available. Clients which require
|
||||
# legacy authentication will be unable to log in.
|
||||
#
|
||||
#compatibility_mode = "hybrid"
|
||||
|
||||
Generated
+15
-15
@@ -3,11 +3,11 @@
|
||||
"advisory-db": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1778915282,
|
||||
"narHash": "sha256-iqXYpuCoWoGypnpM5ceXN748QlYeBXDtZx0uI98qFLo=",
|
||||
"lastModified": 1779575509,
|
||||
"narHash": "sha256-wXKYURZz76ZC5lbuDA1oVQA/MxSB3pSJ1raF1HG0oIc=",
|
||||
"owner": "rustsec",
|
||||
"repo": "advisory-db",
|
||||
"rev": "f2ae5fc8e5d208373b6c838f9676434525327a72",
|
||||
"rev": "831c50f4a4304068f125e603add6a8839f08b3eb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -18,11 +18,11 @@
|
||||
},
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1778106249,
|
||||
"narHash": "sha256-cM/AuKy5tMhwOOQIbha8ZRRMHVfNf7cv2aljIw+qoCg=",
|
||||
"lastModified": 1779130139,
|
||||
"narHash": "sha256-BLrtr42azquO7MdGFU5a7KiMl3YpFlTeIXqy1fT5GlQ=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "6d015ea29630b7ad2402841386da2cb617a470a7",
|
||||
"rev": "edb38893982a3338972bb4a2ec7ce7c29ba10fd9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -39,11 +39,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1778919578,
|
||||
"narHash": "sha256-+z+jgTly48gsAiX8rOe/vs8C/2G4vdCpcEtqMJUpFqw=",
|
||||
"lastModified": 1779612045,
|
||||
"narHash": "sha256-+7lfNVnmXJDkiRYHd5NoNwYoyUcc0LcXPaIJqjO7VWM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "ecd6d4ff22cfdb1339b2915455a2ff4dc85bf52e",
|
||||
"rev": "d7be747f0a65af378de515fc3cee131bf99a008f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -89,11 +89,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1778869304,
|
||||
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
|
||||
"lastModified": 1779508470,
|
||||
"narHash": "sha256-Ap9KJX+5xHIn3bPIpfNgT6MEXdAECECwo4/rmlQD74M=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
|
||||
"rev": "29916453413845e54a65b8a1cf996842300cd299",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -132,11 +132,11 @@
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1778854817,
|
||||
"narHash": "sha256-iG+VuMy8W585geVVCUd7pR025WsY3ZkgSv5Yt5bxDmQ=",
|
||||
"lastModified": 1779569060,
|
||||
"narHash": "sha256-NSnk5D+3KEfRdbgPijs33N2RAKSG6A74SwfnynLcouo=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "1a68212c5683555ad80f0eab71db9715c6d52145",
|
||||
"rev": "987ea33645ab1c709b1df6823038abcb2fe8973e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
liburing,
|
||||
craneLib,
|
||||
pkg-config,
|
||||
callPackage,
|
||||
rustPlatform,
|
||||
cargoExtraArgs ? "",
|
||||
rustflags ? "",
|
||||
target_cpu ? null,
|
||||
rocksdb ? callPackage ./rocksdb.nix { },
|
||||
rocksdb,
|
||||
profile ? "release",
|
||||
}:
|
||||
let
|
||||
@@ -60,7 +59,7 @@ craneLib.buildPackage (
|
||||
]
|
||||
}"
|
||||
|
||||
patchelf --set-rpath "$old_rpath:$extra_rpath" $out/bin/conduwuit
|
||||
patchelf --set-rpath "$old_rpath:$extra_rpath" $out/bin/conduwuit
|
||||
'';
|
||||
|
||||
meta = {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
rocksdb = pkgs.callPackage ./rocksdb.nix { };
|
||||
default = pkgs.callPackage ./continuwuity.nix {
|
||||
inherit self craneLib;
|
||||
inherit (self'.packages) rocksdb;
|
||||
# extra features via `cargoExtraArgs`
|
||||
cargoExtraArgs = "-F http3";
|
||||
# extra RUSTFLAGS via `rustflags`
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
stdenv,
|
||||
# stdenv,
|
||||
# enableJemalloc ? stdenv.hostPlatform.isLinux,
|
||||
enableJemalloc ? false,
|
||||
rocksdb,
|
||||
fetchFromGitea,
|
||||
rust-jemalloc-sys-unprefixed,
|
||||
@@ -13,7 +15,7 @@
|
||||
#
|
||||
# [1]: https://github.com/tikv/jemallocator/blob/ab0676d77e81268cd09b059260c75b38dbef2d51/jemalloc-sys/src/env.rs#L17
|
||||
jemalloc = rust-jemalloc-sys-unprefixed;
|
||||
enableJemalloc = stdenv.hostPlatform.isLinux;
|
||||
inherit enableJemalloc;
|
||||
}).overrideAttrs
|
||||
({
|
||||
version = "continuwuity-v0.5.0-unstable-2026-05-19";
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
file = inputs.self + "/rust-toolchain.toml";
|
||||
|
||||
# See also `rust-toolchain.toml`
|
||||
sha256 = "sha256-gh/xTkxKHL4eiRXzWv8KP7vfjSk61Iq48x47BEDFgfk=";
|
||||
sha256 = "sha256-mvUGEOHYJpn3ikC5hckneuGixaC+yGrkMM/liDIDgoU=";
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
Generated
+126
-126
@@ -125,14 +125,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rsbuild/core": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-2.0.6.tgz",
|
||||
"integrity": "sha512-0/u7oTgPp9NsL7E7qXzYiOOPAsOJiDbOr0FmG6gizJDIpYK8nospogNrwQ00SG0had9fdhLI7XkhP160IaLnWw==",
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-2.0.11.tgz",
|
||||
"integrity": "sha512-Mpp/viUSkVdSWJkFipdZxM2nUztrBwSnMm6Q86bPzLHtHnXqQ3VFpSMlA4wWRyySNddP6s6efKiVpx0ZOCf7Gg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rspack/core": "~2.0.3",
|
||||
"@swc/helpers": "^0.5.21"
|
||||
"@rspack/core": "~2.0.6",
|
||||
"@swc/helpers": "^0.5.23"
|
||||
},
|
||||
"bin": {
|
||||
"rsbuild": "bin/rsbuild.js"
|
||||
@@ -150,9 +150,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rsbuild/plugin-react": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@rsbuild/plugin-react/-/plugin-react-2.0.0.tgz",
|
||||
"integrity": "sha512-/1gzt39EGUSFEqB83g46QoOwsgv172HI18i6au1b6lgIaX4sv9stuX4ijdHbHCp8PqYEq+MyQ99jIQMO6I+etg==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rsbuild/plugin-react/-/plugin-react-2.0.1.tgz",
|
||||
"integrity": "sha512-n5m3VxEm6m3Dv1VkI0WnxsildySJ6M+QjGIzkZDy5UebRCIJ1Q/hlQVyhofBL6C+AcsF9fGjlHQkeiteXJSr3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -169,28 +169,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/binding": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.0.3.tgz",
|
||||
"integrity": "sha512-4exVNhGhW5RFHjK87XeTKbkA/qAgI5NHJlT1jNqiJv0gcUXLqTOEU3w7f8+f9zUo4JMFvPc0c9veOi4M19YYTg==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.0.6.tgz",
|
||||
"integrity": "sha512-z5EO9mPlmYNpHAlRGub0Chr6D+Klgy+tX36n7tCm7VRGRlwTmTU9wSENrYbHcCpFbegtrE0s30rDeTBeOu+JiQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"@rspack/binding-darwin-arm64": "2.0.3",
|
||||
"@rspack/binding-darwin-x64": "2.0.3",
|
||||
"@rspack/binding-linux-arm64-gnu": "2.0.3",
|
||||
"@rspack/binding-linux-arm64-musl": "2.0.3",
|
||||
"@rspack/binding-linux-x64-gnu": "2.0.3",
|
||||
"@rspack/binding-linux-x64-musl": "2.0.3",
|
||||
"@rspack/binding-wasm32-wasi": "2.0.3",
|
||||
"@rspack/binding-win32-arm64-msvc": "2.0.3",
|
||||
"@rspack/binding-win32-ia32-msvc": "2.0.3",
|
||||
"@rspack/binding-win32-x64-msvc": "2.0.3"
|
||||
"@rspack/binding-darwin-arm64": "2.0.6",
|
||||
"@rspack/binding-darwin-x64": "2.0.6",
|
||||
"@rspack/binding-linux-arm64-gnu": "2.0.6",
|
||||
"@rspack/binding-linux-arm64-musl": "2.0.6",
|
||||
"@rspack/binding-linux-x64-gnu": "2.0.6",
|
||||
"@rspack/binding-linux-x64-musl": "2.0.6",
|
||||
"@rspack/binding-wasm32-wasi": "2.0.6",
|
||||
"@rspack/binding-win32-arm64-msvc": "2.0.6",
|
||||
"@rspack/binding-win32-ia32-msvc": "2.0.6",
|
||||
"@rspack/binding-win32-x64-msvc": "2.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/binding-darwin-arm64": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.0.3.tgz",
|
||||
"integrity": "sha512-4UyCjLJwU/WxR6K1/gG4u3+jUsoaRHJ5rNu9fto/UbvrItwdlVNULChAApqZFw6mcSetMddSjSICeuj5pSB6sA==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.0.6.tgz",
|
||||
"integrity": "sha512-0giCKiWlBfcM4i2scv1j2k9HlSecO9Ybhaa5wsMUyvcFeKr9HbNHh7C2eDFlC6zaI85IUdY71TXF/g/Tcxr9MA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -202,9 +202,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-darwin-x64": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.0.3.tgz",
|
||||
"integrity": "sha512-K3evrbTgZNa8emEqk+AjDtbuoXZp5tPZz3pcEgETxuu3KanW8Zu+Fb+TUp1DEUcL0xOmHPPox8H2cZ3pF4Zmug==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.0.6.tgz",
|
||||
"integrity": "sha512-/mMo2IpI02aOKMlHbVbZue3TJxFqHGX+ibVTdEO+6bzRSuHs7+R9KM5U3XH2YxcWJy5Sid1X1T1pJAjsXcE3rA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -216,9 +216,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-arm64-gnu": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.0.3.tgz",
|
||||
"integrity": "sha512-aPLDaaTtX1wqjLYAIHc2MGDQZtv1Hbjx47oaaefbWz5GbAnSA4P8jdYIeeGRyrqvQ0WqJXIWXgT0d/iXtes00A==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.0.6.tgz",
|
||||
"integrity": "sha512-H6ACzeM1KBxYDEF8YAim3501Jb1aCsSG79Gjm1M4pwJ5OJPK2ydiJEa438ugXmh0962eKYMHI2yZY0sQq8txaw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -233,9 +233,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-arm64-musl": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.0.3.tgz",
|
||||
"integrity": "sha512-0WulUQPop6vmSDfrTxghmVlm+6crU8/XqD2f0dOWbEniZVuDZJ5/Y/cBqTRyk3rjl0vrmUv3lc87/t7UgQJQSw==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.0.6.tgz",
|
||||
"integrity": "sha512-QTFmBg0n+L397Wi8CIjbd5pe/hxpHnqCDaG1A7e2NWX8Fj9zulAoKLiKflQa1ELEhAY4Foq88aX75+Ilt2tHcw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -250,9 +250,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-x64-gnu": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.0.3.tgz",
|
||||
"integrity": "sha512-fAhiMuV5omT53YMft+f3Y9euAFgspuyBAk9ZpeW2buL2TkuUMwP07adhhvQfKdQ5gpELfzmjQaRDGqaIT8UWiA==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.0.6.tgz",
|
||||
"integrity": "sha512-rerCAz022zf0ewxI+7n3SrqLEaxCL+MXRxKjK5FLUGFa8UkIrivq+VUP/1OB6JLh2Bucebc7Y9WoWHvtk22mLA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -267,9 +267,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-x64-musl": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.0.3.tgz",
|
||||
"integrity": "sha512-0kcuFoZ8vy2iNWoISFOZt+/Ujo7LRLrzE7h07AV5r+oN/mv+/v14Sd/8NUtDIScCkrYOszYq/QS31e6t0UrVfw==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.0.6.tgz",
|
||||
"integrity": "sha512-96IgOFXQjX6Wbxd+DCYJFy2r/VMu1OoHifW4Cr3kGTYDKoQOIMLwb0ieu/ILp2dGWFMZo5S8odiByAmNICAOIA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -284,9 +284,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-wasm32-wasi": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.0.3.tgz",
|
||||
"integrity": "sha512-x2fsw7GzNZEnw444ikj4/b8kVjM0Y0TllxmizHpYZ9gmaQrOk5OXo9RQdz+l4zzoGors0l2IZP5Cc4GJNCaSoQ==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.0.6.tgz",
|
||||
"integrity": "sha512-0aWiF+qmdb0csp1x+MaR2o1pscoquLaEbLTVdKjmoTRs6sguMemtB1ObnVTahAUL73P66WePuNpFAJ81zNdqzQ==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
@@ -300,9 +300,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/binding-win32-arm64-msvc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.0.3.tgz",
|
||||
"integrity": "sha512-jqlxuVPdrgMuwj/HEjSkC/jmhl4fAuKyob36zJXq2uAusn2FRJ4kClGe1fLFpfxRXFVQAWwlAOwLJg8T0suuaA==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.0.6.tgz",
|
||||
"integrity": "sha512-BX638A1MXsjc2E3tUskVh3X/WBIHjLKK+lo395v7MmEL9u2BA6l3F6RyW+YaJOt5aEOOv83iA7iCZsviVZ49Uw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -314,9 +314,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-win32-ia32-msvc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.0.3.tgz",
|
||||
"integrity": "sha512-QM4JEuyk5QaZ5gnvnAIaCwVQzCkrD2E4Sud77kx/MVGDsRkcOlMx3blMC5QNHPDamRmWGk+7314YOQvRhKuWyg==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.0.6.tgz",
|
||||
"integrity": "sha512-DCK/+MlN35uvH7tp4j0hbg8wIs9MHArMIrNZXtiD8xP6DNw2wrXcGC1VaxxR5apyWpqXAfIL/KsXBiWS3ygCvg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -328,9 +328,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-win32-x64-msvc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.0.3.tgz",
|
||||
"integrity": "sha512-vSQNnAy0wswG6AfNRuArTHQBiXOXl+A9ddQxBFup4PMHUzXxKtsBLQzw7BgFC0EgrPeHbt+30j7sXVZKYukj4A==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.0.6.tgz",
|
||||
"integrity": "sha512-TxutgzdEX9BkAU/5liKxdQmggJ23INz7EZDWtzSJO6C2SiSYzTJdyPQDIJi1ddkM5TX/drzH184gAJMVOQefng==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -342,20 +342,20 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/core": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/core/-/core-2.0.3.tgz",
|
||||
"integrity": "sha512-2ufO/8FHIA/lX6UOgSsKPhpDvHr0sh9lYq/n/LsIZsTwu3973BGbu2fg1Akvuu3rEnskPqXjsqH2EPBzEA42uA==",
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/core/-/core-2.0.6.tgz",
|
||||
"integrity": "sha512-ronRqH1T2dYdMFVOQbGvDNxYaLugQK8qhNYYtS2DbOvPKQYvdIYWDenL9k/WV+hLoknnPWMn2ME2cKJcK3Po+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rspack/binding": "2.0.3"
|
||||
"@rspack/binding": "2.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@module-federation/runtime-tools": "^0.24.1 || ^2.0.0",
|
||||
"@swc/helpers": ">=0.5.1"
|
||||
"@swc/helpers": "^0.5.23"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@module-federation/runtime-tools": {
|
||||
@@ -383,17 +383,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspress/core": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/core/-/core-2.0.11.tgz",
|
||||
"integrity": "sha512-4YBOFmSMFv5GWrCa80qSIW8VxqZQQS/PknVq2r7Hb7kgfB38Fzciopn3hjb3hNwI4TTRbsi/Jev2HyRWD4bYAQ==",
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/core/-/core-2.0.14.tgz",
|
||||
"integrity": "sha512-k59i08zwBGgHrjHw8CK1m4CeTrKPvZRmV54bxubQl6AdDdmhJK6WrNg3UthwWmd38scKtqF40ATXDE8RMiNcNA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mdx-js/mdx": "^3.1.1",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
"@rsbuild/core": "^2.0.5",
|
||||
"@rsbuild/plugin-react": "~2.0.0",
|
||||
"@rspress/shared": "2.0.11",
|
||||
"@rsbuild/core": "^2.0.9",
|
||||
"@rsbuild/plugin-react": "~2.0.1",
|
||||
"@rspress/shared": "2.0.14",
|
||||
"@shikijs/rehype": "^4.0.2",
|
||||
"@types/unist": "^3.0.3",
|
||||
"@unhead/react": "^2.1.15",
|
||||
@@ -411,8 +411,8 @@
|
||||
"react-dom": "^19.2.6",
|
||||
"react-lazy-with-preload": "^2.2.1",
|
||||
"react-reconciler": "0.33.0",
|
||||
"react-render-to-markdown": "19.0.1",
|
||||
"react-router-dom": "^7.15.0",
|
||||
"react-render-to-markdown": "19.1.0",
|
||||
"react-router-dom": "^7.15.1",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-cjk-friendly": "^2.0.1",
|
||||
@@ -436,9 +436,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspress/plugin-client-redirects": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/plugin-client-redirects/-/plugin-client-redirects-2.0.11.tgz",
|
||||
"integrity": "sha512-DI9vod5mGccg57c19CuFpN3mGP1FEEueOUnEUz1UHXSyXg9YTj+ox7Xla4jUUzAzoPVGiWSSsfbtCTwdoxAsbg==",
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/plugin-client-redirects/-/plugin-client-redirects-2.0.14.tgz",
|
||||
"integrity": "sha512-/WpbWUiepQglpPeplxCnELe2c7VdBUxPiICPAVnS1ZxAFdYkIpW0C+Vbk1t08kZqx8EAZGu+s6Zy43zyQpjdxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -449,9 +449,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspress/plugin-sitemap": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/plugin-sitemap/-/plugin-sitemap-2.0.11.tgz",
|
||||
"integrity": "sha512-046LCHgbJXdaPipWB2SWMjZcAtIrOjXGZOD92xlTjhZ74D7Mk1Nod1MQdtOEoISWedcHdgpUVXMDbB1doKBpPQ==",
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/plugin-sitemap/-/plugin-sitemap-2.0.14.tgz",
|
||||
"integrity": "sha512-Gpone22PvXGfGRSyi/WM8IXgsvKhNspXqHjtPD3g62jX8SJL3kpj2YZ2V28WEkg672fICauUYXrpre74Rddcsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -462,26 +462,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspress/shared": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/shared/-/shared-2.0.11.tgz",
|
||||
"integrity": "sha512-7l5Pso4s597utJyisVEnd7n/40h053nfE8DwGQMeS8RLGtSwVgxFwNHsSrvQEGtFlLrg2aWWSITqnAVO1wfTew==",
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@rspress/shared/-/shared-2.0.14.tgz",
|
||||
"integrity": "sha512-sCe9tAo+s9tR4DmFSjMyHOxQvhzTSYXkkMUfVEo5w+uMCNXXGAIC6D0xAVDMHq1jIFF9ix47VxzlCo+CYNS14g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rsbuild/core": "^2.0.5",
|
||||
"@rsbuild/core": "^2.0.9",
|
||||
"@shikijs/rehype": "^4.0.2",
|
||||
"unified": "^11.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/core": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.0.2.tgz",
|
||||
"integrity": "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.1.0.tgz",
|
||||
"integrity": "sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/primitive": "4.0.2",
|
||||
"@shikijs/types": "4.0.2",
|
||||
"@shikijs/primitive": "4.1.0",
|
||||
"@shikijs/types": "4.1.0",
|
||||
"@shikijs/vscode-textmate": "^10.0.2",
|
||||
"@types/hast": "^3.0.4",
|
||||
"hast-util-to-html": "^9.0.5"
|
||||
@@ -491,28 +491,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/engine-javascript": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.0.2.tgz",
|
||||
"integrity": "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.1.0.tgz",
|
||||
"integrity": "sha512-YquhawCUgaBfhsS72e2Y/dI59gCBNPHu3fEO/tvLaXrTssxZrY5ddjtNLTwndrMgPo8b3IscE+xoICDzpTmlFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "4.0.2",
|
||||
"@shikijs/types": "4.1.0",
|
||||
"@shikijs/vscode-textmate": "^10.0.2",
|
||||
"oniguruma-to-es": "^4.3.4"
|
||||
"oniguruma-to-es": "^4.3.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/engine-oniguruma": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.0.2.tgz",
|
||||
"integrity": "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.1.0.tgz",
|
||||
"integrity": "sha512-axLpjVs45YBvvINa+dJF+NPW+KtFkNXsFr4SDw2BMj9GdeMnGxVB9PQb2xXlJYovslt/nz6giedAyOANkfc7hg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "4.0.2",
|
||||
"@shikijs/types": "4.1.0",
|
||||
"@shikijs/vscode-textmate": "^10.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -520,26 +520,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/langs": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.0.2.tgz",
|
||||
"integrity": "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.1.0.tgz",
|
||||
"integrity": "sha512-nwOMruEkbgdZfQ/b8CgpNBVOpvG1k0N5tbmgiFeqsan401+x3ILqlzZJowSla4Agmq4hG2Uf2wh5jLTEhR8VSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "4.0.2"
|
||||
"@shikijs/types": "4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/primitive": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.0.2.tgz",
|
||||
"integrity": "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.1.0.tgz",
|
||||
"integrity": "sha512-zx2/2Uwj2q9X3KSyYREEhXO23xBw5WUhP4orK2lE4r+t9JGITmEe0JH+wPmJhqHpOT2bRRs6lAL945+LDvOAGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "4.0.2",
|
||||
"@shikijs/types": "4.1.0",
|
||||
"@shikijs/vscode-textmate": "^10.0.2",
|
||||
"@types/hast": "^3.0.4"
|
||||
},
|
||||
@@ -548,16 +548,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/rehype": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.0.2.tgz",
|
||||
"integrity": "sha512-cmPlKLD8JeojasNFoY64162ScpEdEdQUMuVodPCrv1nx1z3bjmGwoKWDruQWa/ejSznImlaeB0Ty6Q3zPaVQAA==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.1.0.tgz",
|
||||
"integrity": "sha512-HQwltCcO2/UiFz44/8whyji4rP1VghLu++MgvQn+lQA8/gvuycGkay8DH8o8VAOvLBDKGOkBEw7cC1Cm33GObQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "4.0.2",
|
||||
"@shikijs/types": "4.1.0",
|
||||
"@types/hast": "^3.0.4",
|
||||
"hast-util-to-string": "^3.0.1",
|
||||
"shiki": "4.0.2",
|
||||
"shiki": "4.1.0",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-visit": "^5.1.0"
|
||||
},
|
||||
@@ -566,22 +566,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/themes": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.0.2.tgz",
|
||||
"integrity": "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.1.0.tgz",
|
||||
"integrity": "sha512-emCcTnUM7yO2wltYbaxm+yLvcCI4+h8XBKc4KmJ7EZUXoSGjcCHifkI//R4OFit9ewpg7H2/9tjOuXrT2v/Knw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "4.0.2"
|
||||
"@shikijs/types": "4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/types": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.0.2.tgz",
|
||||
"integrity": "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.1.0.tgz",
|
||||
"integrity": "sha512-3EQWX54fMpniOrDblzAhiwiJwpiTMW6+B9DWyUd9ska483tbayFYuw47UxwuPknI31bKnySfVQ/QW+jFL4rFdA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -600,9 +600,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz",
|
||||
"integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==",
|
||||
"version": "0.5.23",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz",
|
||||
"integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -682,9 +682,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||
"version": "19.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz",
|
||||
"integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@@ -2809,9 +2809,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-render-to-markdown": {
|
||||
"version": "19.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-render-to-markdown/-/react-render-to-markdown-19.0.1.tgz",
|
||||
"integrity": "sha512-BPv48o+ubcu2JyUDIktvJXFqLIZqR7hA4mvGu1eFIofz9fogT2me9UvXwRvqvGs9jEtNaJkxZIUKUX0oiK4hDA==",
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-render-to-markdown/-/react-render-to-markdown-19.1.0.tgz",
|
||||
"integrity": "sha512-dF9b3tO41ezqdmHP8X92kbHbMexJ6iC7iHw4ykC8fwiO7DgpFc9PhMoKlI+BcPzRxGcWgQSdrixVB9RykhjJpQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3164,18 +3164,18 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/shiki": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-4.0.2.tgz",
|
||||
"integrity": "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-4.1.0.tgz",
|
||||
"integrity": "sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/core": "4.0.2",
|
||||
"@shikijs/engine-javascript": "4.0.2",
|
||||
"@shikijs/engine-oniguruma": "4.0.2",
|
||||
"@shikijs/langs": "4.0.2",
|
||||
"@shikijs/themes": "4.0.2",
|
||||
"@shikijs/types": "4.0.2",
|
||||
"@shikijs/core": "4.1.0",
|
||||
"@shikijs/engine-javascript": "4.1.0",
|
||||
"@shikijs/engine-oniguruma": "4.1.0",
|
||||
"@shikijs/langs": "4.1.0",
|
||||
"@shikijs/themes": "4.1.0",
|
||||
"@shikijs/types": "4.1.0",
|
||||
"@shikijs/vscode-textmate": "^10.0.2",
|
||||
"@types/hast": "^3.0.4"
|
||||
},
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
|
||||
[toolchain]
|
||||
profile = "minimal"
|
||||
channel = "1.95.0"
|
||||
channel = "1.96.0"
|
||||
components = [
|
||||
# For rust-analyzer
|
||||
"rust-src",
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = conduwuit_core::name(), version = conduwuit_core::version())]
|
||||
#[command(name = conduwuit_core::BRANDING, version = conduwuit_core::version())]
|
||||
pub enum AdminCommand {
|
||||
#[command(subcommand)]
|
||||
/// Commands for managing appservices
|
||||
|
||||
+229
-9
@@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
fmt::Write,
|
||||
iter::once,
|
||||
time::{Instant, SystemTime},
|
||||
@@ -22,7 +22,7 @@
|
||||
use lettre::message::Mailbox;
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
|
||||
OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomVersionId,
|
||||
OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomVersionId, UInt,
|
||||
api::federation::event::get_room_state, events::AnyStateEvent, serde::Raw,
|
||||
};
|
||||
use service::rooms::{
|
||||
@@ -69,6 +69,205 @@ pub(super) async fn get_auth_chain(&self, event_id: OwnedEventId) -> Result {
|
||||
self.write_str(&out).await
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
enum NodeStatus {
|
||||
Normal(bool),
|
||||
SoftFailed(bool),
|
||||
Rejected(bool),
|
||||
}
|
||||
|
||||
struct AuthChild {
|
||||
node_id: String,
|
||||
event_id: OwnedEventId,
|
||||
depth: UInt,
|
||||
ts: UInt,
|
||||
first_seen: bool,
|
||||
pdu: Option<PduEvent>,
|
||||
}
|
||||
|
||||
fn render_node(
|
||||
graph: &mut String,
|
||||
node_id: &str,
|
||||
event_id: &EventId,
|
||||
name: &str,
|
||||
status: NodeStatus,
|
||||
) -> Result {
|
||||
let evt_str = event_id.to_string();
|
||||
|
||||
let status_label = match status {
|
||||
| NodeStatus::Normal(false) => format!("{evt_str}: {name}"),
|
||||
| NodeStatus::Normal(true) => format!("{evt_str}: {name} (missing locally)"),
|
||||
| NodeStatus::SoftFailed(false) => format!("{evt_str}: {name} (soft-failed)"),
|
||||
| NodeStatus::SoftFailed(true) =>
|
||||
format!("{evt_str}: {name} (soft-failed & missing locally)"),
|
||||
| NodeStatus::Rejected(false) => format!("{evt_str}: {name} (rejected)"),
|
||||
| NodeStatus::Rejected(true) => format!("{evt_str}: {name} (rejected & missing locally)"),
|
||||
};
|
||||
|
||||
writeln!(graph, "{node_id}[\"{}\"]", status_label.as_str())?;
|
||||
|
||||
match status {
|
||||
| NodeStatus::Rejected(_) => writeln!(graph, "class {node_id} rejected;")?,
|
||||
| NodeStatus::SoftFailed(_) => writeln!(graph, "class {node_id} soft_failed;")?,
|
||||
| NodeStatus::Normal(_) => {},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn show_auth_chain(&self, event_id: OwnedEventId) -> Result {
|
||||
let node_status = async |event_id: &EventId, missing: bool| -> NodeStatus {
|
||||
if self
|
||||
.services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.is_event_rejected(event_id)
|
||||
.await
|
||||
{
|
||||
NodeStatus::Rejected(missing)
|
||||
} else if self
|
||||
.services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.is_event_soft_failed(event_id)
|
||||
.await
|
||||
{
|
||||
NodeStatus::SoftFailed(missing)
|
||||
} else {
|
||||
NodeStatus::Normal(missing)
|
||||
}
|
||||
};
|
||||
|
||||
let Ok(root) = self.services.rooms.timeline.get_pdu(&event_id).await else {
|
||||
return Err!("Event not found.");
|
||||
};
|
||||
|
||||
let mut graph = String::from(
|
||||
"```mermaid\n%% This is a mermaid graph. You can plug this output into\n\
|
||||
%% https://mermaid.live/edit to visualise it on-the-fly.\nflowchart TD\n\
|
||||
classDef rejected fill:#ffe5e5,stroke:#cc0000,stroke-width:2px,color:#000;\n\
|
||||
classDef soft_failed fill:#fff6cc,stroke:#c9a400,stroke-width:2px,color:#000;\n"
|
||||
);
|
||||
|
||||
let mut node_ids: HashMap<OwnedEventId, String> = HashMap::new();
|
||||
let mut cached_events: HashMap<OwnedEventId, PduEvent> =
|
||||
HashMap::from([(event_id.clone(), root.clone())]);
|
||||
let mut scheduled: HashSet<OwnedEventId> = HashSet::from([event_id.clone()]);
|
||||
let mut visited: HashSet<OwnedEventId> = HashSet::new();
|
||||
let mut stack = vec![root];
|
||||
let mut next_node_id = 0_usize;
|
||||
|
||||
let node_id_for = |event_id: &OwnedEventId,
|
||||
node_ids: &mut HashMap<OwnedEventId, String>,
|
||||
next_node_id: &mut usize| {
|
||||
node_ids
|
||||
.entry(event_id.clone())
|
||||
.or_insert_with(|| {
|
||||
let id = format!("n{}", *next_node_id);
|
||||
*next_node_id = next_node_id.saturating_add(1);
|
||||
id
|
||||
})
|
||||
.clone()
|
||||
};
|
||||
let node_name = |e: &PduEvent| {
|
||||
if let Some(state_key) = e.state_key() {
|
||||
format!("{},'{}'", e.event_type(), state_key)
|
||||
} else {
|
||||
format!("{}", e.event_type())
|
||||
}
|
||||
};
|
||||
|
||||
while let Some(event) = stack.pop() {
|
||||
let current_event_id = event.event_id().to_owned();
|
||||
if !visited.insert(current_event_id.clone()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let current_node_id = node_id_for(¤t_event_id, &mut node_ids, &mut next_node_id);
|
||||
let current_status = node_status(¤t_event_id, false).await;
|
||||
|
||||
render_node(
|
||||
&mut graph,
|
||||
¤t_node_id,
|
||||
¤t_event_id,
|
||||
&node_name(&event),
|
||||
current_status,
|
||||
)?;
|
||||
|
||||
let mut children = Vec::with_capacity(event.auth_events.len());
|
||||
for auth_event_id in event.auth_events().rev() {
|
||||
let auth_event_id = auth_event_id.to_owned();
|
||||
let auth_node_id = node_id_for(&auth_event_id, &mut node_ids, &mut next_node_id);
|
||||
writeln!(graph, "{current_node_id} --> {auth_node_id}")?;
|
||||
|
||||
let first_seen = scheduled.insert(auth_event_id.clone());
|
||||
let auth_pdu = if let Some(auth_pdu) = cached_events.get(&auth_event_id) {
|
||||
// NOTE: events might be referenced multiple times (like the create event)
|
||||
// so this saves some cheeky db lookup time
|
||||
Some(auth_pdu.clone())
|
||||
} else if first_seen {
|
||||
match self.services.rooms.timeline.get_pdu(&auth_event_id).await {
|
||||
| Ok(auth_event) => {
|
||||
cached_events.insert(auth_event_id.clone(), auth_event.clone());
|
||||
Some(auth_event)
|
||||
},
|
||||
| Err(_) => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// NOTE: Depth is used as the primary sorting key here, even though it has no
|
||||
// bearing on state resolution or anything. Timestamp is used as a
|
||||
// tiebreaker, failing back to lexicographical comparison.
|
||||
let (depth, ts) = auth_pdu
|
||||
.as_ref()
|
||||
.map_or((UInt::MAX, UInt::MAX), |pdu| (pdu.depth, pdu.origin_server_ts));
|
||||
|
||||
children.push(AuthChild {
|
||||
node_id: auth_node_id,
|
||||
event_id: auth_event_id,
|
||||
depth,
|
||||
ts,
|
||||
first_seen,
|
||||
pdu: auth_pdu,
|
||||
});
|
||||
}
|
||||
|
||||
children.sort_by(|a, b| {
|
||||
a.depth
|
||||
.cmp(&b.depth)
|
||||
.then(a.ts.cmp(&b.ts))
|
||||
.then(a.event_id.as_str().cmp(b.event_id.as_str()))
|
||||
});
|
||||
|
||||
for child in children.into_iter().rev() {
|
||||
if !child.first_seen {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(child_pdu) = child.pdu {
|
||||
// We have this PDU so will want to traverse it.
|
||||
stack.push(child_pdu);
|
||||
} else {
|
||||
// We don't have this PDU locally so we can't traverse its auth events,
|
||||
// but we can still render it as a node.
|
||||
render_node(
|
||||
&mut graph,
|
||||
&child.node_id,
|
||||
&child.event_id,
|
||||
"",
|
||||
node_status(&child.event_id, true).await,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graph.push_str("```\n");
|
||||
self.write_str(&graph).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn parse_pdu(&self) -> Result {
|
||||
if self.body.len() < 2
|
||||
@@ -111,15 +310,31 @@ pub(super) async fn get_pdu(&self, event_id: OwnedEventId) -> Result {
|
||||
outlier = true;
|
||||
pdu_json = self.services.rooms.timeline.get_pdu_json(&event_id).await;
|
||||
}
|
||||
let rejected = self
|
||||
.services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.is_event_rejected(&event_id)
|
||||
.await;
|
||||
let soft_failed = self
|
||||
.services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.is_event_soft_failed(&event_id)
|
||||
.await;
|
||||
|
||||
match pdu_json {
|
||||
| Err(_) => return Err!("PDU not found locally."),
|
||||
| Ok(json) => {
|
||||
let text = serde_json::to_string_pretty(&json)?;
|
||||
let msg = if outlier {
|
||||
"Outlier (Rejected / Soft Failed) PDU found in our database"
|
||||
let msg = if rejected {
|
||||
"Rejected PDU:"
|
||||
} else if soft_failed {
|
||||
"Soft-failed PDU:"
|
||||
} else if outlier {
|
||||
"Outlier PDU:"
|
||||
} else {
|
||||
"PDU found in our database"
|
||||
"PDU:"
|
||||
};
|
||||
write!(self, "{msg}\n```json\n{text}\n```")
|
||||
},
|
||||
@@ -614,6 +829,10 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
.await;
|
||||
|
||||
state.insert(shortstatekey, pdu.event_id.clone());
|
||||
self.services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.clear_pdu_markers(pdu.event_id());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,6 +850,10 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
.rooms
|
||||
.outlier
|
||||
.add_pdu_outlier(&event_id, &value);
|
||||
self.services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.clear_pdu_markers(&event_id);
|
||||
}
|
||||
|
||||
info!("Resolving new room state");
|
||||
@@ -662,10 +885,7 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
.force_state(room_id.clone().as_ref(), short_state_hash, added, removed, &state_lock)
|
||||
.await?;
|
||||
|
||||
info!(
|
||||
"Updating joined counts for room just in case (e.g. we may have found a difference in \
|
||||
the room's m.room.member state"
|
||||
);
|
||||
info!("Updating joined counts for room");
|
||||
self.services
|
||||
.rooms
|
||||
.state_cache
|
||||
|
||||
+10
-1
@@ -17,12 +17,21 @@ pub enum DebugCommand {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
/// Get the auth_chain of a PDU
|
||||
/// Loads the auth_chain of a PDU, reporting how long it took.
|
||||
GetAuthChain {
|
||||
/// An event ID (the $ character followed by the base64 reference hash)
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// Walks & displays the auth_chain of a PDU in a mermaid graph format.
|
||||
///
|
||||
/// This is useless to basically anyone but developers, and is also probably
|
||||
/// slow and memory hungry.
|
||||
ShowAuthChain {
|
||||
/// The root event ID to start walking back from.
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// Parse and print a PDU from a JSON
|
||||
///
|
||||
/// The PDU event is only checked for validity and is not added to the
|
||||
|
||||
@@ -30,14 +30,37 @@ pub(super) async fn issue_token(&self, expires: super::TokenExpires) -> Result {
|
||||
.issue_token(self.sender_or_service_user().into(), expires);
|
||||
|
||||
self.write_str(&format!(
|
||||
"New registration token issued: `{token}`. {}.",
|
||||
"New registration token issued: `{token}` . {}.",
|
||||
if let Some(expires) = info.expires {
|
||||
format!("{expires}")
|
||||
} else {
|
||||
"Never expires".to_owned()
|
||||
}
|
||||
))
|
||||
.await
|
||||
.await?;
|
||||
|
||||
if self
|
||||
.services
|
||||
.config
|
||||
.oauth
|
||||
.compatibility_mode
|
||||
.oauth_available()
|
||||
{
|
||||
self.write_str(&format!(
|
||||
"\nInvite link using this token: {}",
|
||||
self.services
|
||||
.config
|
||||
.get_client_domain()
|
||||
.join(&format!(
|
||||
"{}/account/register/?flow=trusted&token={token}",
|
||||
conduwuit::ROUTE_PREFIX
|
||||
))
|
||||
.unwrap()
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
|
||||
+21
-152
@@ -1,13 +1,10 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
fmt::Write as _,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use api::client::{
|
||||
full_user_deactivate, leave_room, recreate_push_rules_and_return, remote_leave_room,
|
||||
};
|
||||
use conduwuit::{
|
||||
Err, Result, debug_warn, error, info,
|
||||
Err, Result, debug_warn, info,
|
||||
matrix::{Event, pdu::PartialPdu},
|
||||
utils::{self, ReadyExt},
|
||||
warn,
|
||||
@@ -53,130 +50,22 @@ pub(super) async fn list_users(&self) -> Result {
|
||||
#[admin_command]
|
||||
pub(super) async fn create_user(&self, username: String, password: Option<String>) -> Result {
|
||||
// Validate user id
|
||||
let user_id = parse_local_user_id(self.services, &username)?;
|
||||
|
||||
if let Err(e) = user_id.validate_strict() {
|
||||
if self.services.config.emergency_password.is_none() {
|
||||
return Err!("Username {user_id} contains disallowed characters or spaces: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
if self.services.users.exists(&user_id).await {
|
||||
return Err!("User {user_id} already exists");
|
||||
}
|
||||
|
||||
let password = password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH));
|
||||
|
||||
// Create user
|
||||
self.services
|
||||
.users
|
||||
.create(&user_id, Some(HashedPassword::new(&password)?))
|
||||
.await?;
|
||||
|
||||
// Default to pretty displayname
|
||||
let mut displayname = user_id.localpart().to_owned();
|
||||
|
||||
// If `new_user_displayname_suffix` is set, registration will push whatever
|
||||
// content is set to the user's display name with a space before it
|
||||
if !self
|
||||
let user_id = self
|
||||
.services
|
||||
.server
|
||||
.config
|
||||
.new_user_displayname_suffix
|
||||
.is_empty()
|
||||
{
|
||||
write!(displayname, " {}", self.services.server.config.new_user_displayname_suffix)?;
|
||||
}
|
||||
.users
|
||||
.determine_registration_user_id(Some(username), None, None)
|
||||
.await?;
|
||||
|
||||
let password = HashedPassword::new(
|
||||
&password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH)),
|
||||
)?;
|
||||
|
||||
self.services
|
||||
.users
|
||||
.set_displayname(&user_id, Some(displayname));
|
||||
.create_local_account(&user_id, password, None)
|
||||
.await;
|
||||
|
||||
// Initial account data
|
||||
self.services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
&user_id,
|
||||
ruma::events::GlobalAccountDataEventType::PushRules
|
||||
.to_string()
|
||||
.into(),
|
||||
&serde_json::to_value(ruma::events::push_rules::PushRulesEvent::new(
|
||||
ruma::events::push_rules::PushRulesEventContent::new(
|
||||
ruma::push::Ruleset::server_default(&user_id),
|
||||
),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if !self.services.server.config.auto_join_rooms.is_empty() {
|
||||
for room in &self.services.server.config.auto_join_rooms {
|
||||
let Ok(room_id) = self.services.rooms.alias.resolve(room).await else {
|
||||
error!(
|
||||
%user_id,
|
||||
"Failed to resolve room alias to room ID when attempting to auto join {room}, skipping"
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(self.services.globals.server_name(), &room_id)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Skipping room {room} to automatically join as we have never joined before."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(room_server_name) = room.server_name() {
|
||||
match self
|
||||
.services
|
||||
.rooms
|
||||
.membership
|
||||
.join_room(
|
||||
&user_id,
|
||||
&room_id,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
&[
|
||||
self.services.globals.server_name().to_owned(),
|
||||
room_server_name.to_owned(),
|
||||
],
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(_response) => {
|
||||
info!("Automatically joined room {room} for user {user_id}");
|
||||
},
|
||||
| Err(e) => {
|
||||
// don't return this error so we don't fail registrations
|
||||
error!(
|
||||
"Failed to automatically join room {room} for user {user_id}: {e}"
|
||||
);
|
||||
self.services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"Failed to automatically join room {room} for user {user_id}: \
|
||||
{e}"
|
||||
))
|
||||
.await;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we dont add a device since we're not the user, just the creator
|
||||
|
||||
// Make the first user to register an administrator and disable first-run mode.
|
||||
self.services.firstrun.empower_first_user(&user_id).await?;
|
||||
|
||||
self.write_str(&format!("Created user with user_id: {user_id} and password: `{password}`"))
|
||||
.await
|
||||
self.write_str(&format!("Created user {user_id}")).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
@@ -233,7 +122,7 @@ pub(super) async fn suspend(&self, user_id: String) -> Result {
|
||||
// TODO: Record the actual user that sent the suspension where possible
|
||||
self.services
|
||||
.users
|
||||
.suspend_account(&user_id, self.sender_or_service_user())
|
||||
.suspend_account(&user_id, self.sender)
|
||||
.await;
|
||||
|
||||
self.write_str(&format!("User {user_id} has been suspended."))
|
||||
@@ -302,31 +191,6 @@ pub(super) async fn reset_password(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn issue_password_reset_link(&self, username: String) -> Result {
|
||||
use conduwuit_service::password_reset::{PASSWORD_RESET_PATH, RESET_TOKEN_QUERY_PARAM};
|
||||
|
||||
self.bail_restricted()?;
|
||||
|
||||
let mut reset_url = self
|
||||
.services
|
||||
.config
|
||||
.get_client_domain()
|
||||
.join(PASSWORD_RESET_PATH)
|
||||
.unwrap();
|
||||
|
||||
let user_id = parse_local_user_id(self.services, &username)?;
|
||||
let token = self.services.password_reset.issue_token(user_id).await?;
|
||||
reset_url
|
||||
.query_pairs_mut()
|
||||
.append_pair(RESET_TOKEN_QUERY_PARAM, &token.token);
|
||||
|
||||
self.write_str(&format!("Password reset link issued for {username}: {reset_url}"))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) -> Result {
|
||||
if self.body.len() < 2
|
||||
@@ -740,14 +604,19 @@ pub(super) async fn force_join_room(
|
||||
&self,
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
via: Option<String>,
|
||||
) -> Result {
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let (room_id, servers) = self
|
||||
let (room_id, mut servers) = self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_with_servers(&room_id, None)
|
||||
.await?;
|
||||
if let Some(via) = via.map(ServerName::parse).transpose()? {
|
||||
servers.retain(|n| *n != via);
|
||||
servers.insert(0, via);
|
||||
}
|
||||
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
@@ -1070,7 +939,7 @@ pub(super) async fn lock(&self, user_id: String) -> Result {
|
||||
}
|
||||
self.services
|
||||
.users
|
||||
.lock_account(&user_id, self.sender_or_service_user())
|
||||
.lock_account(&user_id, self.sender)
|
||||
.await;
|
||||
|
||||
self.write_str(&format!("User {user_id} has been locked."))
|
||||
|
||||
@@ -29,12 +29,6 @@ pub enum UserCommand {
|
||||
password: Option<String>,
|
||||
},
|
||||
|
||||
/// Issue a self-service password reset link for a user.
|
||||
IssuePasswordResetLink {
|
||||
/// Username of the user who may use the link
|
||||
username: String,
|
||||
},
|
||||
|
||||
/// Get a user's associated email address.
|
||||
GetEmail {
|
||||
user_id: String,
|
||||
@@ -179,8 +173,15 @@ pub enum UserCommand {
|
||||
|
||||
/// Manually join a local user to a room.
|
||||
ForceJoinRoom {
|
||||
/// The user to join
|
||||
user_id: String,
|
||||
/// The room to join
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
/// The server name to join via.
|
||||
///
|
||||
/// This server will always be tried first, however if more are
|
||||
/// available, they may be tried after.
|
||||
via: Option<String>,
|
||||
},
|
||||
|
||||
/// Manually leave a local user from a room.
|
||||
|
||||
@@ -62,6 +62,8 @@ zstd_compression = [
|
||||
"reqwest/zstd",
|
||||
]
|
||||
|
||||
admin_api = []
|
||||
|
||||
[dependencies]
|
||||
async-trait.workspace = true
|
||||
axum-client-ip.workspace = true
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub mod rooms;
|
||||
@@ -1,36 +0,0 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result};
|
||||
use futures::StreamExt;
|
||||
use ruma::OwnedRoomId;
|
||||
use ruminuwuity::admin::continuwuity::rooms;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_continuwuity/admin/rooms/list`
|
||||
///
|
||||
/// Lists all rooms known to this server, excluding banned ones.
|
||||
pub(crate) async fn list_rooms(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<rooms::list::v1::Request>,
|
||||
) -> Result<rooms::list::v1::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
if !services.users.is_admin(sender_user).await {
|
||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||
}
|
||||
|
||||
let mut rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.filter_map(|room_id| async move {
|
||||
if !services.rooms.metadata.is_banned(&room_id).await {
|
||||
Some(room_id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
rooms.sort();
|
||||
Ok(rooms::list::v1::Response::new(rooms))
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod ban;
|
||||
pub mod list;
|
||||
@@ -24,10 +24,10 @@
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
},
|
||||
};
|
||||
use service::{mailer::messages, uiaa::Identity, users::HashedPassword};
|
||||
use service::{mailer::messages, uiaa::UiaaInitiator, users::HashedPassword};
|
||||
|
||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||
use crate::Ruma;
|
||||
use crate::{Ruma, router::ClientIdentity};
|
||||
|
||||
pub(crate) mod register;
|
||||
pub(crate) mod threepid;
|
||||
@@ -49,41 +49,16 @@ pub(crate) async fn get_register_available_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_username_availability::v3::Request>,
|
||||
) -> Result<get_username_availability::v3::Response> {
|
||||
// Validate user id
|
||||
let user_id =
|
||||
match UserId::parse_with_server_name(&body.username, services.globals.server_name()) {
|
||||
| Ok(user_id) => {
|
||||
if let Err(e) = user_id.validate_strict() {
|
||||
return Err!(Request(InvalidUsername(debug_warn!(
|
||||
"Username {} contains disallowed characters or spaces: {e}",
|
||||
body.username
|
||||
))));
|
||||
}
|
||||
|
||||
user_id
|
||||
},
|
||||
| Err(e) => {
|
||||
return Err!(Request(InvalidUsername(debug_warn!(
|
||||
"Username {} is not valid: {e}",
|
||||
body.username
|
||||
))));
|
||||
},
|
||||
};
|
||||
|
||||
// Check if username is creative enough
|
||||
if services.users.exists(&user_id).await {
|
||||
return Err!(Request(UserInUse("User ID is not available.")));
|
||||
}
|
||||
|
||||
if let Some(ref info) = body.appservice_info {
|
||||
if !info.is_user_match(&user_id) {
|
||||
return Err!(Request(Exclusive("Username is not in an appservice namespace.")));
|
||||
}
|
||||
}
|
||||
|
||||
if services.appservice.is_exclusive_user_id(&user_id).await {
|
||||
return Err!(Request(Exclusive("Username is reserved by an appservice.")));
|
||||
}
|
||||
let _ = services
|
||||
.users
|
||||
.determine_registration_user_id(
|
||||
Some(body.username.clone()),
|
||||
None,
|
||||
body.identity
|
||||
.as_ref()
|
||||
.and_then(ClientIdentity::appservice_info),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(get_username_availability::v3::Response::new(true))
|
||||
}
|
||||
@@ -111,7 +86,7 @@ pub(crate) async fn change_password_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<change_password::v3::Request>,
|
||||
) -> Result<change_password::v3::Response> {
|
||||
let identity = if let Some(ref user_id) = body.sender_user {
|
||||
let identity = if let Some(identity) = body.identity.as_ref() {
|
||||
// A signed-in user is trying to change their password, prompt them for their
|
||||
// existing one
|
||||
|
||||
@@ -121,7 +96,10 @@ pub(crate) async fn change_password_route(
|
||||
&body.auth,
|
||||
vec![AuthFlow::new(vec![AuthType::Password])],
|
||||
Box::default(),
|
||||
Some(Identity::from_user_id(user_id)),
|
||||
Some(UiaaInitiator::new(
|
||||
identity.expect_sender_user()?,
|
||||
identity.sender_device(),
|
||||
)),
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
@@ -157,7 +135,12 @@ pub(crate) async fn change_password_route(
|
||||
services
|
||||
.users
|
||||
.all_device_ids(&sender_user)
|
||||
.ready_filter(|id| *id != body.sender_device())
|
||||
.ready_filter(|id| {
|
||||
body.identity
|
||||
.as_ref()
|
||||
.and_then(|identity| identity.sender_device())
|
||||
.is_none_or(|sender_device| sender_device != *id)
|
||||
})
|
||||
.for_each(async |id| services.users.remove_device(&sender_user, &id).await)
|
||||
.await;
|
||||
|
||||
@@ -173,7 +156,12 @@ pub(crate) async fn change_password_route(
|
||||
.await
|
||||
.ok()
|
||||
.as_ref()
|
||||
.is_some_and(|pusher_device| pusher_device != body.sender_device())
|
||||
.is_some_and(|pusher_device| {
|
||||
body.identity
|
||||
.as_ref()
|
||||
.and_then(|identity| identity.sender_device())
|
||||
.is_none_or(|sender_device| sender_device != *pusher_device)
|
||||
})
|
||||
.then_some(pushkey)
|
||||
})
|
||||
.for_each(async |pushkey| {
|
||||
@@ -187,7 +175,7 @@ pub(crate) async fn change_password_route(
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.notice(&format!("User {} changed their password.", &sender_user))
|
||||
.notice(&format!("User {sender_user} changed their password."))
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -241,9 +229,11 @@ pub(crate) async fn whoami_route(
|
||||
State(_): State<crate::State>,
|
||||
body: Ruma<whoami::v3::Request>,
|
||||
) -> Result<whoami::v3::Response> {
|
||||
Ok(assign!(whoami::v3::Response::new(body.sender_user().to_owned(), false), {
|
||||
device_id: body.sender_device,
|
||||
}))
|
||||
Ok(
|
||||
assign!(whoami::v3::Response::new(body.identity.expect_sender_user()?.to_owned(), false), {
|
||||
device_id: body.identity.sender_device().map(ToOwned::to_owned),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/account/deactivate`
|
||||
@@ -265,15 +255,24 @@ pub(crate) async fn deactivate_route(
|
||||
) -> Result<deactivate::v3::Response> {
|
||||
// Authentication for this endpoint is technically optional,
|
||||
// but we require the user to be logged in
|
||||
let sender_user = body
|
||||
.sender_user
|
||||
let identity = body
|
||||
.identity
|
||||
.as_ref()
|
||||
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
|
||||
|
||||
let sender_user = identity.expect_sender_user()?;
|
||||
|
||||
if !services.config.allow_deactivation {
|
||||
return Err!(Request(Unauthorized(
|
||||
"You may not deactivate your own account. Contact your server's administrator for \
|
||||
assistance."
|
||||
)));
|
||||
}
|
||||
|
||||
// Prompt the user to confirm with their password using UIAA
|
||||
let _ = services
|
||||
.uiaa
|
||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
||||
.authenticate_password(&body.auth, sender_user, identity.sender_device(), None)
|
||||
.await?;
|
||||
|
||||
// Remove profile pictures and display name
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
use std::{collections::HashMap, fmt::Write};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::ClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug_info, error, info,
|
||||
Err, Result, debug_info, info,
|
||||
utils::{self},
|
||||
warn,
|
||||
};
|
||||
use conduwuit_service::Services;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use futures::StreamExt;
|
||||
use lettre::{Address, message::Mailbox};
|
||||
use ruma::{
|
||||
OwnedUserId, UserId,
|
||||
api::client::{
|
||||
account::{
|
||||
register::{self, LoginType, RegistrationKind},
|
||||
@@ -20,11 +18,6 @@
|
||||
uiaa::{AuthFlow, AuthType},
|
||||
},
|
||||
assign,
|
||||
events::{
|
||||
GlobalAccountDataEventType, push_rules::PushRulesEvent,
|
||||
room::message::RoomMessageEventContent,
|
||||
},
|
||||
push,
|
||||
};
|
||||
use serde_json::value::RawValue;
|
||||
use service::{mailer::messages, users::HashedPassword};
|
||||
@@ -32,8 +25,6 @@
|
||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||
use crate::Ruma;
|
||||
|
||||
const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||
|
||||
/// # `POST /_matrix/client/v3/register`
|
||||
///
|
||||
/// Register an account on this homeserver.
|
||||
@@ -52,14 +43,12 @@ pub(crate) async fn register_route(
|
||||
return Err!(Request(GuestAccessForbidden("Guests may not register on this server.")));
|
||||
}
|
||||
|
||||
let emergency_mode_enabled = services.config.emergency_password.is_some();
|
||||
|
||||
// Allow registration if it's enabled in the config file or if this is the first
|
||||
// run (so the first user account can be created)
|
||||
let allow_registration =
|
||||
services.config.allow_registration || services.firstrun.is_first_run();
|
||||
|
||||
if !allow_registration && body.appservice_info.is_none() {
|
||||
if !allow_registration && body.identity.is_none() {
|
||||
info!(
|
||||
?body.username,
|
||||
?body.initial_device_display_name,
|
||||
@@ -71,101 +60,59 @@ pub(crate) async fn register_route(
|
||||
)));
|
||||
}
|
||||
|
||||
let identity = if body.appservice_info.is_some() {
|
||||
// Appservices can skip auth
|
||||
None
|
||||
let user_id = if body.body.login_type == Some(LoginType::ApplicationService) {
|
||||
let Some(appservice_info) = &body.identity else {
|
||||
return Err!(Request(Forbidden(
|
||||
"Only appservices can use the appservice login type."
|
||||
)));
|
||||
};
|
||||
|
||||
let user_id = services
|
||||
.users
|
||||
.determine_registration_user_id(body.username.clone(), None, Some(appservice_info))
|
||||
.await?;
|
||||
|
||||
services.users.create(&user_id, None).await?;
|
||||
|
||||
user_id
|
||||
} else {
|
||||
// Perform UIAA to determine the user's identity
|
||||
let (flows, params) = create_registration_uiaa_session(&services).await?;
|
||||
|
||||
Some(
|
||||
services
|
||||
.uiaa
|
||||
.authenticate(&body.auth, flows, params, None)
|
||||
.await?,
|
||||
)
|
||||
};
|
||||
|
||||
// If the user didn't supply a username but did supply an email, use
|
||||
// the email's user as their initial localpart to avoid falling back to
|
||||
// a randomly generated localpart
|
||||
let supplied_username = body.username.clone().or_else(|| {
|
||||
if let Some(identity) = &identity
|
||||
&& let Some(email) = &identity.email
|
||||
{
|
||||
Some(email.user().to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let user_id =
|
||||
determine_registration_user_id(&services, supplied_username, emergency_mode_enabled)
|
||||
let identity = services
|
||||
.uiaa
|
||||
.authenticate(&body.auth, flows, params, None)
|
||||
.await?;
|
||||
|
||||
if body.body.login_type == Some(LoginType::ApplicationService) {
|
||||
// For appservice logins, make sure that the user ID is in the appservice's
|
||||
// namespace
|
||||
let password = if let Some(password) = &body.password {
|
||||
HashedPassword::new(password)?
|
||||
} else {
|
||||
return Err!(Request(InvalidParam("A password must be provided.")));
|
||||
};
|
||||
|
||||
match body.appservice_info {
|
||||
| Some(ref info) =>
|
||||
if !info.is_user_match(&user_id) && !emergency_mode_enabled {
|
||||
return Err!(Request(Exclusive(
|
||||
"Username is not in an appservice namespace."
|
||||
)));
|
||||
},
|
||||
| _ => {
|
||||
return Err!(Request(MissingToken("Missing appservice token.")));
|
||||
},
|
||||
}
|
||||
} else if services.appservice.is_exclusive_user_id(&user_id).await && !emergency_mode_enabled
|
||||
{
|
||||
// For non-appservice logins, ban user IDs which are in an appservice's
|
||||
// namespace (unless emergency mode is enabled)
|
||||
return Err!(Request(Exclusive("Username is reserved by an appservice.")));
|
||||
}
|
||||
let user_id = services
|
||||
.users
|
||||
.determine_registration_user_id(body.username.clone(), identity.email.as_ref(), None)
|
||||
.await?;
|
||||
|
||||
let password = if body.appservice_info.is_some() {
|
||||
None
|
||||
} else if let Some(password) = body.password.as_deref() {
|
||||
Some(HashedPassword::new(password)?)
|
||||
} else {
|
||||
return Err!(Request(InvalidParam("A password must be provided")));
|
||||
services
|
||||
.users
|
||||
.create_local_account(&user_id, password, identity.email)
|
||||
.await;
|
||||
services.users.join_auto_join_rooms(&user_id).await;
|
||||
user_id
|
||||
};
|
||||
|
||||
// Create user
|
||||
services.users.create(&user_id, password).await?;
|
||||
|
||||
// Set an initial display name
|
||||
let mut displayname = user_id.localpart().to_owned();
|
||||
|
||||
// Apply the new user displayname suffix, if it's set
|
||||
if !services.globals.new_user_displayname_suffix().is_empty()
|
||||
&& body.appservice_info.is_none()
|
||||
{
|
||||
write!(displayname, " {}", services.server.config.new_user_displayname_suffix)?;
|
||||
}
|
||||
|
||||
services
|
||||
.users
|
||||
.set_displayname(&user_id, Some(displayname.clone()));
|
||||
|
||||
// Initial account data
|
||||
services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
&user_id,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(PushRulesEvent::new(
|
||||
push::Ruleset::server_default(&user_id).into(),
|
||||
))
|
||||
.expect("should be able to serialize push rules"),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Generate new device id if the user didn't specify one
|
||||
let (token, device) = if !body.inhibit_login {
|
||||
// If UIAA is disabled, we can't create a device. In that case only appservices
|
||||
// can reach this point in the first place, so we return an error for them.
|
||||
if !services.config.oauth.compatibility_mode.uiaa_available() {
|
||||
return Err!(Request(AppserviceLoginUnsupported(
|
||||
"User-interactive appservice registration is not available on this server."
|
||||
)));
|
||||
}
|
||||
|
||||
// Generate new device id if the user didn't specify one
|
||||
let device_id = body
|
||||
.device_id
|
||||
.clone()
|
||||
@@ -181,6 +128,7 @@ pub(crate) async fn register_route(
|
||||
&user_id,
|
||||
&device_id,
|
||||
&new_token,
|
||||
None,
|
||||
body.initial_device_display_name.clone(),
|
||||
Some(client.to_string()),
|
||||
)
|
||||
@@ -191,118 +139,7 @@ pub(crate) async fn register_route(
|
||||
(None, None)
|
||||
};
|
||||
|
||||
debug_info!(%user_id, ?device, "User account was created");
|
||||
|
||||
// If the user registered with an email, associate it with their account.
|
||||
if let Some(identity) = identity
|
||||
&& let Some(email) = identity.email
|
||||
{
|
||||
// This may fail if the email is already in use, but we already check for that
|
||||
// in `/requestToken`, so ignoring the error is acceptable here in the rare case
|
||||
// that an email is sniped by another user between the `/requestToken` request
|
||||
// and the `/register` request.
|
||||
let _ = services
|
||||
.threepid
|
||||
.associate_localpart_email(user_id.localpart(), &email)
|
||||
.await;
|
||||
}
|
||||
|
||||
let device_display_name = body.initial_device_display_name.as_deref().unwrap_or("");
|
||||
|
||||
if body.appservice_info.is_none() {
|
||||
if !device_display_name.is_empty() {
|
||||
let notice = format!(
|
||||
"New user \"{user_id}\" registered on this server from IP {client} and device \
|
||||
display name \"{device_display_name}\""
|
||||
);
|
||||
|
||||
info!("{notice}");
|
||||
if services.server.config.admin_room_notices {
|
||||
services.admin.notice(¬ice).await;
|
||||
}
|
||||
} else {
|
||||
let notice = format!("New user \"{user_id}\" registered on this server.");
|
||||
|
||||
info!("{notice}");
|
||||
if services.server.config.admin_room_notices {
|
||||
services.admin.notice(¬ice).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make the first user to register an administrator and disable first-run mode.
|
||||
let was_first_user = services.firstrun.empower_first_user(&user_id).await?;
|
||||
|
||||
// If the registering user was not the first and we're suspending users on
|
||||
// register, suspend them.
|
||||
if !was_first_user && services.config.suspend_on_register {
|
||||
// Note that we can still do auto joins for suspended users
|
||||
services
|
||||
.users
|
||||
.suspend_account(&user_id, &services.globals.server_user)
|
||||
.await;
|
||||
// And send an @room notice to the admin room, to prompt admins to review the
|
||||
// new user and ideally unsuspend them if deemed appropriate.
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_loud_message(RoomMessageEventContent::text_plain(format!(
|
||||
"User {user_id} has been suspended as they are not the first user on this \
|
||||
server. Please review and unsuspend them if appropriate."
|
||||
)))
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
if body.appservice_info.is_none() && !services.server.config.auto_join_rooms.is_empty() {
|
||||
for room in &services.server.config.auto_join_rooms {
|
||||
let Ok(room_id) = services.rooms.alias.resolve(room).await else {
|
||||
error!(
|
||||
"Failed to resolve room alias to room ID when attempting to auto join \
|
||||
{room}, skipping"
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), &room_id)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Skipping room {room} to automatically join as we have never joined before."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(room_server_name) = room.server_name() {
|
||||
match services
|
||||
.rooms
|
||||
.membership
|
||||
.join_room(
|
||||
&user_id,
|
||||
&room_id,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
&[services.globals.server_name().to_owned(), room_server_name.to_owned()],
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
| Err(e) => {
|
||||
// don't return this error so we don't fail registrations
|
||||
error!(
|
||||
"Failed to automatically join room {room} for user {user_id}: {e}"
|
||||
);
|
||||
},
|
||||
| _ => {
|
||||
info!("Automatically joined room {room} for user {user_id}");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
debug_info!(%user_id, ?device, "New account created via legacy registration");
|
||||
|
||||
Ok(assign!(register::v3::Response::new(user_id), {
|
||||
access_token: token,
|
||||
@@ -374,21 +211,21 @@ async fn create_registration_uiaa_session(
|
||||
|
||||
// Require all users to agree to the terms and conditions, if configured
|
||||
let terms = &services.config.registration_terms;
|
||||
if !terms.is_empty() {
|
||||
let mut terms =
|
||||
serde_json::to_value(terms.clone()).expect("failed to serialize terms");
|
||||
if !terms.documents.is_empty() {
|
||||
let mut terms_map = HashMap::new();
|
||||
|
||||
// Insert a dummy `version` field
|
||||
for (_, documents) in terms.as_object_mut().unwrap() {
|
||||
let documents = documents.as_object_mut().unwrap();
|
||||
|
||||
documents.insert("version".to_owned(), "latest".into());
|
||||
for (id, document) in &terms.documents {
|
||||
terms_map.insert(id.to_owned(), serde_json::json!({
|
||||
terms.language.clone(): serde_json::to_value(document).expect("should be able to serialize document")
|
||||
}));
|
||||
}
|
||||
|
||||
terms_map.insert("version".to_owned(), "latest".into());
|
||||
|
||||
params.insert(
|
||||
AuthType::Terms.as_str().to_owned(),
|
||||
serde_json::json!({
|
||||
"policies": terms,
|
||||
"policies": terms_map,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -421,81 +258,6 @@ async fn create_registration_uiaa_session(
|
||||
Ok((flows, params))
|
||||
}
|
||||
|
||||
async fn determine_registration_user_id(
|
||||
services: &Services,
|
||||
supplied_username: Option<String>,
|
||||
emergency_mode_enabled: bool,
|
||||
) -> Result<OwnedUserId> {
|
||||
if let Some(supplied_username) = supplied_username {
|
||||
// The user gets to pick their username. Do some validation to make sure it's
|
||||
// acceptable.
|
||||
|
||||
// Don't allow registration with forbidden usernames.
|
||||
if services
|
||||
.globals
|
||||
.forbidden_usernames()
|
||||
.is_match(&supplied_username)
|
||||
&& !emergency_mode_enabled
|
||||
{
|
||||
return Err!(Request(Forbidden("Username is forbidden")));
|
||||
}
|
||||
|
||||
// Create and validate the user ID
|
||||
let user_id = match UserId::parse_with_server_name(
|
||||
&supplied_username,
|
||||
services.globals.server_name(),
|
||||
) {
|
||||
| Ok(user_id) => {
|
||||
if let Err(e) = user_id.validate_strict() {
|
||||
// Unless we are in emergency mode, we should follow synapse's behaviour on
|
||||
// not allowing things like spaces and UTF-8 characters in usernames
|
||||
if !emergency_mode_enabled {
|
||||
return Err!(Request(InvalidUsername(debug_warn!(
|
||||
"Username {supplied_username} contains disallowed characters or \
|
||||
spaces: {e}"
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
// Don't allow registration with user IDs that aren't local
|
||||
if !services.globals.user_is_local(&user_id) {
|
||||
return Err!(Request(InvalidUsername(
|
||||
"Username {supplied_username} is not local to this server"
|
||||
)));
|
||||
}
|
||||
|
||||
user_id
|
||||
},
|
||||
| Err(e) => {
|
||||
return Err!(Request(InvalidUsername(debug_warn!(
|
||||
"Username {supplied_username} is not valid: {e}"
|
||||
))));
|
||||
},
|
||||
};
|
||||
|
||||
if services.users.exists(&user_id).await {
|
||||
return Err!(Request(UserInUse("User ID is not available.")));
|
||||
}
|
||||
|
||||
Ok(user_id)
|
||||
} else {
|
||||
// The user didn't specify a username. Generate a username for
|
||||
// them.
|
||||
|
||||
loop {
|
||||
let user_id = UserId::parse_with_server_name(
|
||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
||||
services.globals.server_name(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if !services.users.exists(&user_id).await {
|
||||
break Ok(user_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/v3/register/email/requestToken`
|
||||
///
|
||||
/// Requests a validation email for the purpose of registering a new account.
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
},
|
||||
thirdparty::{Medium, ThirdPartyIdentifierInit},
|
||||
};
|
||||
use service::{mailer::messages, uiaa::Identity};
|
||||
use service::mailer::messages;
|
||||
|
||||
use crate::Ruma;
|
||||
use crate::{Ruma, router::ClientIdentity};
|
||||
|
||||
/// # `GET _matrix/client/v3/account/3pid`
|
||||
///
|
||||
@@ -22,7 +22,7 @@ pub(crate) async fn third_party_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_3pids::v3::Request>,
|
||||
) -> Result<get_3pids::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let mut threepids = vec![];
|
||||
|
||||
if let Some(email) = services
|
||||
@@ -53,6 +53,14 @@ pub(crate) async fn request_3pid_management_token_via_email_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<request_3pid_management_token_via_email::v3::Request>,
|
||||
) -> Result<request_3pid_management_token_via_email::v3::Response> {
|
||||
// Authentication for this endpoint is technically optional,
|
||||
// but we require the user to be logged in
|
||||
let sender_user = body
|
||||
.identity
|
||||
.as_ref()
|
||||
.map(ClientIdentity::expect_sender_user)
|
||||
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))??;
|
||||
|
||||
if !services.threepid.email_requirement().may_change() {
|
||||
return Err!(Request(Forbidden("You may not change your email address.")));
|
||||
}
|
||||
@@ -76,7 +84,7 @@ pub(crate) async fn request_3pid_management_token_via_email_route(
|
||||
Mailbox::new(None, email),
|
||||
|verification_link| messages::ChangeEmail {
|
||||
server_name: services.config.server_name.as_str(),
|
||||
user_id: body.sender_user.as_deref(),
|
||||
user_id: Some(sender_user),
|
||||
verification_link,
|
||||
},
|
||||
&body.client_secret,
|
||||
@@ -107,8 +115,6 @@ pub(crate) async fn add_3pid_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<add_3pid::v3::Request>,
|
||||
) -> Result<add_3pid::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if !services.threepid.email_requirement().may_change() {
|
||||
return Err!(Request(Forbidden("You may not change your email address.")));
|
||||
}
|
||||
@@ -116,18 +122,24 @@ pub(crate) async fn add_3pid_route(
|
||||
// Require password auth to add an email
|
||||
let _ = services
|
||||
.uiaa
|
||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
||||
.authenticate_password(
|
||||
&body.auth,
|
||||
body.identity.expect_sender_user()?,
|
||||
body.identity.sender_device(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let email = services
|
||||
.threepid
|
||||
.consume_valid_session(&body.sid, &body.client_secret)
|
||||
.get_valid_session(&body.sid, &body.client_secret)
|
||||
.await
|
||||
.map_err(|message| err!(Request(ThreepidAuthFailed("{message}"))))?;
|
||||
.map_err(|message| err!(Request(ThreepidAuthFailed("{message}"))))?
|
||||
.consume();
|
||||
|
||||
services
|
||||
.threepid
|
||||
.associate_localpart_email(sender_user.localpart(), &email)
|
||||
.associate_localpart_email(body.identity.expect_sender_user()?.localpart(), &email)
|
||||
.await?;
|
||||
|
||||
Ok(add_3pid::v3::Response::new())
|
||||
@@ -138,8 +150,6 @@ pub(crate) async fn delete_3pid_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_3pid::v3::Request>,
|
||||
) -> Result<delete_3pid::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if body.medium != Medium::Email {
|
||||
return Ok(delete_3pid::v3::Response::new(ThirdPartyIdRemovalStatus::NoSupport));
|
||||
}
|
||||
@@ -150,7 +160,7 @@ pub(crate) async fn delete_3pid_route(
|
||||
|
||||
if services
|
||||
.threepid
|
||||
.disassociate_localpart_email(sender_user.localpart())
|
||||
.disassociate_localpart_email(body.identity.expect_sender_user()?.localpart())
|
||||
.await
|
||||
.is_none()
|
||||
{
|
||||
|
||||
@@ -22,9 +22,9 @@ pub(crate) async fn set_global_account_data_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_global_account_data::v3::Request>,
|
||||
) -> Result<set_global_account_data::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if sender_user != body.user_id && body.appservice_info.is_none() {
|
||||
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||
return Err!(Request(Forbidden("You cannot set account data for other users.")));
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ pub(crate) async fn set_room_account_data_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_room_account_data::v3::Request>,
|
||||
) -> Result<set_room_account_data::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if sender_user != body.user_id && body.appservice_info.is_none() {
|
||||
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||
return Err!(Request(Forbidden("You cannot set account data for other users.")));
|
||||
}
|
||||
|
||||
@@ -72,9 +72,9 @@ pub(crate) async fn get_global_account_data_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_global_account_data::v3::Request>,
|
||||
) -> Result<get_global_account_data::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if sender_user != body.user_id && body.appservice_info.is_none() {
|
||||
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||
return Err!(Request(Forbidden("You cannot get account data of other users.")));
|
||||
}
|
||||
|
||||
@@ -94,9 +94,9 @@ pub(crate) async fn get_room_account_data_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_room_account_data::v3::Request>,
|
||||
) -> Result<get_room_account_data::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if sender_user != body.user_id && body.appservice_info.is_none() {
|
||||
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||
return Err!(Request(Forbidden("You cannot get account data of other users.")));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::Err;
|
||||
use ruma::api::client::admin::{is_user_locked, lock_user};
|
||||
|
||||
use crate::router::Ruma;
|
||||
|
||||
/// # `GET /_matrix/client/v1/admin/lock/{userId}`
|
||||
///
|
||||
/// Check the account lock status of a target user
|
||||
pub(crate) async fn get_locked_status(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<is_user_locked::v1::Request>,
|
||||
) -> conduwuit::Result<is_user_locked::v1::Response> {
|
||||
if !services.users.is_active_local(&body.user_id).await {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Can only check the lock status of active local users"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(is_user_locked::v1::Response::new(
|
||||
services.users.is_locked(&body.user_id).await?,
|
||||
))
|
||||
}
|
||||
|
||||
/// # `PUT /_matrix/client/v1/admin/lock/{userId}`
|
||||
///
|
||||
/// Set the account lock status of a target user
|
||||
pub(crate) async fn put_locked_status(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<lock_user::v1::Request>,
|
||||
) -> conduwuit::Result<lock_user::v1::Response> {
|
||||
if !services.users.is_active_local(&body.user_id).await {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Can only set the locked status of active local users"
|
||||
)));
|
||||
}
|
||||
|
||||
if body.identity.sender_user() == Some(&body.user_id) {
|
||||
return Err!(Request(Forbidden("You cannot lock yourself")));
|
||||
}
|
||||
|
||||
if services.users.is_admin(&body.user_id).await {
|
||||
return Err!(Request(Forbidden("You cannot lock another server administrator")));
|
||||
}
|
||||
|
||||
if services.users.is_locked(&body.user_id).await? == body.locked {
|
||||
// No change
|
||||
return Ok(lock_user::v1::Response::new(body.locked));
|
||||
}
|
||||
|
||||
let action = if body.locked {
|
||||
services
|
||||
.users
|
||||
.lock_account(&body.user_id, body.identity.sender_user())
|
||||
.await;
|
||||
"suspended"
|
||||
} else {
|
||||
services.users.unlock_account(&body.user_id).await;
|
||||
"unsuspended"
|
||||
};
|
||||
|
||||
if services.config.admin_room_notices {
|
||||
// Notify the admin room that an account has been un/suspended
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!("{} has been {} by {}.", body.user_id, action, body.identity))
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(lock_user::v1::Response::new(body.locked))
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
mod lock;
|
||||
pub(crate) mod site;
|
||||
mod suspend;
|
||||
|
||||
pub(crate) use self::suspend::*;
|
||||
pub(crate) use self::{lock::*, suspend::*};
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub(crate) mod rooms;
|
||||
pub(crate) mod users;
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
use crate::{Ruma, client::leave_room};
|
||||
|
||||
/// # `PUT /_continuwuity/admin/rooms/{roomID}/ban`
|
||||
/// # `PUT /_continuwuity/admin/v1/rooms/{roomID}/ban`
|
||||
///
|
||||
/// Bans or unbans a room.
|
||||
pub(crate) async fn ban_room(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<rooms::ban::v1::Request>,
|
||||
) -> Result<rooms::ban::v1::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
if !services.users.is_admin(sender_user).await {
|
||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Event, Result,
|
||||
utils::stream::{BroadbandExt, WidebandExt},
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
OwnedRoomId,
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
create::RoomCreateEventContent,
|
||||
encryption::PossiblyRedactedRoomEncryptionEventContent,
|
||||
tombstone::PossiblyRedactedRoomTombstoneEventContent,
|
||||
},
|
||||
},
|
||||
};
|
||||
use ruminuwuity::admin::continuwuity::rooms;
|
||||
use tokio::join;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_continuwuity/admin/rooms`
|
||||
///
|
||||
/// Lists all room IDs known to this server, excluding banned ones.
|
||||
///
|
||||
/// This is the legacy version of the endpoint, which does not support
|
||||
/// pagination or including banned rooms. It is recommended to use the
|
||||
/// `/v1/rooms` endpoint instead. This endpoint may be removed in a future
|
||||
/// release.
|
||||
pub(crate) async fn legacy_list_rooms_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<rooms::list::unstable::Request>,
|
||||
) -> Result<rooms::list::unstable::Response> {
|
||||
let mut rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.filter_map(|room_id| async move {
|
||||
if !services.rooms.metadata.is_banned(&room_id).await {
|
||||
Some(room_id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
rooms.sort();
|
||||
Ok(rooms::list::unstable::Response::new(rooms))
|
||||
}
|
||||
|
||||
/// # `GET /_continuwuity/admin/v1/rooms`
|
||||
///
|
||||
/// Lists rooms known to this server.
|
||||
pub(crate) async fn list_rooms_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<rooms::list::v1::Request>,
|
||||
) -> Result<rooms::list::v1::Response> {
|
||||
let include_banned_rooms = body.include_banned_rooms;
|
||||
let rooms = services
|
||||
.rooms
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.wide_filter_map(|room_id| async move {
|
||||
if include_banned_rooms || !services.rooms.metadata.is_banned(&room_id).await {
|
||||
Some(room_id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.skip(body.offset.unwrap_or_default())
|
||||
.take(body.limit.unwrap_or(100).min(100))
|
||||
.broad_filter_map(|room_id| async move {
|
||||
let (
|
||||
banned,
|
||||
disabled,
|
||||
member_count,
|
||||
local_member_count,
|
||||
resident_server_count,
|
||||
published,
|
||||
create_event,
|
||||
encryption_event,
|
||||
name_event,
|
||||
topic_event,
|
||||
canonical_alias_event,
|
||||
join_rules_event,
|
||||
history_visibility_event,
|
||||
tombstone_event,
|
||||
) = join!(
|
||||
services.rooms.metadata.is_banned(&room_id),
|
||||
services.rooms.metadata.is_disabled(&room_id),
|
||||
services.rooms.state_cache.room_joined_count(&room_id),
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.active_local_users_in_room(&room_id)
|
||||
.count(),
|
||||
services.rooms.state_cache.room_servers(&room_id).count(),
|
||||
services.rooms.directory.is_public_room(&room_id),
|
||||
services.rooms.state_accessor.room_state_get(
|
||||
&room_id,
|
||||
&StateEventType::RoomCreate,
|
||||
""
|
||||
),
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<PossiblyRedactedRoomEncryptionEventContent>(
|
||||
&room_id,
|
||||
&StateEventType::RoomEncryption,
|
||||
""
|
||||
),
|
||||
services.rooms.state_accessor.room_state_get_content(
|
||||
&room_id,
|
||||
&StateEventType::RoomName,
|
||||
""
|
||||
),
|
||||
services.rooms.state_accessor.room_state_get_content(
|
||||
&room_id,
|
||||
&StateEventType::RoomTopic,
|
||||
""
|
||||
),
|
||||
services.rooms.state_accessor.room_state_get_content(
|
||||
&room_id,
|
||||
&StateEventType::RoomCanonicalAlias,
|
||||
""
|
||||
),
|
||||
services.rooms.state_accessor.room_state_get_content(
|
||||
&room_id,
|
||||
&StateEventType::RoomJoinRules,
|
||||
""
|
||||
),
|
||||
services.rooms.state_accessor.room_state_get_content(
|
||||
&room_id,
|
||||
&StateEventType::RoomHistoryVisibility,
|
||||
""
|
||||
),
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<PossiblyRedactedRoomTombstoneEventContent>(
|
||||
&room_id,
|
||||
&StateEventType::RoomTombstone,
|
||||
""
|
||||
),
|
||||
);
|
||||
let Ok(create_event) = create_event else {
|
||||
return None;
|
||||
};
|
||||
let create_content = create_event
|
||||
.get_content::<RoomCreateEventContent>()
|
||||
.expect("m.room.create content must be valid");
|
||||
Some(rooms::list::v1::MinimalRoomInfo {
|
||||
room_id,
|
||||
banned,
|
||||
disabled,
|
||||
member_count: usize::try_from(member_count.unwrap_or_default())
|
||||
.expect("u64 should fit in usize"),
|
||||
local_member_count,
|
||||
resident_server_count,
|
||||
creators: vec![create_event.sender],
|
||||
encrypted: encryption_event.is_ok_and(|c| c.algorithm.is_some()),
|
||||
federated: create_content.federate,
|
||||
published,
|
||||
version: create_content.room_version,
|
||||
name: name_event.unwrap_or(None),
|
||||
topic: topic_event.unwrap_or(None),
|
||||
canonical_alias: canonical_alias_event.unwrap_or(None),
|
||||
join_rules: join_rules_event.unwrap_or(None),
|
||||
history_visibility: history_visibility_event.unwrap_or(None),
|
||||
predecessor: create_content.predecessor.map(|c| c.room_id),
|
||||
successor: tombstone_event.map_or(None, |c| c.replacement_room),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
Ok(rooms::list::v1::Response::new(rooms))
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod ban;
|
||||
mod list;
|
||||
|
||||
pub(crate) use ban::ban_room;
|
||||
pub(crate) use list::*;
|
||||
@@ -0,0 +1,119 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, err, error, info,
|
||||
utils::{IterStream, stream::BroadbandExt},
|
||||
warn,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::UserId;
|
||||
use ruminuwuity::admin::continuwuity::users;
|
||||
use service::users::HashedPassword;
|
||||
|
||||
use crate::router::Ruma;
|
||||
|
||||
/// # `POST /_continuwuity/admin/v1/users/create`
|
||||
///
|
||||
/// Creates a new user.
|
||||
pub(crate) async fn create_user_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<users::create::v1::Request>,
|
||||
) -> conduwuit::Result<users::create::v1::Response> {
|
||||
let email = body
|
||||
.email
|
||||
.clone()
|
||||
.map(lettre::Address::try_from)
|
||||
.transpose()
|
||||
.map_err(|e| err!(Request(BadJson("Invalid email address: {e}"))))?;
|
||||
|
||||
let ref user_id = services
|
||||
.users
|
||||
.determine_registration_user_id(Some(body.localpart.clone()), email.as_ref(), None)
|
||||
.await?;
|
||||
|
||||
services
|
||||
.users
|
||||
.create_local_account(user_id, HashedPassword::new(&body.password)?, email)
|
||||
.await;
|
||||
|
||||
if body.suspended {
|
||||
services
|
||||
.users
|
||||
.suspend_account(&user_id, body.identity.sender_user())
|
||||
.await;
|
||||
}
|
||||
if body.locked {
|
||||
services
|
||||
.users
|
||||
.lock_account(user_id, body.identity.sender_user())
|
||||
.await;
|
||||
}
|
||||
if body.login_disabled {
|
||||
services.users.disable_login(user_id);
|
||||
}
|
||||
if let Some(ref value) = body.display_name {
|
||||
services.users.set_profile_key(
|
||||
user_id,
|
||||
"displayname",
|
||||
Some(serde_json::to_value(value)?),
|
||||
);
|
||||
}
|
||||
if let Some(ref value) = body.avatar_url {
|
||||
services
|
||||
.users
|
||||
.set_profile_key(user_id, "avatar_url", Some(serde_json::to_value(value)?));
|
||||
}
|
||||
if body.admin {
|
||||
services
|
||||
.admin
|
||||
.make_user_admin(user_id)
|
||||
.await
|
||||
.inspect_err(|e| error!("failed to make new user {user_id} an admin: {e}"))
|
||||
.ok();
|
||||
}
|
||||
if !body.skip_auto_join {
|
||||
services.users.join_auto_join_rooms(user_id).await;
|
||||
}
|
||||
|
||||
body.auto_join_rooms
|
||||
.clone()
|
||||
.into_iter()
|
||||
.stream()
|
||||
.broad_filter_map(|room| async move {
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_with_servers(&room, None)
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
warn!(
|
||||
"Failed to resolve room alias to room ID when attempting to auto join \
|
||||
{room}: {e}"
|
||||
);
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.for_each_concurrent(None, |(room_id, servers)| async move {
|
||||
match services
|
||||
.rooms
|
||||
.membership
|
||||
.join_room(
|
||||
user_id,
|
||||
&room_id,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
servers.as_ref(),
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
| Err(e) => {
|
||||
warn!("Failed to automatically join {user_id} to {room_id}: {e}");
|
||||
},
|
||||
| _ => {
|
||||
info!("Automatically joined room {user_id} to {room_id}");
|
||||
},
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(users::create::v1::Response::new(user_id.to_owned()))
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::utils::stream::WidebandExt;
|
||||
use futures::StreamExt;
|
||||
use ruminuwuity::admin::continuwuity::users;
|
||||
use tokio::join;
|
||||
|
||||
use crate::router::Ruma;
|
||||
|
||||
/// # `GET /_continuwuity/admin/v1/users`
|
||||
///
|
||||
/// Lists all users on this homeserver.
|
||||
pub(crate) async fn list_users_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<users::list::v1::Request>,
|
||||
) -> conduwuit::Result<users::list::v1::Response> {
|
||||
let users = services
|
||||
.users
|
||||
.list_local_users()
|
||||
.skip(body.offset.unwrap_or_default())
|
||||
.take(body.limit.unwrap_or(100).min(100))
|
||||
.wide_filter_map(|user_id| async move {
|
||||
let (deactivated, suspended, locked, admin, login_disabled) = join!(
|
||||
services.users.is_deactivated(&user_id),
|
||||
services.users.is_suspended(&user_id),
|
||||
services.users.is_locked(&user_id),
|
||||
services.users.is_admin(&user_id),
|
||||
services.users.is_login_disabled(&user_id),
|
||||
);
|
||||
Some(users::list::v1::User {
|
||||
user_id: user_id.clone(),
|
||||
deactivated: deactivated.unwrap_or_default(),
|
||||
suspended: suspended.unwrap_or_default(),
|
||||
locked: locked.unwrap_or_default(),
|
||||
admin,
|
||||
login_disabled,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
Ok(users::list::v1::Response::new(users))
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod create;
|
||||
mod list;
|
||||
|
||||
pub(crate) use create::*;
|
||||
pub(crate) use list::*;
|
||||
@@ -1,7 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result};
|
||||
use futures::future::{join, join3};
|
||||
use ruminuwuity::admin::{get_suspended, set_suspended};
|
||||
use ruma::api::client::admin::{is_user_suspended, suspend_user};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
/// Check the suspension status of a target user
|
||||
pub(crate) async fn get_suspended_status(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_suspended::v1::Request>,
|
||||
) -> Result<get_suspended::v1::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
let (admin, active) =
|
||||
join(services.users.is_admin(sender_user), services.users.is_active(&body.user_id)).await;
|
||||
body: Ruma<is_user_suspended::v1::Request>,
|
||||
) -> Result<is_user_suspended::v1::Response> {
|
||||
let (admin, active) = join(
|
||||
services.users.is_admin(body.identity.expect_sender_user()?),
|
||||
services.users.is_active(&body.user_id),
|
||||
)
|
||||
.await;
|
||||
if !admin {
|
||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||
}
|
||||
@@ -25,7 +26,7 @@ pub(crate) async fn get_suspended_status(
|
||||
if !active {
|
||||
return Err!(Request(NotFound("Unknown user")));
|
||||
}
|
||||
Ok(get_suspended::v1::Response::new(
|
||||
Ok(is_user_suspended::v1::Response::new(
|
||||
services.users.is_suspended(&body.user_id).await?,
|
||||
))
|
||||
}
|
||||
@@ -35,9 +36,9 @@ pub(crate) async fn get_suspended_status(
|
||||
/// Set the suspension status of a target user
|
||||
pub(crate) async fn put_suspended_status(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_suspended::v1::Request>,
|
||||
) -> Result<set_suspended::v1::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
body: Ruma<suspend_user::v1::Request>,
|
||||
) -> Result<suspend_user::v1::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let (sender_admin, active, target_admin) = join3(
|
||||
services.users.is_admin(sender_user),
|
||||
@@ -63,13 +64,13 @@ pub(crate) async fn put_suspended_status(
|
||||
}
|
||||
if services.users.is_suspended(&body.user_id).await? == body.suspended {
|
||||
// No change
|
||||
return Ok(set_suspended::v1::Response::new(body.suspended));
|
||||
return Ok(suspend_user::v1::Response::new(body.suspended));
|
||||
}
|
||||
|
||||
let action = if body.suspended {
|
||||
services
|
||||
.users
|
||||
.suspend_account(&body.user_id, sender_user)
|
||||
.suspend_account(&body.user_id, body.identity.sender_user())
|
||||
.await;
|
||||
"suspended"
|
||||
} else {
|
||||
@@ -85,5 +86,5 @@ pub(crate) async fn put_suspended_status(
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(set_suspended::v1::Response::new(body.suspended))
|
||||
Ok(suspend_user::v1::Response::new(body.suspended))
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ pub(crate) async fn create_alias_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<create_alias::v3::Request>,
|
||||
) -> Result<create_alias::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
@@ -19,7 +20,7 @@ pub(crate) async fn create_alias_route(
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.appservice_checks(&body.room_alias, &body.appservice_info)
|
||||
.appservice_checks(&body.room_alias, body.identity.appservice_info())
|
||||
.await?;
|
||||
|
||||
// this isn't apart of alias_checks or delete alias route because we should
|
||||
@@ -59,7 +60,8 @@ pub(crate) async fn delete_alias_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_alias::v3::Request>,
|
||||
) -> Result<delete_alias::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
@@ -67,7 +69,7 @@ pub(crate) async fn delete_alias_route(
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.appservice_checks(&body.room_alias, &body.appservice_info)
|
||||
.appservice_checks(&body.room_alias, body.identity.appservice_info())
|
||||
.await?;
|
||||
|
||||
services
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, err};
|
||||
use conduwuit::{Err, Result};
|
||||
use ruma::{
|
||||
api::{appservice::ping, client::appservice::request_ping},
|
||||
assign,
|
||||
@@ -15,9 +15,7 @@ pub(crate) async fn appservice_ping(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<request_ping::v1::Request>,
|
||||
) -> Result<request_ping::v1::Response> {
|
||||
let appservice_info = body.appservice_info.as_ref().ok_or_else(|| {
|
||||
err!(Request(Forbidden("This endpoint can only be called by appservices.")))
|
||||
})?;
|
||||
let appservice_info = &body.identity;
|
||||
|
||||
if body.appservice_id != appservice_info.registration.id {
|
||||
return Err!(Request(Forbidden(
|
||||
|
||||
+47
-26
@@ -25,7 +25,7 @@ pub(crate) async fn create_backup_version_route(
|
||||
) -> Result<create_backup_version::v3::Response> {
|
||||
let version = services
|
||||
.key_backups
|
||||
.create_backup(body.sender_user(), &body.algorithm)?;
|
||||
.create_backup(body.identity.expect_sender_user()?, &body.algorithm)?;
|
||||
|
||||
Ok(create_backup_version::v3::Response::new(version))
|
||||
}
|
||||
@@ -40,7 +40,7 @@ pub(crate) async fn update_backup_version_route(
|
||||
) -> Result<update_backup_version::v3::Response> {
|
||||
services
|
||||
.key_backups
|
||||
.update_backup(body.sender_user(), &body.version, &body.algorithm)
|
||||
.update_backup(body.identity.expect_sender_user()?, &body.version, &body.algorithm)
|
||||
.await?;
|
||||
|
||||
Ok(update_backup_version::v3::Response::new())
|
||||
@@ -53,13 +53,15 @@ pub(crate) async fn get_latest_backup_info_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_latest_backup_info::v3::Request>,
|
||||
) -> Result<get_latest_backup_info::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let (version, algorithm) = services
|
||||
.key_backups
|
||||
.get_latest_backup(body.sender_user())
|
||||
.get_latest_backup(sender_user)
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Key backup does not exist."))))?;
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &version).await;
|
||||
let (count, etag) = get_count_etag(&services, sender_user, &version).await;
|
||||
|
||||
Ok(get_latest_backup_info::v3::Response::new(algorithm, count, etag, version))
|
||||
}
|
||||
@@ -71,15 +73,17 @@ pub(crate) async fn get_backup_info_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_backup_info::v3::Request>,
|
||||
) -> Result<get_backup_info::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let algorithm = services
|
||||
.key_backups
|
||||
.get_backup(body.sender_user(), &body.version)
|
||||
.get_backup(sender_user, &body.version)
|
||||
.await
|
||||
.map_err(|_| {
|
||||
err!(Request(NotFound("Key backup does not exist at version {:?}", body.version)))
|
||||
})?;
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
||||
let (count, etag) = get_count_etag(&services, sender_user, &body.version).await;
|
||||
|
||||
Ok(get_backup_info::v3::Response::new(algorithm, count, etag, body.version.clone()))
|
||||
}
|
||||
@@ -96,7 +100,7 @@ pub(crate) async fn delete_backup_version_route(
|
||||
) -> Result<delete_backup_version::v3::Response> {
|
||||
services
|
||||
.key_backups
|
||||
.delete_backup(body.sender_user(), &body.version)
|
||||
.delete_backup(body.identity.expect_sender_user()?, &body.version)
|
||||
.await;
|
||||
|
||||
Ok(delete_backup_version::v3::Response::new())
|
||||
@@ -114,9 +118,11 @@ pub(crate) async fn add_backup_keys_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<add_backup_keys::v3::Request>,
|
||||
) -> Result<add_backup_keys::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if services
|
||||
.key_backups
|
||||
.get_latest_backup_version(body.sender_user())
|
||||
.get_latest_backup_version(sender_user)
|
||||
.await
|
||||
.is_ok_and(|version| version != body.version)
|
||||
{
|
||||
@@ -129,12 +135,12 @@ pub(crate) async fn add_backup_keys_route(
|
||||
for (session_id, key_data) in &room.sessions {
|
||||
services
|
||||
.key_backups
|
||||
.add_key(body.sender_user(), &body.version, room_id, session_id, key_data)
|
||||
.add_key(sender_user, &body.version, room_id, session_id, key_data)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
||||
let (count, etag) = get_count_etag(&services, sender_user, &body.version).await;
|
||||
|
||||
Ok(add_backup_keys::v3::Response::new(etag, count))
|
||||
}
|
||||
@@ -151,9 +157,11 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<add_backup_keys_for_room::v3::Request>,
|
||||
) -> Result<add_backup_keys_for_room::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if services
|
||||
.key_backups
|
||||
.get_latest_backup_version(body.sender_user())
|
||||
.get_latest_backup_version(sender_user)
|
||||
.await
|
||||
.is_ok_and(|version| version != body.version)
|
||||
{
|
||||
@@ -165,11 +173,11 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
||||
for (session_id, key_data) in &body.sessions {
|
||||
services
|
||||
.key_backups
|
||||
.add_key(body.sender_user(), &body.version, &body.room_id, session_id, key_data)
|
||||
.add_key(sender_user, &body.version, &body.room_id, session_id, key_data)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
||||
let (count, etag) = get_count_etag(&services, sender_user, &body.version).await;
|
||||
|
||||
Ok(add_backup_keys_for_room::v3::Response::new(etag, count))
|
||||
}
|
||||
@@ -186,9 +194,11 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<add_backup_keys_for_session::v3::Request>,
|
||||
) -> Result<add_backup_keys_for_session::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if services
|
||||
.key_backups
|
||||
.get_latest_backup_version(body.sender_user())
|
||||
.get_latest_backup_version(sender_user)
|
||||
.await
|
||||
.is_ok_and(|version| version != body.version)
|
||||
{
|
||||
@@ -201,7 +211,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||
let mut ok_to_replace = true;
|
||||
if let Some(old_key) = &services
|
||||
.key_backups
|
||||
.get_session(body.sender_user(), &body.version, &body.room_id, &body.session_id)
|
||||
.get_session(sender_user, &body.version, &body.room_id, &body.session_id)
|
||||
.await
|
||||
.ok()
|
||||
{
|
||||
@@ -260,7 +270,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||
services
|
||||
.key_backups
|
||||
.add_key(
|
||||
body.sender_user(),
|
||||
sender_user,
|
||||
&body.version,
|
||||
&body.room_id,
|
||||
&body.session_id,
|
||||
@@ -269,7 +279,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||
.await?;
|
||||
}
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
||||
let (count, etag) = get_count_etag(&services, sender_user, &body.version).await;
|
||||
|
||||
Ok(add_backup_keys_for_session::v3::Response::new(etag, count))
|
||||
}
|
||||
@@ -283,7 +293,7 @@ pub(crate) async fn get_backup_keys_route(
|
||||
) -> Result<get_backup_keys::v3::Response> {
|
||||
let rooms = services
|
||||
.key_backups
|
||||
.get_all(body.sender_user(), &body.version)
|
||||
.get_all(body.identity.expect_sender_user()?, &body.version)
|
||||
.await;
|
||||
|
||||
Ok(get_backup_keys::v3::Response::new(rooms))
|
||||
@@ -298,7 +308,7 @@ pub(crate) async fn get_backup_keys_for_room_route(
|
||||
) -> Result<get_backup_keys_for_room::v3::Response> {
|
||||
let sessions = services
|
||||
.key_backups
|
||||
.get_room(body.sender_user(), &body.version, &body.room_id)
|
||||
.get_room(body.identity.expect_sender_user()?, &body.version, &body.room_id)
|
||||
.await;
|
||||
|
||||
Ok(get_backup_keys_for_room::v3::Response::new(sessions))
|
||||
@@ -313,7 +323,12 @@ pub(crate) async fn get_backup_keys_for_session_route(
|
||||
) -> Result<get_backup_keys_for_session::v3::Response> {
|
||||
let key_data = services
|
||||
.key_backups
|
||||
.get_session(body.sender_user(), &body.version, &body.room_id, &body.session_id)
|
||||
.get_session(
|
||||
body.identity.expect_sender_user()?,
|
||||
&body.version,
|
||||
&body.room_id,
|
||||
&body.session_id,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| {
|
||||
err!(Request(NotFound(debug_error!("Backup key not found for this user's session."))))
|
||||
@@ -329,12 +344,14 @@ pub(crate) async fn delete_backup_keys_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_backup_keys::v3::Request>,
|
||||
) -> Result<delete_backup_keys::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
services
|
||||
.key_backups
|
||||
.delete_all_keys(body.sender_user(), &body.version)
|
||||
.delete_all_keys(sender_user, &body.version)
|
||||
.await;
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
||||
let (count, etag) = get_count_etag(&services, sender_user, &body.version).await;
|
||||
|
||||
Ok(delete_backup_keys::v3::Response::new(etag, count))
|
||||
}
|
||||
@@ -346,12 +363,14 @@ pub(crate) async fn delete_backup_keys_for_room_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_backup_keys_for_room::v3::Request>,
|
||||
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
services
|
||||
.key_backups
|
||||
.delete_room_keys(body.sender_user(), &body.version, &body.room_id)
|
||||
.delete_room_keys(sender_user, &body.version, &body.room_id)
|
||||
.await;
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
||||
let (count, etag) = get_count_etag(&services, sender_user, &body.version).await;
|
||||
|
||||
Ok(delete_backup_keys_for_room::v3::Response::new(etag, count))
|
||||
}
|
||||
@@ -363,12 +382,14 @@ pub(crate) async fn delete_backup_keys_for_session_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_backup_keys_for_session::v3::Request>,
|
||||
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
services
|
||||
.key_backups
|
||||
.delete_room_key(body.sender_user(), &body.version, &body.room_id, &body.session_id)
|
||||
.delete_room_key(sender_user, &body.version, &body.room_id, &body.session_id)
|
||||
.await;
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
||||
let (count, etag) = get_count_etag(&services, sender_user, &body.version).await;
|
||||
|
||||
Ok(delete_backup_keys_for_session::v3::Response::new(etag, count))
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ pub(crate) async fn get_capabilities_route(
|
||||
|
||||
if services
|
||||
.users
|
||||
.is_admin(body.sender_user.as_ref().unwrap())
|
||||
.is_admin(body.identity.expect_sender_user()?)
|
||||
.await
|
||||
{
|
||||
// Advertise suspension API
|
||||
|
||||
@@ -37,8 +37,8 @@ pub(crate) async fn get_context_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_context::v3::Request>,
|
||||
) -> Result<get_context::v3::Response> {
|
||||
let sender = body.sender();
|
||||
let (sender_user, sender_device) = sender;
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let sender_device = body.identity.sender_device();
|
||||
let room_id = &body.room_id;
|
||||
let event_id = &body.event_id;
|
||||
let filter = &body.filter;
|
||||
@@ -143,7 +143,7 @@ pub(crate) async fn get_context_route(
|
||||
|
||||
let lazy_loading_context = lazy_loading::Context {
|
||||
user_id: sender_user,
|
||||
device_id: Some(sender_device),
|
||||
device_id: sender_device,
|
||||
room_id,
|
||||
token: Some(base_count.into_unsigned()),
|
||||
options: Some(&filter.lazy_load_options),
|
||||
|
||||
@@ -25,16 +25,11 @@ pub(crate) async fn put_dehydrated_device_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<put_dehydrated_device::Request>,
|
||||
) -> Result<put_dehydrated_device::Response> {
|
||||
let sender_user = body
|
||||
.sender_user
|
||||
.as_deref()
|
||||
.expect("AccessToken authentication required");
|
||||
|
||||
let device_id = body.body.device_id.clone();
|
||||
let device_id = body.device_id.clone();
|
||||
|
||||
services
|
||||
.users
|
||||
.set_dehydrated_device(sender_user, body.body)
|
||||
.set_dehydrated_device(body.identity.expect_sender_user()?, body.body)
|
||||
.await?;
|
||||
|
||||
Ok(put_dehydrated_device::Response::new(device_id))
|
||||
@@ -49,7 +44,7 @@ pub(crate) async fn delete_dehydrated_device_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<delete_dehydrated_device::Request>,
|
||||
) -> Result<delete_dehydrated_device::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let device_id = services.users.get_dehydrated_device_id(sender_user).await?;
|
||||
|
||||
@@ -67,7 +62,7 @@ pub(crate) async fn get_dehydrated_device_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_dehydrated_device::Request>,
|
||||
) -> Result<get_dehydrated_device::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let device = services.users.get_dehydrated_device(sender_user).await?;
|
||||
|
||||
@@ -83,7 +78,7 @@ pub(crate) async fn get_dehydrated_events_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_events::Request>,
|
||||
) -> Result<get_events::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let device_id = &body.body.device_id;
|
||||
let existing_id = services.users.get_dehydrated_device_id(sender_user).await;
|
||||
|
||||
+11
-13
@@ -8,7 +8,6 @@
|
||||
self, delete_device, delete_devices, get_device, get_devices, update_device,
|
||||
},
|
||||
};
|
||||
use service::uiaa::Identity;
|
||||
|
||||
use crate::{Ruma, client::DEVICE_ID_LENGTH};
|
||||
|
||||
@@ -21,7 +20,7 @@ pub(crate) async fn get_devices_route(
|
||||
) -> Result<get_devices::v3::Response> {
|
||||
let devices: Vec<device::Device> = services
|
||||
.users
|
||||
.all_devices_metadata(body.sender_user())
|
||||
.all_devices_metadata(body.identity.expect_sender_user()?)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
@@ -37,7 +36,7 @@ pub(crate) async fn get_device_route(
|
||||
) -> Result<get_device::v3::Response> {
|
||||
let device = services
|
||||
.users
|
||||
.get_device_metadata(body.sender_user(), &body.body.device_id)
|
||||
.get_device_metadata(body.identity.expect_sender_user()?, &body.body.device_id)
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
|
||||
|
||||
@@ -53,8 +52,8 @@ pub(crate) async fn update_device_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<update_device::v3::Request>,
|
||||
) -> Result<update_device::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let appservice = body.appservice_info.as_ref();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let appservice = body.identity.appservice_info();
|
||||
|
||||
match services
|
||||
.users
|
||||
@@ -95,6 +94,7 @@ pub(crate) async fn update_device_route(
|
||||
&device_id,
|
||||
&appservice.registration.as_token,
|
||||
None,
|
||||
None,
|
||||
Some(client.to_string()),
|
||||
)
|
||||
.await?;
|
||||
@@ -118,15 +118,14 @@ pub(crate) async fn delete_device_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_device::v3::Request>,
|
||||
) -> Result<delete_device::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let appservice = body.appservice_info.as_ref();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
// Appservices get to skip UIAA for this endpoint
|
||||
if appservice.is_none() {
|
||||
if let Some(sender_device) = body.identity.sender_device() {
|
||||
// Prompt the user to confirm with their password using UIAA
|
||||
let _ = services
|
||||
.uiaa
|
||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
||||
.authenticate_password(&body.auth, sender_user, Some(sender_device), None)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -154,15 +153,14 @@ pub(crate) async fn delete_devices_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_devices::v3::Request>,
|
||||
) -> Result<delete_devices::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let appservice = body.appservice_info.as_ref();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
// Appservices get to skip UIAA for this endpoint
|
||||
if appservice.is_none() {
|
||||
if let Some(sender_device) = body.identity.sender_device() {
|
||||
// Prompt the user to confirm with their password using UIAA
|
||||
let _ = services
|
||||
.uiaa
|
||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
||||
.authenticate_password(&body.auth, sender_user, Some(sender_device), None)
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ pub(crate) async fn set_room_visibility_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<set_room_visibility::v3::Request>,
|
||||
) -> Result<set_room_visibility::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if !services.rooms.metadata.exists(&body.room_id).await {
|
||||
// Return 404 if the room doesn't exist
|
||||
@@ -130,7 +130,7 @@ pub(crate) async fn set_room_visibility_route(
|
||||
| room::Visibility::Public => {
|
||||
if services.server.config.lockdown_public_room_directory
|
||||
&& !services.users.is_admin(sender_user).await
|
||||
&& body.appservice_info.is_none()
|
||||
&& !body.identity.is_appservice()
|
||||
{
|
||||
info!(
|
||||
"Non-admin user {sender_user} tried to publish {0} to the room directory \
|
||||
|
||||
@@ -15,7 +15,7 @@ pub(crate) async fn get_filter_route(
|
||||
) -> Result<get_filter::v3::Response> {
|
||||
services
|
||||
.users
|
||||
.get_filter(body.sender_user(), &body.filter_id)
|
||||
.get_filter(body.identity.expect_sender_user()?, &body.filter_id)
|
||||
.await
|
||||
.map(get_filter::v3::Response::new)
|
||||
.map_err(|_| err!(Request(NotFound("Filter not found."))))
|
||||
@@ -30,7 +30,7 @@ pub(crate) async fn create_filter_route(
|
||||
) -> Result<create_filter::v3::Response> {
|
||||
let filter_id = services
|
||||
.users
|
||||
.create_filter(body.sender_user(), &body.filter);
|
||||
.create_filter(body.identity.expect_sender_user()?, &body.filter);
|
||||
|
||||
Ok(create_filter::v3::Response::new(filter_id))
|
||||
}
|
||||
|
||||
+13
-7
@@ -26,7 +26,7 @@
|
||||
serde::Raw,
|
||||
};
|
||||
use serde_json::json;
|
||||
use service::uiaa::Identity;
|
||||
use service::oauth::OAuthTicket;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -41,7 +41,8 @@ pub(crate) async fn upload_keys_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<upload_keys::v3::Request>,
|
||||
) -> Result<upload_keys::v3::Response> {
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let sender_device = body.identity.expect_sender_device()?;
|
||||
|
||||
for (key_id, one_time_key) in &body.one_time_keys {
|
||||
if one_time_key
|
||||
@@ -154,7 +155,7 @@ pub(crate) async fn get_keys_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_keys::v3::Request>,
|
||||
) -> Result<get_keys::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
get_keys_helper(
|
||||
&services,
|
||||
@@ -191,7 +192,7 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<upload_signing_keys::v3::Request>,
|
||||
) -> Result<upload_signing_keys::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if uiaa_needed_to_upload_keys(
|
||||
services,
|
||||
@@ -204,7 +205,12 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
{
|
||||
let _ = services
|
||||
.uiaa
|
||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
||||
.authenticate_password(
|
||||
&body.auth,
|
||||
sender_user,
|
||||
body.identity.sender_device(),
|
||||
Some(OAuthTicket::CrossSigningReset),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -287,7 +293,7 @@ pub(crate) async fn upload_signatures_route(
|
||||
return Ok(upload_signatures::v3::Response::new());
|
||||
}
|
||||
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
for (user_id, keys) in &body.signed_keys {
|
||||
for (key_id, key) in keys {
|
||||
@@ -340,7 +346,7 @@ pub(crate) async fn get_key_changes_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_key_changes::v3::Request>,
|
||||
) -> Result<get_key_changes::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let mut device_list_updates = HashSet::new();
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ pub(crate) async fn create_content_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<create_content::v3::Response> {
|
||||
let user = body.sender_user();
|
||||
let user = body.identity.expect_sender_user()?;
|
||||
if services.users.is_suspended(user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
@@ -92,7 +92,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content_thumbnail::v1::Request>,
|
||||
) -> Result<get_content_thumbnail::v1::Response> {
|
||||
let user = body.sender_user();
|
||||
let user = body.identity.expect_sender_user()?;
|
||||
|
||||
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
||||
let mxc = Mxc {
|
||||
@@ -142,7 +142,7 @@ pub(crate) async fn get_content_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content::v1::Request>,
|
||||
) -> Result<get_content::v1::Response> {
|
||||
let user = body.sender_user();
|
||||
let user = body.identity.expect_sender_user()?;
|
||||
|
||||
let mxc = Mxc {
|
||||
server_name: &body.server_name,
|
||||
@@ -189,7 +189,7 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_content_as_filename::v1::Request>,
|
||||
) -> Result<get_content_as_filename::v1::Response> {
|
||||
let user = body.sender_user();
|
||||
let user = body.identity.expect_sender_user()?;
|
||||
|
||||
let mxc = Mxc {
|
||||
server_name: &body.server_name,
|
||||
@@ -240,7 +240,7 @@ pub(crate) async fn get_media_preview_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_media_preview::v1::Request>,
|
||||
) -> Result<get_media_preview::v1::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let url = &body.url;
|
||||
let url = Url::parse(&body.url).map_err(|e| {
|
||||
|
||||
@@ -56,7 +56,7 @@ pub(crate) async fn get_media_preview_legacy_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<get_media_preview::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let url = &body.url;
|
||||
let url = Url::parse(&body.url).map_err(|e| {
|
||||
|
||||
@@ -15,7 +15,7 @@ pub(crate) async fn ban_user_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<ban_user::v3::Request>,
|
||||
) -> Result<ban_user::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if sender_user == body.user_id {
|
||||
return Err!(Request(Forbidden("You cannot ban yourself.")));
|
||||
|
||||
@@ -18,7 +18,7 @@ pub(crate) async fn forget_room_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<forget_room::v3::Request>,
|
||||
) -> Result<forget_room::v3::Response> {
|
||||
let user_id = body.sender_user();
|
||||
let user_id = body.identity.expect_sender_user()?;
|
||||
let room_id = &body.room_id;
|
||||
|
||||
let joined = services.rooms.state_cache.is_joined(user_id, room_id);
|
||||
|
||||
@@ -29,7 +29,7 @@ pub(crate) async fn invite_user_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<invite_user::v3::Request>,
|
||||
) -> Result<invite_user::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ pub(crate) async fn join_room_by_id_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<join_room_by_id::v3::Request>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
@@ -97,7 +97,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
||||
) -> Result<join_room_by_id_or_alias::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let body = &body.body;
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
|
||||
@@ -15,7 +15,7 @@ pub(crate) async fn kick_user_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<kick_user::v3::Request>,
|
||||
) -> Result<kick_user::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ pub(crate) async fn knock_room_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<knock_room::v3::Request>,
|
||||
) -> Result<knock_room::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let body = &body.body;
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
|
||||
@@ -32,10 +32,15 @@ pub(crate) async fn leave_room_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<leave_room::v3::Request>,
|
||||
) -> Result<leave_room::v3::Response> {
|
||||
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone())
|
||||
.boxed()
|
||||
.await
|
||||
.map(|()| leave_room::v3::Response::new())
|
||||
leave_room(
|
||||
&services,
|
||||
body.identity.expect_sender_user()?,
|
||||
&body.room_id,
|
||||
body.reason.clone(),
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
.map(|()| leave_room::v3::Response::new())
|
||||
}
|
||||
|
||||
// Make a user leave all their joined rooms, rescinds knocks, forgets all rooms,
|
||||
|
||||
@@ -30,7 +30,7 @@ pub(crate) async fn get_member_events_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_member_events::v3::Request>,
|
||||
) -> Result<get_member_events::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let membership = body.membership.as_ref();
|
||||
let not_membership = body.not_membership.as_ref();
|
||||
|
||||
@@ -72,7 +72,7 @@ pub(crate) async fn joined_members_route(
|
||||
if !services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(body.sender_user(), &body.room_id)
|
||||
.user_can_see_state_events(body.identity.expect_sender_user()?, &body.room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
||||
|
||||
@@ -40,7 +40,7 @@ pub(crate) async fn joined_rooms_route(
|
||||
let joined_rooms = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(body.sender_user())
|
||||
.rooms_joined(body.identity.expect_sender_user()?)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
@@ -105,11 +105,7 @@ pub(crate) async fn banned_room_check(
|
||||
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
||||
}
|
||||
} else if let Some(server_name) = server_name {
|
||||
if services
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.is_match(server_name.host())
|
||||
{
|
||||
if services.moderation.is_remote_server_forbidden(server_name) {
|
||||
warn!(
|
||||
"User {user_id} who is not an admin tried joining a room which has the server \
|
||||
name {server_name} that is globally forbidden. Rejecting.",
|
||||
|
||||
@@ -14,7 +14,7 @@ pub(crate) async fn unban_user_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<unban_user::v3::Request>,
|
||||
) -> Result<unban_user::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
};
|
||||
use futures::{FutureExt, StreamExt, TryFutureExt, future::OptionFuture, pin_mut};
|
||||
use ruma::{
|
||||
DeviceId, RoomId, UserId,
|
||||
RoomId, UserId,
|
||||
api::{
|
||||
Direction,
|
||||
client::{filter::RoomEventFilter, message::get_message_events},
|
||||
@@ -37,7 +37,6 @@
|
||||
serde::Raw,
|
||||
};
|
||||
use ruminuwuity::invite_permission_config::FilterLevel;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -76,8 +75,8 @@ pub(crate) async fn get_message_events_route(
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<get_message_events::v3::Request>,
|
||||
) -> Result<get_message_events::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_device = body.sender_device.as_deref();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let sender_device = body.identity.sender_device();
|
||||
let room_id = &body.room_id;
|
||||
let filter = &body.filter;
|
||||
|
||||
@@ -158,17 +157,7 @@ pub(crate) async fn get_message_events_route(
|
||||
|
||||
let lazy_loading_context = lazy_loading::Context {
|
||||
user_id: sender_user,
|
||||
device_id: sender_device.or_else(|| {
|
||||
if let Some(registration) = body.appservice_info.as_ref() {
|
||||
Some(<&DeviceId>::from(registration.registration.id.as_str()))
|
||||
} else {
|
||||
warn!(
|
||||
"No device_id provided and no appservice registration found, this should be \
|
||||
unreachable"
|
||||
);
|
||||
None
|
||||
}
|
||||
}),
|
||||
device_id: sender_device,
|
||||
room_id,
|
||||
token: Some(from.into_unsigned()),
|
||||
options: Some(&filter.lazy_load_options),
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
pub(super) mod membership;
|
||||
pub(super) mod message;
|
||||
pub(super) mod mutual_rooms;
|
||||
pub(super) mod oauth;
|
||||
pub(super) mod openid;
|
||||
pub(super) mod presence;
|
||||
pub(super) mod profile;
|
||||
@@ -61,6 +62,7 @@
|
||||
pub use membership::{leave_all_rooms, leave_room, remote_leave_room};
|
||||
pub(super) use message::*;
|
||||
pub(super) use mutual_rooms::*;
|
||||
pub(super) use oauth::*;
|
||||
pub(super) use openid::*;
|
||||
pub(super) use presence::*;
|
||||
pub(super) use profile::*;
|
||||
@@ -73,6 +75,7 @@
|
||||
pub(super) use room::*;
|
||||
pub(super) use search::*;
|
||||
pub(super) use send::*;
|
||||
pub use session::handle_login;
|
||||
pub(super) use session::*;
|
||||
pub(super) use space::*;
|
||||
pub(super) use state::*;
|
||||
|
||||
@@ -15,7 +15,7 @@ pub(crate) async fn get_mutual_rooms_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<mutual_rooms::unstable::Request>,
|
||||
) -> Result<mutual_rooms::unstable::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if sender_user == body.user_id {
|
||||
return Err!(Request(Unknown("You cannot request rooms in common with yourself.")));
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
use axum::{
|
||||
Json, Router,
|
||||
extract::{Request, State},
|
||||
middleware::{self, Next},
|
||||
response::{IntoResponse, Response},
|
||||
routing::method_routing::{get, post},
|
||||
};
|
||||
use const_str::concat;
|
||||
use http::StatusCode;
|
||||
use serde_json::json;
|
||||
pub(crate) use server_metadata::*;
|
||||
|
||||
mod register_client;
|
||||
mod server_metadata;
|
||||
mod token;
|
||||
|
||||
const BASE_PATH: &str = concat!(conduwuit_core::ROUTE_PREFIX, "/oauth2/");
|
||||
const AUTH_CODE_PATH: &str = "grant/authorization_code";
|
||||
const JWKS_URI_PATH: &str = "client/keys.json";
|
||||
const CLIENT_REGISTER_PATH: &str = "client/register";
|
||||
const TOKEN_REVOKE_PATH: &str = "client/revoke";
|
||||
const TOKEN_PATH: &str = "grant/token";
|
||||
const ACCOUNT_MANAGEMENT_PATH: &str = concat!(conduwuit_core::ROUTE_PREFIX, "/account/deeplink");
|
||||
|
||||
pub(crate) fn router(state: crate::State) -> Router<crate::State> {
|
||||
Router::new()
|
||||
.nest(BASE_PATH, oauth_router())
|
||||
.route(
|
||||
"/.well-known/openid-configuration",
|
||||
get(
|
||||
// TODO(unspecced): used by old versions of the matrix-js-sdk
|
||||
async |State(services): State<crate::State>| {
|
||||
Json(authorization_server_metadata(&services).await)
|
||||
},
|
||||
),
|
||||
)
|
||||
.layer(middleware::from_fn_with_state(
|
||||
state,
|
||||
async |State(state): State<crate::State>, request: Request, next: Next| -> Response {
|
||||
if state.config.oauth.compatibility_mode.oauth_available() {
|
||||
next.run(request).await
|
||||
} else {
|
||||
(StatusCode::NOT_FOUND, "OAuth is unavailable on this server").into_response()
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn oauth_router() -> Router<crate::State> {
|
||||
Router::new()
|
||||
.route(concat!("/", CLIENT_REGISTER_PATH), post(register_client::register_client_route))
|
||||
// TODO(unspecced): used by old versions of the matrix-js-sdk
|
||||
.route(concat!("/", JWKS_URI_PATH), get(async || Json(json!({"keys": []}))))
|
||||
.route(concat!("/", TOKEN_PATH), post(token::token_route))
|
||||
.route(concat!("/", TOKEN_REVOKE_PATH), post(token::revoke_token_route))
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::StatusCode;
|
||||
use serde::Serialize;
|
||||
use service::oauth::client_metadata::ClientMetadata;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct RegisteredClient {
|
||||
client_id: String,
|
||||
#[serde(flatten)]
|
||||
metadata: ClientMetadata,
|
||||
}
|
||||
|
||||
pub(crate) async fn register_client_route(
|
||||
State(services): State<crate::State>,
|
||||
Json(metadata): Json<ClientMetadata>,
|
||||
) -> Result<Response, Response> {
|
||||
let client_id = services
|
||||
.oauth
|
||||
.register_client(&metadata)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::BAD_REQUEST, Json(err)).into_response())?;
|
||||
|
||||
Ok(Json(RegisteredClient { client_id, metadata }).into_response())
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result};
|
||||
use ruma::{
|
||||
api::client::discovery::get_authorization_server_metadata::{
|
||||
self, v1::AccountManagementAction,
|
||||
},
|
||||
serde::Raw,
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
use service::Services;
|
||||
|
||||
use crate::{
|
||||
Ruma,
|
||||
client::oauth::{
|
||||
ACCOUNT_MANAGEMENT_PATH, AUTH_CODE_PATH, CLIENT_REGISTER_PATH, JWKS_URI_PATH, TOKEN_PATH,
|
||||
TOKEN_REVOKE_PATH,
|
||||
},
|
||||
};
|
||||
|
||||
pub(crate) async fn get_authorization_server_metadata_route(
|
||||
State(services): State<crate::State>,
|
||||
_body: Ruma<get_authorization_server_metadata::v1::Request>,
|
||||
) -> Result<get_authorization_server_metadata::v1::Response> {
|
||||
if !services.config.oauth.compatibility_mode.oauth_available() {
|
||||
return Err!(Request(Unrecognized("OAuth is unavailable on this server")));
|
||||
}
|
||||
|
||||
let metadata = Raw::new(&authorization_server_metadata(&services).await).unwrap();
|
||||
|
||||
Ok(get_authorization_server_metadata::v1::Response::new(metadata.cast_unchecked()))
|
||||
}
|
||||
|
||||
pub(crate) async fn authorization_server_metadata(services: &Services) -> Value {
|
||||
let endpoint_base = services
|
||||
.config
|
||||
.get_client_domain()
|
||||
.join(super::BASE_PATH)
|
||||
.unwrap();
|
||||
|
||||
json!({
|
||||
"account_management_uri": endpoint_base.join(ACCOUNT_MANAGEMENT_PATH).unwrap(),
|
||||
"account_management_actions_supported": [
|
||||
AccountManagementAction::AccountDeactivate,
|
||||
AccountManagementAction::CrossSigningReset,
|
||||
AccountManagementAction::DeviceDelete,
|
||||
AccountManagementAction::DeviceView,
|
||||
AccountManagementAction::DevicesList,
|
||||
AccountManagementAction::Profile,
|
||||
],
|
||||
"authorization_endpoint": endpoint_base.join(AUTH_CODE_PATH).unwrap(),
|
||||
"code_challenge_methods_supported": ["S256"],
|
||||
"grant_types_supported": ["authorization_code", "refresh_token"],
|
||||
"issuer": services.config.get_client_domain(),
|
||||
"jwks_uri": endpoint_base.join(JWKS_URI_PATH).unwrap(),
|
||||
"prompt_values_supported": ["create"],
|
||||
"registration_endpoint": endpoint_base.join(CLIENT_REGISTER_PATH).unwrap(),
|
||||
"response_modes_supported": ["query", "fragment"],
|
||||
"response_types_supported": ["code"],
|
||||
"revocation_endpoint": endpoint_base.join(TOKEN_REVOKE_PATH).unwrap(),
|
||||
"token_endpoint": endpoint_base.join(TOKEN_PATH).unwrap(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
use axum::{Form, Json, extract::State, response::IntoResponse};
|
||||
use http::StatusCode;
|
||||
use service::oauth::grant::{RevokeTokenRequest, TokenRequest};
|
||||
|
||||
pub(crate) async fn token_route(
|
||||
State(services): State<crate::State>,
|
||||
Form(request): Form<TokenRequest>,
|
||||
) -> impl IntoResponse {
|
||||
match services.oauth.issue_token(request).await {
|
||||
| Ok(response) => Ok(Json(response)),
|
||||
| Err(err) => Err((StatusCode::BAD_REQUEST, Json(err))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn revoke_token_route(
|
||||
State(services): State<crate::State>,
|
||||
Form(request): Form<RevokeTokenRequest>,
|
||||
) -> impl IntoResponse {
|
||||
match services.oauth.revoke_token(request.token).await {
|
||||
| Ok(()) => Ok(StatusCode::OK),
|
||||
| Err(err) => Err((StatusCode::BAD_REQUEST, Json(err))),
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ pub(crate) async fn create_openid_token_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<account::request_openid_token::v3::Request>,
|
||||
) -> Result<account::request_openid_token::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if sender_user != body.user_id {
|
||||
return Err!(Request(InvalidParam(
|
||||
|
||||
@@ -16,17 +16,19 @@ pub(crate) async fn set_presence_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_presence::v3::Request>,
|
||||
) -> Result<set_presence::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if !services.config.allow_local_presence {
|
||||
return Err!(Request(Forbidden("Presence is disabled on this server")));
|
||||
}
|
||||
|
||||
if body.sender_user() != body.user_id && body.appservice_info.is_none() {
|
||||
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||
return Err!(Request(InvalidParam("Not allowed to set presence of other users")));
|
||||
}
|
||||
|
||||
services
|
||||
.presence
|
||||
.set_presence(body.sender_user(), &body.presence, None, None, body.status_msg.clone())
|
||||
.set_presence(sender_user, &body.presence, None, None, body.status_msg.clone())
|
||||
.await?;
|
||||
|
||||
Ok(set_presence::v3::Response::new())
|
||||
@@ -49,7 +51,7 @@ pub(crate) async fn get_presence_route(
|
||||
let has_shared_rooms = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.user_sees_user(body.sender_user(), &body.user_id)
|
||||
.user_sees_user(body.identity.expect_sender_user()?, &body.user_id)
|
||||
.await;
|
||||
|
||||
if has_shared_rooms {
|
||||
|
||||
+147
-53
@@ -8,12 +8,12 @@
|
||||
UserId,
|
||||
api::{
|
||||
client::profile::{
|
||||
delete_profile_field, get_profile, get_profile_field, set_profile_field,
|
||||
PropagateTo, delete_profile_field, get_profile, get_profile_field, set_profile_field,
|
||||
},
|
||||
federation,
|
||||
},
|
||||
assign,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
events::room::member::MembershipState,
|
||||
presence::PresenceState,
|
||||
profile::{ProfileFieldName, ProfileFieldValue},
|
||||
};
|
||||
@@ -31,6 +31,12 @@ pub(crate) async fn get_profile_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_profile::v3::Request>,
|
||||
) -> Result<get_profile::v3::Response> {
|
||||
if services.config.require_auth_for_profile_requests && body.identity.is_none() {
|
||||
return Err!(Request(Unauthorized(
|
||||
"This server requires authentication to view user profiles."
|
||||
)));
|
||||
}
|
||||
|
||||
let Some(profile) = fetch_full_profile(&services, &body.user_id).await else {
|
||||
return Err!(Request(NotFound("This user's profile could not be fetched.")));
|
||||
};
|
||||
@@ -42,6 +48,12 @@ pub(crate) async fn get_profile_field_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_profile_field::v3::Request>,
|
||||
) -> Result<get_profile_field::v3::Response> {
|
||||
if services.config.require_auth_for_profile_requests && body.identity.is_none() {
|
||||
return Err!(Request(Unauthorized(
|
||||
"This server requires authentication to view user profiles."
|
||||
)));
|
||||
}
|
||||
|
||||
let value = fetch_profile_field(&services, &body.user_id, body.field.clone()).await?;
|
||||
|
||||
Ok(assign!(get_profile_field::v3::Response::default(), { value }))
|
||||
@@ -51,9 +63,12 @@ pub(crate) async fn set_profile_field_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_profile_field::v3::Request>,
|
||||
) -> Result<set_profile_field::v3::Response> {
|
||||
if body.user_id != body.sender_user()
|
||||
&& !(body.appservice_info.is_some()
|
||||
|| services.admin.user_is_admin(body.sender_user()).await)
|
||||
if body.user_id != body.identity.expect_sender_user()?
|
||||
&& !(body.identity.is_appservice()
|
||||
|| services
|
||||
.admin
|
||||
.user_is_admin(body.identity.expect_sender_user()?)
|
||||
.await)
|
||||
{
|
||||
return Err!(Request(Forbidden("You may not change other users' profile data.")));
|
||||
}
|
||||
@@ -62,8 +77,13 @@ pub(crate) async fn set_profile_field_route(
|
||||
return Err!(Request(InvalidParam("You may not change a remote user's profile data.")));
|
||||
}
|
||||
|
||||
set_profile_field(&services, &body.user_id, ProfileFieldChange::Set(body.value.clone()))
|
||||
.await?;
|
||||
set_profile_field(
|
||||
&services,
|
||||
&body.user_id,
|
||||
ProfileFieldChange::Set(body.value.clone()),
|
||||
body.propagate_to.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(set_profile_field::v3::Response::new())
|
||||
}
|
||||
@@ -72,9 +92,12 @@ pub(crate) async fn delete_profile_field_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_profile_field::v3::Request>,
|
||||
) -> Result<delete_profile_field::v3::Response> {
|
||||
if body.user_id != body.sender_user()
|
||||
&& !(body.appservice_info.is_some()
|
||||
|| services.admin.user_is_admin(body.sender_user()).await)
|
||||
if body.user_id != body.identity.expect_sender_user()?
|
||||
&& !(body.identity.is_appservice()
|
||||
|| services
|
||||
.admin
|
||||
.user_is_admin(body.identity.expect_sender_user()?)
|
||||
.await)
|
||||
{
|
||||
return Err!(Request(Forbidden("You may not change other users' profile data.")));
|
||||
}
|
||||
@@ -83,8 +106,13 @@ pub(crate) async fn delete_profile_field_route(
|
||||
return Err!(Request(InvalidParam("You may not change a remote user's profile data.")));
|
||||
}
|
||||
|
||||
set_profile_field(&services, &body.user_id, ProfileFieldChange::Delete(body.field.clone()))
|
||||
.await?;
|
||||
set_profile_field(
|
||||
&services,
|
||||
&body.user_id,
|
||||
ProfileFieldChange::Delete(body.field.clone()),
|
||||
body.propagate_to.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(delete_profile_field::v3::Response::new())
|
||||
}
|
||||
@@ -119,7 +147,13 @@ async fn fetch_full_profile(
|
||||
continue;
|
||||
};
|
||||
|
||||
let _ = set_profile_field(services, user_id, ProfileFieldChange::Set(value)).await;
|
||||
let _ = set_profile_field(
|
||||
services,
|
||||
user_id,
|
||||
ProfileFieldChange::Set(value),
|
||||
PropagateTo::None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Some(BTreeMap::from_iter(response))
|
||||
@@ -153,8 +187,13 @@ async fn fetch_profile_field(
|
||||
|
||||
if let Some(value) = response.get(field.as_str()).map(ToOwned::to_owned) {
|
||||
if let Ok(value) = ProfileFieldValue::new(field.as_str(), value) {
|
||||
let _ = set_profile_field(services, user_id, ProfileFieldChange::Set(value.clone()))
|
||||
.await;
|
||||
let _ = set_profile_field(
|
||||
services,
|
||||
user_id,
|
||||
ProfileFieldChange::Set(value.clone()),
|
||||
PropagateTo::None,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(Some(value))
|
||||
} else {
|
||||
@@ -163,7 +202,13 @@ async fn fetch_profile_field(
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
let _ = set_profile_field(services, user_id, ProfileFieldChange::Delete(field)).await;
|
||||
let _ = set_profile_field(
|
||||
services,
|
||||
user_id,
|
||||
ProfileFieldChange::Delete(field),
|
||||
PropagateTo::None,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
@@ -256,6 +301,7 @@ async fn set_profile_field(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
change: ProfileFieldChange,
|
||||
propagate_to: PropagateTo,
|
||||
) -> Result<()> {
|
||||
const MAX_KEY_LENGTH_BYTES: usize = 255;
|
||||
const MAX_PROFILE_LENGTH_BYTES: usize = 65536;
|
||||
@@ -303,6 +349,91 @@ async fn set_profile_field(
|
||||
}
|
||||
}
|
||||
|
||||
// If the user is local and changed their displayname or avatar_url, update it
|
||||
// in all their joined rooms. This is done before updating their profile data
|
||||
// so we can check the old value of the field if `propagate_to` is `unchanged`.
|
||||
if matches!(field_name, ProfileFieldName::AvatarUrl | ProfileFieldName::DisplayName)
|
||||
&& matches!(propagate_to, PropagateTo::All | PropagateTo::Unchanged)
|
||||
&& services.globals.user_is_local(user_id)
|
||||
{
|
||||
let current_displayname = services.users.displayname(user_id).await.ok();
|
||||
let current_avatar_url = services.users.avatar_url(user_id).await.ok();
|
||||
|
||||
let mut all_joined_rooms = services.rooms.state_cache.rooms_joined(user_id);
|
||||
|
||||
while let Some(room_id) = all_joined_rooms.next().await {
|
||||
// TODO: this clobbers any custom fields on the event content
|
||||
let mut current_membership = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&room_id, user_id)
|
||||
.await
|
||||
.expect("should be able to fetch membership event for joined room");
|
||||
|
||||
assert_eq!(
|
||||
current_membership.membership,
|
||||
MembershipState::Join,
|
||||
"user should be joined"
|
||||
);
|
||||
|
||||
// If `propagate_to` is `unchanged`, and the current value of the field we're
|
||||
// updating was changed from its global value in this room, skip it.
|
||||
if matches!(propagate_to, PropagateTo::Unchanged) {
|
||||
let field_changed_from_global = match field_name {
|
||||
| ProfileFieldName::AvatarUrl =>
|
||||
current_membership.avatar_url.as_ref() != current_avatar_url.as_ref(),
|
||||
| ProfileFieldName::DisplayName =>
|
||||
current_membership.displayname.as_ref() != current_displayname.as_ref(),
|
||||
| _ => unreachable!(),
|
||||
};
|
||||
|
||||
if field_changed_from_global {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id.as_str()).await;
|
||||
|
||||
// Preserve keys in accordance with the key copying rules
|
||||
current_membership.reason = None;
|
||||
current_membership.join_authorized_via_users_server = None;
|
||||
match &change {
|
||||
| ProfileFieldChange::Set(ProfileFieldValue::AvatarUrl(avatar_url)) => {
|
||||
current_membership.avatar_url = Some(avatar_url.clone());
|
||||
},
|
||||
| ProfileFieldChange::Set(ProfileFieldValue::DisplayName(displayname)) => {
|
||||
current_membership.displayname = Some(displayname.clone());
|
||||
},
|
||||
| ProfileFieldChange::Delete(ProfileFieldName::AvatarUrl) => {
|
||||
current_membership.avatar_url = None;
|
||||
},
|
||||
| ProfileFieldChange::Delete(ProfileFieldName::DisplayName) => {
|
||||
current_membership.displayname = None;
|
||||
},
|
||||
| _ => unreachable!(),
|
||||
}
|
||||
|
||||
let _ = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu::state(user_id.to_string(), ¤t_membership),
|
||||
user_id,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
// Send a presence EDU to indicate the profile changed
|
||||
let _ = services
|
||||
.presence
|
||||
.ping_presence(user_id, &PresenceState::Online)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
match change {
|
||||
| ProfileFieldChange::Set(ProfileFieldValue::DisplayName(displayname)) => {
|
||||
services
|
||||
@@ -326,42 +457,5 @@ async fn set_profile_field(
|
||||
.set_profile_key(user_id, other.field_name().as_str(), other.value()),
|
||||
}
|
||||
|
||||
// If the user is local and changed their displayname or avatar_url, update it
|
||||
// in all their joined rooms
|
||||
if matches!(field_name, ProfileFieldName::AvatarUrl | ProfileFieldName::DisplayName)
|
||||
&& services.users.is_active_local(user_id).await
|
||||
{
|
||||
let displayname = services.users.displayname(user_id).await.ok();
|
||||
let avatar_url = services.users.avatar_url(user_id).await.ok();
|
||||
let membership_content = assign!(
|
||||
RoomMemberEventContent::new(MembershipState::Join), { displayname, avatar_url }
|
||||
);
|
||||
|
||||
let mut all_joined_rooms = services.rooms.state_cache.rooms_joined(user_id);
|
||||
|
||||
while let Some(room_id) = all_joined_rooms.next().await {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id.as_str()).await;
|
||||
|
||||
let _ = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu::state(user_id.to_string(), &membership_content),
|
||||
user_id,
|
||||
Some(&room_id),
|
||||
&state_lock,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
// Send a presence EDU to indicate the profile changed
|
||||
let _ = services
|
||||
.presence
|
||||
.ping_presence(user_id, &PresenceState::Online)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+13
-12
@@ -30,7 +30,7 @@ pub(crate) async fn get_pushrules_all_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushrules_all::v3::Request>,
|
||||
) -> Result<get_pushrules_all::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let Some(content_value) = services
|
||||
.account_data
|
||||
@@ -101,7 +101,7 @@ pub(crate) async fn get_pushrules_global_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushrules_global_scope::v3::Request>,
|
||||
) -> Result<get_pushrules_global_scope::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let Some(content_value) = services
|
||||
.account_data
|
||||
@@ -189,7 +189,7 @@ pub(crate) async fn get_pushrule_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushrule::v3::Request>,
|
||||
) -> Result<get_pushrule::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
// remove old deprecated mentions push rules as per MSC4210
|
||||
#[allow(deprecated)]
|
||||
@@ -226,7 +226,7 @@ pub(crate) async fn set_pushrule_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_pushrule::v3::Request>,
|
||||
) -> Result<set_pushrule::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let body = &body.body;
|
||||
let mut account_data: PushRulesEvent = services
|
||||
.account_data
|
||||
@@ -282,7 +282,7 @@ pub(crate) async fn get_pushrule_actions_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushrule_actions::v3::Request>,
|
||||
) -> Result<get_pushrule_actions::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
// remove old deprecated mentions push rules as per MSC4210
|
||||
#[allow(deprecated)]
|
||||
@@ -316,7 +316,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_pushrule_actions::v3::Request>,
|
||||
) -> Result<set_pushrule_actions::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let mut account_data: PushRulesEvent = services
|
||||
.account_data
|
||||
@@ -349,7 +349,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushrule_enabled::v3::Request>,
|
||||
) -> Result<get_pushrule_enabled::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
// remove old deprecated mentions push rules as per MSC4210
|
||||
#[allow(deprecated)]
|
||||
@@ -383,7 +383,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_pushrule_enabled::v3::Request>,
|
||||
) -> Result<set_pushrule_enabled::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let mut account_data: PushRulesEvent = services
|
||||
.account_data
|
||||
@@ -416,7 +416,7 @@ pub(crate) async fn delete_pushrule_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_pushrule::v3::Request>,
|
||||
) -> Result<delete_pushrule::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
let mut account_data: PushRulesEvent = services
|
||||
.account_data
|
||||
@@ -458,7 +458,7 @@ pub(crate) async fn get_pushers_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushers::v3::Request>,
|
||||
) -> Result<get_pushers::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
Ok(get_pushers::v3::Response::new(services.pusher.get_pushers(sender_user).await))
|
||||
}
|
||||
@@ -472,11 +472,12 @@ pub(crate) async fn set_pushers_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_pusher::v3::Request>,
|
||||
) -> Result<set_pusher::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let sender_device = body.identity.expect_sender_device()?;
|
||||
|
||||
services
|
||||
.pusher
|
||||
.set_pusher(sender_user, body.sender_device(), &body.action)
|
||||
.set_pusher(sender_user, sender_device, &body.action)
|
||||
.await?;
|
||||
|
||||
Ok(set_pusher::v3::Response::new())
|
||||
|
||||
@@ -26,7 +26,7 @@ pub(crate) async fn set_read_marker_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_read_marker::v3::Request>,
|
||||
) -> Result<set_read_marker::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if let Some(event) = &body.fully_read {
|
||||
let fully_read_event = FullyReadEvent::new(FullyReadEventContent::new(event.to_owned()));
|
||||
@@ -118,10 +118,11 @@ pub(crate) async fn create_receipt_route(
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<create_receipt::v3::Request>,
|
||||
) -> Result<create_receipt::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
services
|
||||
.users
|
||||
.update_device_last_seen(sender_user, body.sender_device.as_deref(), client_ip)
|
||||
.update_device_last_seen(sender_user, body.identity.sender_device(), client_ip)
|
||||
.await;
|
||||
|
||||
if matches!(
|
||||
|
||||
@@ -17,10 +17,10 @@ pub(crate) async fn redact_event_route(
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<redact_event::v3::Request>,
|
||||
) -> Result<redact_event::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
services
|
||||
.users
|
||||
.update_device_last_seen(sender_user, body.sender_device.as_deref(), client_ip)
|
||||
.update_device_last_seen(sender_user, body.identity.sender_device(), client_ip)
|
||||
.await;
|
||||
let body = &body.body;
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
|
||||
@@ -28,7 +28,7 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
|
||||
paginate_relations_with_filter(
|
||||
&services,
|
||||
body.sender_user(),
|
||||
body.identity.expect_sender_user()?,
|
||||
&body.room_id,
|
||||
&body.event_id,
|
||||
body.event_type.clone().into(),
|
||||
@@ -56,7 +56,7 @@ pub(crate) async fn get_relating_events_with_rel_type_route(
|
||||
) -> Result<get_relating_events_with_rel_type::v1::Response> {
|
||||
paginate_relations_with_filter(
|
||||
&services,
|
||||
body.sender_user(),
|
||||
body.identity.expect_sender_user()?,
|
||||
&body.room_id,
|
||||
&body.event_id,
|
||||
None,
|
||||
@@ -84,7 +84,7 @@ pub(crate) async fn get_relating_events_route(
|
||||
) -> Result<get_relating_events::v1::Response> {
|
||||
paginate_relations_with_filter(
|
||||
&services,
|
||||
body.sender_user(),
|
||||
body.identity.expect_sender_user()?,
|
||||
&body.room_id,
|
||||
&body.event_id,
|
||||
None,
|
||||
|
||||
@@ -36,7 +36,7 @@ pub(crate) async fn report_room_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<report_room::v3::Request>,
|
||||
) -> Result<report_room::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
@@ -92,7 +92,7 @@ pub(crate) async fn report_event_route(
|
||||
body: Ruma<report_content::v3::Request>,
|
||||
) -> Result<report_content::v3::Response> {
|
||||
// user authentication
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
@@ -135,8 +135,8 @@ pub(crate) async fn report_user_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<report_user::v3::Request>,
|
||||
) -> Result<report_user::v3::Response> {
|
||||
// user authentication
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub(crate) async fn get_room_aliases_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<aliases::v3::Request>,
|
||||
) -> Result<aliases::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
name::RoomNameEventContent,
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
server_acl::RoomServerAclEventContent,
|
||||
topic::RoomTopicEventContent,
|
||||
},
|
||||
},
|
||||
@@ -60,10 +61,10 @@ pub(crate) async fn create_room_route(
|
||||
) -> Result<create_room::v3::Response> {
|
||||
use create_room::v3::RoomPreset;
|
||||
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if !services.globals.allow_room_creation()
|
||||
&& body.appservice_info.is_none()
|
||||
&& !body.identity.is_appservice()
|
||||
&& !services.users.is_admin(sender_user).await
|
||||
{
|
||||
return Err!(Request(Forbidden("Room creation has been disabled.",)));
|
||||
@@ -129,7 +130,7 @@ pub(crate) async fn create_room_route(
|
||||
if body.visibility == room::Visibility::Public
|
||||
&& services.server.config.lockdown_public_room_directory
|
||||
&& !services.users.is_admin(sender_user).await
|
||||
&& body.appservice_info.is_none()
|
||||
&& !body.identity.is_appservice()
|
||||
{
|
||||
warn!(
|
||||
"Non-admin user {sender_user} tried to publish {room_id:?} to the room directory \
|
||||
@@ -185,7 +186,7 @@ pub(crate) async fn create_room_route(
|
||||
|
||||
let alias: Option<OwnedRoomAliasId> = match body.room_alias_name.as_ref() {
|
||||
| Some(alias) =>
|
||||
Some(room_alias_check(&services, alias, body.appservice_info.as_ref()).await?),
|
||||
Some(room_alias_check(&services, alias, body.identity.appservice_info()).await?),
|
||||
| _ => None,
|
||||
};
|
||||
|
||||
@@ -477,7 +478,32 @@ pub(crate) async fn create_room_route(
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
// 6. Events listed in initial_state
|
||||
// 6. Initial state events provided by the homeserver
|
||||
|
||||
let mut server_initial_state: Vec<PartialPdu> = Vec::new();
|
||||
|
||||
if let Some(allow_list) = services.server.config.default_room_acl_allow.clone() {
|
||||
server_initial_state.push(PartialPdu::state(
|
||||
String::new(),
|
||||
&RoomServerAclEventContent::new(true, allow_list, vec![]),
|
||||
));
|
||||
} else if let Some(deny_list) = services.server.config.default_room_acl_deny.clone() {
|
||||
server_initial_state.push(PartialPdu::state(
|
||||
String::new(),
|
||||
&RoomServerAclEventContent::new(true, vec!["*".to_owned()], deny_list),
|
||||
));
|
||||
}
|
||||
|
||||
for pdu in server_initial_state {
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(pdu, sender_user, Some(&room_id), &state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
// 7. Events listed in initial_state
|
||||
for event in &body.initial_state {
|
||||
let mut partial_pdu = event
|
||||
.deserialize_as_unchecked::<PartialPdu>()
|
||||
@@ -505,7 +531,7 @@ pub(crate) async fn create_room_route(
|
||||
.await?;
|
||||
}
|
||||
|
||||
// 7. Events implied by name and topic
|
||||
// 8. Events implied by name and topic
|
||||
if let Some(name) = &body.name {
|
||||
services
|
||||
.rooms
|
||||
@@ -534,7 +560,7 @@ pub(crate) async fn create_room_route(
|
||||
.await?;
|
||||
}
|
||||
|
||||
// 8. Events implied by invite (and TODO: invite_3pid)
|
||||
// 9. Events implied by invite (and TODO: invite_3pid)
|
||||
drop(state_lock);
|
||||
for recipient_user in &invitees {
|
||||
if let Err(e) =
|
||||
@@ -560,10 +586,7 @@ pub(crate) async fn create_room_route(
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"{sender_user} made {} public to the room directory",
|
||||
&room_id
|
||||
))
|
||||
.send_text(&format!("{sender_user} made {room_id} public to the room directory"))
|
||||
.await;
|
||||
}
|
||||
info!("{sender_user} made {0} public to the room directory", &room_id);
|
||||
|
||||
@@ -12,6 +12,7 @@ pub(crate) async fn get_room_event_route(
|
||||
State(ref services): State<crate::State>,
|
||||
ref body: Ruma<get_room_event::v3::Request>,
|
||||
) -> Result<get_room_event::v3::Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let event_id = &body.event_id;
|
||||
let room_id = &body.room_id;
|
||||
|
||||
@@ -24,25 +25,25 @@ pub(crate) async fn get_room_event_route(
|
||||
let visible = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(body.sender_user(), room_id, event_id)
|
||||
.user_can_see_event(sender_user, room_id, event_id)
|
||||
.map(Ok);
|
||||
|
||||
let (mut event, visible) = try_join(event, visible).await?;
|
||||
|
||||
if !visible || is_ignored_pdu(services, &event, body.sender_user()).await? {
|
||||
if !visible || is_ignored_pdu(services, &event, sender_user).await? {
|
||||
return Err!(Request(Forbidden("You don't have permission to view this event.")));
|
||||
}
|
||||
|
||||
if let Err(e) = services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.add_bundled_aggregations_to_pdu(body.sender_user(), &mut event)
|
||||
.add_bundled_aggregations_to_pdu(sender_user, &mut event)
|
||||
.await
|
||||
{
|
||||
debug_warn!("Failed to add bundled aggregations to event: {e}");
|
||||
}
|
||||
|
||||
event.set_unsigned(body.sender_user.as_deref());
|
||||
event.set_unsigned(Some(body.identity.expect_sender_user()?));
|
||||
|
||||
Ok(get_room_event::v3::Response::new(event.into_format()))
|
||||
}
|
||||
|
||||
@@ -17,12 +17,13 @@ pub(crate) async fn room_initial_sync_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<Request>,
|
||||
) -> Result<Response> {
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let room_id = &body.room_id;
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(body.sender_user(), room_id)
|
||||
.user_can_see_state_events(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Forbidden("No room preview available.")));
|
||||
@@ -31,7 +32,7 @@ pub(crate) async fn room_initial_sync_route(
|
||||
let membership = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.user_membership(body.sender_user(), room_id)
|
||||
.user_membership(sender_user, room_id)
|
||||
.map(Ok);
|
||||
|
||||
let visibility = services.rooms.directory.visibility(room_id).map(Ok);
|
||||
@@ -52,16 +53,14 @@ pub(crate) async fn room_initial_sync_route(
|
||||
.pdus_rev(room_id, None)
|
||||
.try_take(limit)
|
||||
.and_then(async |mut pdu| {
|
||||
pdu.1.set_unsigned(body.sender_user.as_deref());
|
||||
if let Some(sender_user) = body.sender_user.as_deref() {
|
||||
if let Err(e) = services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.add_bundled_aggregations_to_pdu(sender_user, &mut pdu.1)
|
||||
.await
|
||||
{
|
||||
debug_warn!("Failed to add bundled aggregations: {e}");
|
||||
}
|
||||
pdu.1.set_unsigned(Some(sender_user));
|
||||
if let Err(e) = services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.add_bundled_aggregations_to_pdu(sender_user, &mut pdu.1)
|
||||
.await
|
||||
{
|
||||
debug_warn!("Failed to add bundled aggregations: {e}");
|
||||
}
|
||||
Ok(pdu)
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use ruma::api::client::room::get_summary;
|
||||
use service::rooms::summary::Accessibility;
|
||||
|
||||
use crate::Ruma;
|
||||
use crate::{Ruma, router::ClientIdentity};
|
||||
|
||||
/// # `GET /_matrix/client/v1/room_summary/{roomIdOrAlias}`
|
||||
///
|
||||
@@ -28,7 +28,14 @@ pub(crate) async fn get_room_summary(
|
||||
let summary = services
|
||||
.rooms
|
||||
.summary
|
||||
.get_room_summary_for_user(body.sender_user.as_deref(), &room_id, &servers)
|
||||
.get_room_summary_for_user(
|
||||
body.identity
|
||||
.as_ref()
|
||||
.map(ClientIdentity::expect_sender_user)
|
||||
.transpose()?,
|
||||
&room_id,
|
||||
&servers,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match summary {
|
||||
|
||||
+419
-271
@@ -2,47 +2,267 @@
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Error, Event, Result, debug, err, info,
|
||||
Err, Error, Event, Result, debug,
|
||||
debug::DebugInspect,
|
||||
err, error,
|
||||
info::room_version::UNSTABLE_ROOM_VERSIONS,
|
||||
matrix::{StateKey, pdu::PartialPdu},
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, RoomId, RoomVersionId,
|
||||
OwnedEventId, OwnedRoomId, RoomId, UserId,
|
||||
api::{client::room::upgrade_room, error::ErrorKind},
|
||||
assign,
|
||||
events::{
|
||||
StateEventType, TimelineEventType,
|
||||
StateEventType,
|
||||
room::{
|
||||
create::PreviousRoom,
|
||||
create::{PreviousRoom, RoomCreateEventContent},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
tombstone::RoomTombstoneEventContent,
|
||||
},
|
||||
space::child::{RedactedSpaceChildEventContent, SpaceChildEventContent},
|
||||
space::{child::SpaceChildEventContent, parent::SpaceParentEventContent},
|
||||
},
|
||||
int,
|
||||
room_version_rules::RoomIdFormatVersion,
|
||||
};
|
||||
use serde_json::{json, value::to_raw_value};
|
||||
use serde_json::value::to_raw_value;
|
||||
|
||||
use crate::router::Ruma;
|
||||
|
||||
/// Recommended transferable state events list from the spec
|
||||
const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 11] = &[
|
||||
StateEventType::RoomAvatar,
|
||||
StateEventType::RoomServerAcl,
|
||||
StateEventType::RoomEncryption,
|
||||
StateEventType::RoomName,
|
||||
StateEventType::RoomAvatar,
|
||||
StateEventType::RoomTopic,
|
||||
StateEventType::RoomGuestAccess,
|
||||
StateEventType::RoomHistoryVisibility,
|
||||
StateEventType::RoomJoinRules,
|
||||
StateEventType::RoomName,
|
||||
StateEventType::RoomPowerLevels,
|
||||
StateEventType::RoomServerAcl,
|
||||
StateEventType::RoomTopic,
|
||||
// Not explicitly recommended in spec, but very useful.
|
||||
StateEventType::SpaceParent,
|
||||
StateEventType::SpaceChild,
|
||||
StateEventType::SpaceParent, // TODO: m.room.policy?
|
||||
];
|
||||
|
||||
/// Updates spaces that are marked as parents of old_room_id to instead point to
|
||||
/// the new room ID.
|
||||
///
|
||||
/// See: https://github.com/matrix-org/matrix-spec-proposals/pull/4168
|
||||
async fn update_parents(
|
||||
services: &crate::State,
|
||||
sender: &UserId,
|
||||
old_room_id: &RoomId,
|
||||
new_room_id: &RoomId,
|
||||
) -> Result {
|
||||
// Fetch the spaces which this room claims are its parents.
|
||||
|
||||
// In rooms that reference the old room via m.space.child events...
|
||||
let parents = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_keys(old_room_id, &StateEventType::SpaceParent)
|
||||
.await
|
||||
.debug_inspect(|k| debug!(?old_room_id, "Parents: {k:?}"))?;
|
||||
|
||||
for raw_parent_id in parents {
|
||||
let parent_id = RoomId::parse(&raw_parent_id)?;
|
||||
if !services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender, &parent_id)
|
||||
.await
|
||||
{
|
||||
debug!(%parent_id, "Skipping space as sender is not joined");
|
||||
continue; // Skip updating rooms the sender isn't in.
|
||||
}
|
||||
let state_lock = services.rooms.state.mutex.lock(parent_id.as_str()).await;
|
||||
// We're now fetching state from the *space* that has the old room as a *child*.
|
||||
// Follow along. This will be on the test.
|
||||
let Ok(child) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<SpaceChildEventContent>(
|
||||
&parent_id,
|
||||
&StateEventType::SpaceChild,
|
||||
old_room_id.as_str(),
|
||||
)
|
||||
.await
|
||||
.debug_inspect_err(|e| {
|
||||
error!(
|
||||
?parent_id,
|
||||
old_room_id=?old_room_id,
|
||||
new_room_id=?new_room_id,
|
||||
%e,
|
||||
"failed to fetch m.space.child from parent"
|
||||
);
|
||||
})
|
||||
else {
|
||||
// If the space does not have a child event for this room, we can skip it
|
||||
continue;
|
||||
};
|
||||
|
||||
// ...the upgrading server SHOULD send a new m.space.child event with state_key
|
||||
// set to the new room's ID, copying the order and suggested fields from the
|
||||
// content of the m.space.child with state_key of the previous room ID.
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu::state(
|
||||
new_room_id.as_str(),
|
||||
&assign!(
|
||||
SpaceChildEventContent::new(vec![sender.server_name().to_owned()]),
|
||||
{
|
||||
order: child.order,
|
||||
suggested: child.suggested,
|
||||
}
|
||||
),
|
||||
),
|
||||
sender,
|
||||
Some(&parent_id),
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
.debug_inspect_err(|e| {
|
||||
error!(
|
||||
?parent_id,
|
||||
old_room_id=?old_room_id,
|
||||
new_room_id=?new_room_id,
|
||||
%e,
|
||||
"failed to send m.space.child to parent during room upgrade"
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
drop(state_lock);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If the room being upgraded is a space, replace all m.space.parent references
|
||||
/// in its children to point at the newly upgraded room ID, so that they point
|
||||
/// at the new space.
|
||||
///
|
||||
/// See: https://github.com/matrix-org/matrix-spec-proposals/pull/4168
|
||||
async fn update_children(
|
||||
services: &crate::State,
|
||||
sender: &UserId,
|
||||
old_room_id: &RoomId,
|
||||
new_room_id: &RoomId,
|
||||
) -> Result {
|
||||
// Fetch the children of this space.
|
||||
// Note that this might not actually be a space, but just a room that has
|
||||
// children.
|
||||
|
||||
// In rooms that reference the old room via m.space.parent events...
|
||||
// NOTE: Doing that would be expensive. We'll instead fetch rooms which the
|
||||
// space claims are children.
|
||||
let parents = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_keys(old_room_id, &StateEventType::SpaceChild)
|
||||
.await
|
||||
.debug_inspect(|k| debug!(?old_room_id, "Children: {k:?}"))?;
|
||||
|
||||
for raw_child_id in parents {
|
||||
let child_id = RoomId::parse(&raw_child_id)?;
|
||||
if !services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender, &child_id)
|
||||
.await
|
||||
{
|
||||
debug!(%child_id, "Skipping child room as sender is not joined");
|
||||
continue;
|
||||
}
|
||||
let state_lock = services.rooms.state.mutex.lock(child_id.as_str()).await;
|
||||
// We're now fetching state from the *child* that has the old space as a
|
||||
// *parent*. Follow along. This will also be on the test.
|
||||
let Ok(ref parent) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<SpaceParentEventContent>(
|
||||
&child_id,
|
||||
&StateEventType::SpaceParent,
|
||||
old_room_id.as_str(),
|
||||
)
|
||||
.await
|
||||
.debug_inspect_err(|e| {
|
||||
error!(
|
||||
?child_id,
|
||||
old_room_id=?old_room_id,
|
||||
new_room_id=?new_room_id,
|
||||
%e,
|
||||
"failed to fetch m.space.parent from child"
|
||||
);
|
||||
})
|
||||
else {
|
||||
// If the child does not have a parent event for this room, we can skip it.
|
||||
continue;
|
||||
};
|
||||
|
||||
// ... the upgrading server SHOULD send a new m.space.parent event with
|
||||
// state_key set to the new room's ID.
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu::state(
|
||||
new_room_id.as_str(),
|
||||
&assign!(SpaceParentEventContent::new(vec![sender.server_name().to_owned()]), { canonical: parent.canonical }),
|
||||
),
|
||||
sender,
|
||||
Some(&child_id),
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
.debug_inspect_err(|e| error!(
|
||||
child_id=?child_id,
|
||||
old_room_id=?old_room_id,
|
||||
new_room_id=?new_room_id,
|
||||
%e,
|
||||
"failed to send updated m.space.parent to child during room upgrade"
|
||||
))
|
||||
.ok();
|
||||
|
||||
// If the previous m.space.parent event has canonical set to true in content,
|
||||
// homeservers SHOULD update the old state event to set canonical to false,
|
||||
// while setting it to true in the newly-sent m.space.parent event.
|
||||
if parent.canonical {
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu::state(
|
||||
old_room_id.as_str(),
|
||||
&assign!(parent.clone(), {canonical: false}),
|
||||
),
|
||||
sender,
|
||||
Some(&child_id),
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
.debug_inspect_err(|e| {
|
||||
error!(
|
||||
child_id=?child_id,
|
||||
old_room_id=?old_room_id,
|
||||
new_room_id=?new_room_id,
|
||||
%e,
|
||||
"failed to send non-canonical m.space.parent to child room"
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
drop(state_lock);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/upgrade`
|
||||
///
|
||||
/// Upgrades the room.
|
||||
@@ -57,10 +277,14 @@ pub(crate) async fn upgrade_room_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<upgrade_room::v3::Request>,
|
||||
) -> Result<upgrade_room::v3::Response> {
|
||||
// TODO[v12]: Handle additional creators
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if !services.server.supported_room_version(&body.new_version) {
|
||||
let (supported, forbid_unstable, is_unstable) = (
|
||||
services.server.supported_room_version(&body.new_version),
|
||||
!services.config.allow_unstable_room_versions,
|
||||
UNSTABLE_ROOM_VERSIONS.contains(&body.new_version),
|
||||
);
|
||||
if !supported || (forbid_unstable && is_unstable) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::UnsupportedRoomVersion,
|
||||
"This server does not support that room version.",
|
||||
@@ -77,17 +301,15 @@ pub(crate) async fn upgrade_room_route(
|
||||
return Err!(Request(Forbidden("Upgrading the admin room this way is not allowed.")));
|
||||
}
|
||||
|
||||
// First, check if the user has permission to upgrade the room (send tombstone
|
||||
// event)
|
||||
// 1. Check that the user has permission to send m.room.tombstone events in the
|
||||
// room.
|
||||
let old_room_state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
|
||||
|
||||
// Check tombstone permission by attempting to create (but not send) the event
|
||||
// Note that this does internally call the policy server with a fake room ID,
|
||||
// which may not be good?
|
||||
let tombstone_test_result = services
|
||||
// Check tombstone permission by attempting to create (but not send) the event.
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
.create_event(
|
||||
PartialPdu::state(
|
||||
StateKey::new(),
|
||||
&RoomTombstoneEventContent::new(
|
||||
@@ -99,157 +321,104 @@ pub(crate) async fn upgrade_room_route(
|
||||
Some(&body.room_id),
|
||||
&old_room_state_lock,
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Err(_e) = tombstone_test_result {
|
||||
return Err!(Request(Forbidden("User does not have permission to upgrade this room.")));
|
||||
}
|
||||
|
||||
drop(old_room_state_lock);
|
||||
.await
|
||||
.map_err(|_| {
|
||||
err!(Request(Forbidden("You do not have permission to upgrade this room.")))
|
||||
})?;
|
||||
|
||||
// Create a replacement room
|
||||
let room_version_rules = body
|
||||
let new_version_rules = body
|
||||
.new_version
|
||||
.rules()
|
||||
.expect("new room version should have defined rules");
|
||||
let replacement_room_owned = if room_version_rules.room_id_format == RoomIdFormatVersion::V2 {
|
||||
Some(RoomId::new_v1(services.globals.server_name()))
|
||||
} else {
|
||||
|
||||
let last_event = if new_version_rules
|
||||
.authorization
|
||||
.room_create_event_id_as_room_id
|
||||
{
|
||||
None
|
||||
};
|
||||
let replacement_room: Option<&RoomId> = replacement_room_owned.as_ref().map(AsRef::as_ref);
|
||||
let replacement_room_tmp = match replacement_room {
|
||||
| Some(v) => v,
|
||||
| None => &RoomId::new_v1(services.globals.server_name()),
|
||||
};
|
||||
|
||||
let _short_id = services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(replacement_room_tmp)
|
||||
.await;
|
||||
|
||||
// For pre-v12 rooms, send tombstone before creating replacement room
|
||||
let tombstone_event_id = if room_version_rules.room_id_format != RoomIdFormatVersion::V2 {
|
||||
let state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
|
||||
// Send a m.room.tombstone event to the old room to indicate that it is not
|
||||
// intended to be used any further
|
||||
let tombstone_event_id = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu::state(
|
||||
StateKey::new(),
|
||||
&RoomTombstoneEventContent::new(
|
||||
"This room has been replaced".to_owned(),
|
||||
replacement_room.unwrap().to_owned(),
|
||||
),
|
||||
),
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
// Change lock to replacement room
|
||||
drop(state_lock);
|
||||
Some(tombstone_event_id)
|
||||
} else {
|
||||
None
|
||||
Some(
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.get_forward_extremities(&body.room_id)
|
||||
.collect::<Vec<OwnedEventId>>()
|
||||
.await[0]
|
||||
.clone(),
|
||||
)
|
||||
};
|
||||
let state_lock = services
|
||||
.rooms
|
||||
.state
|
||||
.mutex
|
||||
.lock(replacement_room_tmp.as_str())
|
||||
.await;
|
||||
|
||||
// Get the old room creation event
|
||||
let mut create_event_content: CanonicalJsonObject = services
|
||||
let old_create_event: RoomCreateEventContent = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content(&body.room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.map_err(|_| err!(Database("Found room without m.room.create event.")))?;
|
||||
|
||||
// Use the m.room.tombstone event as the predecessor
|
||||
|
||||
let predecessor = {
|
||||
#[allow(deprecated, reason = "Clients still use event_id even though it's deprecated")]
|
||||
Some(assign!(PreviousRoom::new(body.room_id.clone()), {
|
||||
event_id: tombstone_event_id,
|
||||
}))
|
||||
let create_event_content = if new_version_rules.authorization.use_room_create_sender {
|
||||
RoomCreateEventContent::new_v1(sender_user.to_owned())
|
||||
} else {
|
||||
RoomCreateEventContent::new_v11()
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
let create_event_content = {
|
||||
assign!(
|
||||
create_event_content,
|
||||
{
|
||||
additional_creators: if new_version_rules.authorization.additional_room_creators {
|
||||
body.additional_creators.clone()
|
||||
} else { Vec::new() },
|
||||
creator: if new_version_rules.authorization.use_room_create_sender {
|
||||
None
|
||||
} else { Some(sender_user.to_owned()) },
|
||||
predecessor: Some(assign!(PreviousRoom::new(body.room_id.clone()), {
|
||||
event_id: last_event,
|
||||
})),
|
||||
room_type: old_create_event.room_type.clone(),
|
||||
room_version: body.new_version.clone(),
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
// Send a m.room.create event containing a predecessor field and the applicable
|
||||
// room_version
|
||||
{
|
||||
use RoomVersionId::*;
|
||||
match body.new_version {
|
||||
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||
create_event_content.insert(
|
||||
"creator".into(),
|
||||
json!(&sender_user).try_into().map_err(|e| {
|
||||
info!("Error forming creation event: {e}");
|
||||
Error::BadRequest(ErrorKind::BadJson, "Error forming creation event")
|
||||
})?,
|
||||
);
|
||||
},
|
||||
| _ => {
|
||||
// "creator" key no longer exists in V11 rooms
|
||||
create_event_content.remove("creator");
|
||||
},
|
||||
// TODO(hydra): additional_creators
|
||||
}
|
||||
}
|
||||
|
||||
create_event_content.insert(
|
||||
"room_version".into(),
|
||||
json!(&body.new_version)
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
|
||||
);
|
||||
create_event_content.insert(
|
||||
"predecessor".into(),
|
||||
json!(predecessor)
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
|
||||
);
|
||||
|
||||
// Validate creation event content
|
||||
if serde_json::from_str::<CanonicalJsonObject>(
|
||||
to_raw_value(&create_event_content)
|
||||
.expect("Error forming creation event")
|
||||
.get(),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
|
||||
}
|
||||
let replacement_room_id: Option<OwnedRoomId> =
|
||||
if new_version_rules.room_id_format == RoomIdFormatVersion::V2 {
|
||||
None
|
||||
} else {
|
||||
Some(RoomId::new_v1(services.globals.server_name()))
|
||||
};
|
||||
|
||||
let new_room_state_lock = if let Some(new_room_id) = replacement_room_id.as_ref() {
|
||||
services.rooms.state.mutex.lock(new_room_id.as_str()).await
|
||||
} else {
|
||||
// NOTE: Using a hardcoded room ID for the temporary mutex means only one room
|
||||
// can be created at a time. This is actually beneficial, as it reduces the
|
||||
// risk of concurrent in-flight collisions.
|
||||
services.rooms.state.mutex.lock("!new-room").await
|
||||
};
|
||||
debug!("Upgrading {} to room version {}", &body.room_id, &body.new_version);
|
||||
let create_event_id = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu {
|
||||
event_type: TimelineEventType::RoomCreate,
|
||||
content: to_raw_value(&create_event_content)
|
||||
.expect("event is valid, we just created it"),
|
||||
unsigned: None,
|
||||
state_key: Some(StateKey::new()),
|
||||
redacts: None,
|
||||
timestamp: None,
|
||||
},
|
||||
PartialPdu::state(StateKey::new(), &create_event_content),
|
||||
sender_user,
|
||||
replacement_room,
|
||||
&state_lock,
|
||||
replacement_room_id.as_deref(),
|
||||
&new_room_state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
let create_id = create_event_id.as_str().replace('$', "!");
|
||||
let (replacement_room, state_lock) =
|
||||
if room_version_rules.room_id_format == RoomIdFormatVersion::V2 {
|
||||
let parsed_room_id = RoomId::parse(&create_id)?;
|
||||
drop(new_room_state_lock);
|
||||
// re-acquire a new lock with the new room ID.
|
||||
// We don't actually need a state lock for sending the m.room.create event, but
|
||||
// we get one anyway because the function requires it and I can't be bothered
|
||||
// refactoring it.
|
||||
let (replacement_room_id, new_room_state_lock) =
|
||||
if new_version_rules.room_id_format == RoomIdFormatVersion::V2 {
|
||||
let parsed_room_id = RoomId::new_v2(
|
||||
create_event_id
|
||||
.as_str()
|
||||
.strip_prefix("$")
|
||||
.expect("event ID must start with $ sigil"),
|
||||
)?;
|
||||
let lock = services
|
||||
.rooms
|
||||
.state
|
||||
@@ -258,9 +427,13 @@ pub(crate) async fn upgrade_room_route(
|
||||
.await;
|
||||
(Some(parsed_room_id), lock)
|
||||
} else {
|
||||
(replacement_room.map(ToOwned::to_owned), state_lock)
|
||||
let new_room_id =
|
||||
replacement_room_id.expect("replacement room id should be known by now");
|
||||
let lock = services.rooms.state.mutex.lock(new_room_id.as_str()).await;
|
||||
(Some(new_room_id), lock)
|
||||
};
|
||||
|
||||
debug!("Upgraded {} to {}", &body.room_id, replacement_room_id.as_deref().unwrap());
|
||||
// Join the new room
|
||||
services
|
||||
.rooms
|
||||
@@ -274,13 +447,13 @@ pub(crate) async fn upgrade_room_route(
|
||||
}),
|
||||
),
|
||||
sender_user,
|
||||
replacement_room.as_deref(),
|
||||
&state_lock,
|
||||
replacement_room_id.as_deref(),
|
||||
&new_room_state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
// Replicate transferable state events to the new room
|
||||
// 3. Replicate transferable state events to the new room
|
||||
for event_type in TRANSFERABLE_STATE_EVENTS {
|
||||
let state_keys = services
|
||||
.rooms
|
||||
@@ -297,26 +470,45 @@ pub(crate) async fn upgrade_room_route(
|
||||
| Ok(v) => v.content().to_owned(),
|
||||
| Err(_) => continue, // Skipping missing events.
|
||||
};
|
||||
if event_content.get() == "{}" {
|
||||
// If the event content is empty, we skip it
|
||||
continue;
|
||||
}
|
||||
// If this is a power levels event, and the new room version has creators,
|
||||
// we need to make sure they dont appear in the users block of power levels.
|
||||
if *event_type == StateEventType::RoomPowerLevels {
|
||||
// TODO(v12): additional creators
|
||||
let creators = vec![sender_user];
|
||||
let creators = body
|
||||
.additional_creators
|
||||
.clone()
|
||||
.iter()
|
||||
.chain(std::iter::once(&sender_user.to_owned()))
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>();
|
||||
let mut power_levels_event_content: RoomPowerLevelsEventContent =
|
||||
serde_json::from_str(event_content.get()).map_err(|_| {
|
||||
err!(Request(BadJson("Power levels event content is not valid")))
|
||||
})?;
|
||||
for creator in creators {
|
||||
power_levels_event_content.users.remove(creator);
|
||||
if new_version_rules
|
||||
.authorization
|
||||
.explicitly_privilege_room_creators
|
||||
{
|
||||
power_levels_event_content.users.remove(&creator);
|
||||
} else {
|
||||
power_levels_event_content.users.insert(
|
||||
creator.clone(),
|
||||
max(
|
||||
int!(100),
|
||||
power_levels_event_content
|
||||
.users
|
||||
.get(&creator)
|
||||
.copied()
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
event_content = to_raw_value(&power_levels_event_content)
|
||||
.expect("event is valid, we just deserialized and modified it");
|
||||
}
|
||||
|
||||
debug!(%event_type, ?state_key, "Transferring state event to new room");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
@@ -328,15 +520,15 @@ pub(crate) async fn upgrade_room_route(
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
replacement_room.as_deref(),
|
||||
&state_lock,
|
||||
replacement_room_id.as_deref(),
|
||||
&new_room_state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Moves any local aliases to the new room
|
||||
// 4. Move any local aliases to the new room
|
||||
let mut local_aliases = services
|
||||
.rooms
|
||||
.alias
|
||||
@@ -344,6 +536,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
.boxed();
|
||||
|
||||
while let Some(alias) = local_aliases.next().await {
|
||||
debug!(?alias, "Migrating alias");
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
@@ -352,11 +545,31 @@ pub(crate) async fn upgrade_room_route(
|
||||
|
||||
services.rooms.alias.set_alias(
|
||||
&alias,
|
||||
replacement_room.as_ref().unwrap(),
|
||||
replacement_room_id.as_deref().unwrap(),
|
||||
sender_user,
|
||||
)?;
|
||||
}
|
||||
|
||||
// 5. Send a `m.room.tombstone` event to the old room to indicate that it is not
|
||||
// intended to be used any further.
|
||||
debug!(target=?body.room_id, "Sending tombstone to old room");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu::state(
|
||||
StateKey::new(),
|
||||
&RoomTombstoneEventContent::new(
|
||||
"This room has been replaced".to_owned(),
|
||||
replacement_room_id.clone().unwrap(),
|
||||
),
|
||||
),
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
&old_room_state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Get the old room power levels
|
||||
let mut power_levels = services
|
||||
.rooms
|
||||
@@ -378,8 +591,10 @@ pub(crate) async fn upgrade_room_route(
|
||||
power_levels.events_default = new_level;
|
||||
power_levels.invite = new_level;
|
||||
|
||||
// Modify the power levels in the old room to prevent sending of events and
|
||||
// 6. Modify the power levels in the old room to prevent sending of events and
|
||||
// inviting new users
|
||||
// Spec dictates that this is allowed to fail.
|
||||
debug!(target=?body.room_id, ?new_level, "Raising power level in old room to lock it");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
@@ -390,117 +605,50 @@ pub(crate) async fn upgrade_room_route(
|
||||
),
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
&state_lock,
|
||||
&old_room_state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
.await
|
||||
.ok();
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
// For v12 rooms, send tombstone AFTER creating replacement room
|
||||
if room_version_rules.room_id_format == RoomIdFormatVersion::V2 {
|
||||
let old_room_state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
|
||||
// For v12 rooms, no event reference in predecessor due to cyclic dependency -
|
||||
// could best effort one maybe?
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu::state(
|
||||
StateKey::new(),
|
||||
&RoomTombstoneEventContent::new(
|
||||
"This room has been replaced".to_owned(),
|
||||
replacement_room.as_ref().unwrap().to_owned(),
|
||||
),
|
||||
),
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
&old_room_state_lock,
|
||||
)
|
||||
.await?;
|
||||
drop(old_room_state_lock);
|
||||
}
|
||||
|
||||
// Check if the old room has a space parent, and if so, whether we should update
|
||||
// it (m.space.parent, room_id)
|
||||
let parents = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_keys(&body.room_id, &StateEventType::SpaceParent)
|
||||
.await?;
|
||||
|
||||
for raw_space_id in parents {
|
||||
let space_id = RoomId::parse(&raw_space_id)?;
|
||||
let Ok(child) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<SpaceChildEventContent>(
|
||||
&space_id,
|
||||
&StateEventType::SpaceChild,
|
||||
body.room_id.as_str(),
|
||||
)
|
||||
.await
|
||||
else {
|
||||
// If the space does not have a child event for this room, we can skip it
|
||||
continue;
|
||||
};
|
||||
debug!(
|
||||
"Updating space {space_id} child event for room {} to {}",
|
||||
&body.room_id,
|
||||
replacement_room.as_ref().unwrap()
|
||||
// MSC4168: Update spaces that reference this room to point at the new room.
|
||||
debug!("Updating parent spaces");
|
||||
update_parents(
|
||||
&services,
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
replacement_room_id.as_deref().unwrap(),
|
||||
)
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
error!(
|
||||
old_room_id=?body.room_id,
|
||||
new_room_id=?replacement_room_id.as_deref().unwrap(),
|
||||
%e,
|
||||
"failed to update parent spaces during room upgrade"
|
||||
);
|
||||
// First, drop the space's child event
|
||||
let state_lock = services.rooms.state.mutex.lock(space_id.as_str()).await;
|
||||
debug!("Removing space child event for room {} in space {space_id}", &body.room_id);
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu {
|
||||
event_type: StateEventType::SpaceChild.into(),
|
||||
content: to_raw_value(&RedactedSpaceChildEventContent::new())
|
||||
.expect("event is valid, we just created it"),
|
||||
state_key: Some(body.room_id.clone().as_str().into()),
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
Some(&space_id),
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
.ok();
|
||||
// Now, add a new child event for the replacement room
|
||||
debug!(
|
||||
"Adding space child event for room {} in space {space_id}",
|
||||
replacement_room.as_ref().unwrap()
|
||||
})
|
||||
.ok();
|
||||
|
||||
// MSC4168: Update child rooms to point at the new space, where possible
|
||||
debug!("Updating space children");
|
||||
update_children(
|
||||
&services,
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
replacement_room_id.as_deref().unwrap(),
|
||||
)
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
error!(
|
||||
old_room_id=?body.room_id,
|
||||
new_room_id=?replacement_room_id.as_deref().unwrap(),
|
||||
%e,
|
||||
"failed to update space children during room upgrade"
|
||||
);
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PartialPdu::state(
|
||||
replacement_room.as_ref().unwrap().as_str(),
|
||||
&assign!(SpaceChildEventContent::new(vec![sender_user.server_name().to_owned()]), {
|
||||
order: child.order,
|
||||
suggested: child.suggested,
|
||||
}),
|
||||
),
|
||||
sender_user,
|
||||
Some(&space_id),
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
.ok();
|
||||
debug!(
|
||||
"Finished updating space {space_id} child event for room {} to {}",
|
||||
&body.room_id,
|
||||
replacement_room.as_ref().unwrap()
|
||||
);
|
||||
drop(state_lock);
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
|
||||
// Return the replacement room id
|
||||
Ok(upgrade_room::v3::Response::new(replacement_room.as_ref().unwrap().to_owned()))
|
||||
Ok(upgrade_room::v3::Response::new(replacement_room_id.unwrap()))
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) async fn search_events_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<Request>,
|
||||
) -> Result<Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let next_batch = body.next_batch.as_deref();
|
||||
|
||||
let mut result_categories = ResultCategories::new();
|
||||
|
||||
@@ -22,16 +22,16 @@ pub(crate) async fn send_message_event_route(
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<send_message_event::v3::Request>,
|
||||
) -> Result<send_message_event::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_device = body.sender_device.as_deref();
|
||||
let appservice_info = body.appservice_info.as_ref();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let sender_device = body.identity.sender_device();
|
||||
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||
}
|
||||
|
||||
services
|
||||
.users
|
||||
.update_device_last_seen(sender_user, body.sender_device.as_deref(), client_ip)
|
||||
.update_device_last_seen(sender_user, sender_device, client_ip)
|
||||
.await;
|
||||
|
||||
// Forbid m.room.encrypted if encryption is disabled
|
||||
@@ -83,7 +83,11 @@ pub(crate) async fn send_message_event_route(
|
||||
event_type: body.event_type.clone().into(),
|
||||
content,
|
||||
unsigned: Some(unsigned),
|
||||
timestamp: appservice_info.and(body.timestamp),
|
||||
timestamp: if body.identity.is_appservice() {
|
||||
body.timestamp
|
||||
} else {
|
||||
None
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
|
||||
+37
-24
@@ -21,7 +21,7 @@
|
||||
},
|
||||
login::{
|
||||
self,
|
||||
v3::{DiscoveryInfo, HomeserverInfo},
|
||||
v3::{DiscoveryInfo, HomeserverInfo, LoginInfo},
|
||||
},
|
||||
logout, logout_all,
|
||||
},
|
||||
@@ -29,7 +29,6 @@
|
||||
},
|
||||
assign,
|
||||
};
|
||||
use service::uiaa::Identity;
|
||||
|
||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||
use crate::Ruma;
|
||||
@@ -44,6 +43,12 @@ pub(crate) async fn get_login_types_route(
|
||||
ClientIp(client): ClientIp,
|
||||
_body: Ruma<get_login_types::v3::Request>,
|
||||
) -> Result<get_login_types::v3::Response> {
|
||||
if !services.config.oauth.compatibility_mode.uiaa_available() {
|
||||
return Err!(Request(Unrecognized(
|
||||
"User-interactive authentication is not available on this server."
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(get_login_types::v3::Response::new(vec![
|
||||
get_login_types::v3::LoginType::Password(PasswordLoginType::default()),
|
||||
get_login_types::v3::LoginType::ApplicationService(ApplicationServiceLoginType::default()),
|
||||
@@ -53,7 +58,7 @@ pub(crate) async fn get_login_types_route(
|
||||
]))
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_login(
|
||||
pub async fn handle_login(
|
||||
services: &Services,
|
||||
identifier: Option<&UserIdentifier>,
|
||||
password: &str,
|
||||
@@ -87,10 +92,6 @@ pub(crate) async fn handle_login(
|
||||
return Err!(Request(InvalidParam("User ID does not belong to this homeserver")));
|
||||
}
|
||||
|
||||
if services.users.is_locked(&user_id).await? {
|
||||
return Err!(Request(UserLocked("This account has been locked.")));
|
||||
}
|
||||
|
||||
if services.users.is_login_disabled(&user_id).await {
|
||||
warn!(%user_id, "user attempted to log in with a login-disabled account");
|
||||
return Err!(Request(Forbidden("This account is not permitted to log in.")));
|
||||
@@ -119,19 +120,29 @@ pub(crate) async fn login_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<login::v3::Request>,
|
||||
) -> Result<login::v3::Response> {
|
||||
if !services.config.oauth.compatibility_mode.uiaa_available() {
|
||||
return match body.login_info {
|
||||
| LoginInfo::ApplicationService(_) => {
|
||||
Err!(Request(AppserviceLoginUnsupported(
|
||||
"User-interactive appservice login is not available on this server."
|
||||
)))
|
||||
},
|
||||
| _ => {
|
||||
Err!(Request(Unrecognized(
|
||||
"User-interactive authentication is not available on this server."
|
||||
)))
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let emergency_mode_enabled = services.config.emergency_password.is_some();
|
||||
|
||||
// Validate login method
|
||||
// TODO: Other login methods
|
||||
let user_id = match &body.login_info {
|
||||
#[allow(deprecated)]
|
||||
| login::v3::LoginInfo::Password(login::v3::Password {
|
||||
identifier,
|
||||
password,
|
||||
user,
|
||||
..
|
||||
}) => handle_login(&services, identifier.as_ref(), password, user.as_ref()).await?,
|
||||
| login::v3::LoginInfo::Token(login::v3::Token { token, .. }) => {
|
||||
| LoginInfo::Password(login::v3::Password { identifier, password, user, .. }) =>
|
||||
handle_login(&services, identifier.as_ref(), password, user.as_ref()).await?,
|
||||
| LoginInfo::Token(login::v3::Token { token, .. }) => {
|
||||
debug!("Got token login type");
|
||||
if !services.server.config.login_via_existing_session {
|
||||
return Err!(Request(Unknown("Token login is not enabled.")));
|
||||
@@ -139,14 +150,14 @@ pub(crate) async fn login_route(
|
||||
services.users.find_from_login_token(token).await?
|
||||
},
|
||||
#[allow(deprecated)]
|
||||
| login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService {
|
||||
| LoginInfo::ApplicationService(login::v3::ApplicationService {
|
||||
identifier,
|
||||
user,
|
||||
..
|
||||
}) => {
|
||||
debug!("Got appservice login type");
|
||||
|
||||
let Some(ref info) = body.appservice_info else {
|
||||
let Some(ref info) = body.identity else {
|
||||
return Err!(Request(MissingToken("Missing appservice token.")));
|
||||
};
|
||||
|
||||
@@ -173,7 +184,6 @@ pub(crate) async fn login_route(
|
||||
user_id
|
||||
},
|
||||
| _ => {
|
||||
debug!("/login json_body: {:?}", &body.json_body);
|
||||
return Err!(Request(Unknown(
|
||||
debug_warn!(?body.login_info, "Invalid or unsupported login type")
|
||||
)));
|
||||
@@ -203,7 +213,7 @@ pub(crate) async fn login_route(
|
||||
if device_exists {
|
||||
services
|
||||
.users
|
||||
.set_token(&user_id, &device_id, &token)
|
||||
.set_token(&user_id, &device_id, &token, None)
|
||||
.await?;
|
||||
} else {
|
||||
services
|
||||
@@ -212,6 +222,7 @@ pub(crate) async fn login_route(
|
||||
&user_id,
|
||||
&device_id,
|
||||
&token,
|
||||
None,
|
||||
body.initial_device_display_name.clone(),
|
||||
Some(client.to_string()),
|
||||
)
|
||||
@@ -250,16 +261,16 @@ pub(crate) async fn login_token_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<get_login_token::v1::Request>,
|
||||
) -> Result<get_login_token::v1::Response> {
|
||||
if !services.server.config.login_via_existing_session {
|
||||
if !services.config.login_via_existing_session {
|
||||
return Err!(Request(Forbidden("Login via an existing session is not enabled")));
|
||||
}
|
||||
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
// Prompt the user to confirm with their password using UIAA
|
||||
let _ = services
|
||||
.uiaa
|
||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
||||
.authenticate_password(&body.auth, sender_user, body.identity.sender_device(), None)
|
||||
.await?;
|
||||
|
||||
let login_token = utils::random_string(TOKEN_LENGTH);
|
||||
@@ -286,7 +297,9 @@ pub(crate) async fn logout_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<logout::v3::Request>,
|
||||
) -> Result<logout::v3::Response> {
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let sender_device = body.identity.expect_sender_device()?;
|
||||
|
||||
services
|
||||
.users
|
||||
.remove_device(sender_user, sender_device)
|
||||
@@ -332,7 +345,7 @@ pub(crate) async fn logout_all_route(
|
||||
ClientIp(client): ClientIp,
|
||||
body: Ruma<logout_all::v3::Request>,
|
||||
) -> Result<logout_all::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
services
|
||||
.users
|
||||
.all_device_ids(sender_user)
|
||||
|
||||
@@ -27,7 +27,7 @@ pub(crate) async fn get_hierarchy_route(
|
||||
.rooms
|
||||
.summary
|
||||
.get_room_hierarchy_for_user(
|
||||
body.sender_user(),
|
||||
body.identity.expect_sender_user()?,
|
||||
body.room_id.clone(),
|
||||
max_depth,
|
||||
body.suggested_only,
|
||||
|
||||
@@ -38,10 +38,10 @@ pub(crate) async fn send_state_event_for_key_route(
|
||||
ClientIp(ip): ClientIp,
|
||||
body: Ruma<send_state_event::v3::Request>,
|
||||
) -> Result<send_state_event::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
services
|
||||
.users
|
||||
.update_device_last_seen(sender_user, body.sender_device.as_deref(), ip)
|
||||
.update_device_last_seen(sender_user, body.identity.sender_device(), ip)
|
||||
.await;
|
||||
|
||||
if services.users.is_suspended(sender_user).await? {
|
||||
@@ -55,7 +55,7 @@ pub(crate) async fn send_state_event_for_key_route(
|
||||
&body.event_type,
|
||||
&body.body.body,
|
||||
&body.state_key,
|
||||
if body.appservice_info.is_some() {
|
||||
if body.identity.is_appservice() {
|
||||
body.timestamp
|
||||
} else {
|
||||
None
|
||||
@@ -91,7 +91,7 @@ pub(crate) async fn get_state_events_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_state_events::v3::Request>,
|
||||
) -> Result<get_state_events::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
@@ -125,7 +125,7 @@ pub(crate) async fn get_state_event_for_key_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_state_event_for_key::v3::Request>,
|
||||
) -> Result<get_state_event_for_key::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
},
|
||||
assign,
|
||||
events::presence::{PresenceEvent, PresenceEventContent},
|
||||
presence::PresenceState,
|
||||
serde::Raw,
|
||||
};
|
||||
use service::{
|
||||
@@ -181,10 +182,11 @@ pub(crate) async fn sync_events_route(
|
||||
ClientIp(client_ip): ClientIp,
|
||||
body: Ruma<sync_events::v3::Request>,
|
||||
) -> Result<sync_events::v3::Response> {
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
let sender_user = body.identity.expect_sender_user()?;
|
||||
let sender_device = body.identity.expect_sender_device()?;
|
||||
|
||||
// Presence update
|
||||
if services.config.allow_local_presence {
|
||||
if services.config.allow_local_presence && body.set_presence != PresenceState::Offline {
|
||||
services
|
||||
.presence
|
||||
.ping_presence(sender_user, &body.body.set_presence)
|
||||
@@ -225,7 +227,8 @@ pub(crate) async fn build_sync_events(
|
||||
services: &Services,
|
||||
body: &Ruma<sync_events::v3::Request>,
|
||||
) -> Result<sync_events::v3::Response> {
|
||||
let (syncing_user, syncing_device) = body.sender();
|
||||
let syncing_user = body.identity.sender_user().expect("should have a user");
|
||||
let syncing_device = body.identity.sender_device().expect("should have a device");
|
||||
|
||||
let current_count = services.globals.current_count()?;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user