Compare commits

..
Author SHA1 Message Date
Ginger d322c4e7d4 chore: Revert "feat: Enable direct_tls and more logging by default"
This reverts commit 5d2e840859.
2026-07-11 15:47:57 -04:00
103 changed files with 1326 additions and 1480 deletions
+1 -1
View File
@@ -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@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2
uses: https://github.com/taiki-e/install-action@50414676f9f5d50a65992c6dd2ed02641263226c # v2
with:
tool: git-warp-time,timelord-cli@3.0.1
+1 -1
View File
@@ -65,7 +65,7 @@ jobs:
path: binaries
merge-multiple: true
- name: Create Release and Upload
uses: https://github.com/softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3
uses: https://github.com/softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3
with:
draft: true
files: binaries/*
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
- name: Setup Node.js
if: steps.runner-env.outputs.node_major == '' || steps.runner-env.outputs.node_major < '20'
uses: https://github.com/actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
uses: https://github.com/actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 22
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
steps:
- name: 📦 Setup Node.js
uses: https://github.com/actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
uses: https://github.com/actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: "22"
+1 -3
View File
@@ -74,7 +74,6 @@ jobs:
CARGO_INCREMENTAL=${{ env.BUILDKIT_ENDPOINT != '' && '1' || '0' }}
TARGET_CPU=
RUST_PROFILE=release
CARGO_FEATURES=default
platforms: ${{ matrix.platform }}
labels: ${{ steps.prepare.outputs.metadata_labels }}
annotations: ${{ steps.prepare.outputs.metadata_annotations }}
@@ -162,7 +161,6 @@ jobs:
CARGO_INCREMENTAL=${{ env.BUILDKIT_ENDPOINT != '' && '1' || '0' }}
TARGET_CPU=${{ matrix.target_cpu }}
RUST_PROFILE=release-max-perf
CARGO_FEATURES=default,release_max_log_level
platforms: ${{ matrix.platform }}
labels: ${{ steps.prepare.outputs.metadata_labels }}
annotations: ${{ steps.prepare.outputs.metadata_annotations }}
@@ -218,7 +216,7 @@ jobs:
path: binaries
merge-multiple: true
- name: Create Release and Upload
uses: https://github.com/softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3
uses: https://github.com/softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3
with:
draft: true
files: binaries/*
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
name: Renovate
runs-on: ubuntu-latest
container:
image: ghcr.io/renovatebot/renovate:43.260.2@sha256:60d830108d9ff6408d11a55d5f110ee0976bed78a6b82b08211e3ddb72f72952
image: ghcr.io/renovatebot/renovate:43.252.1@sha256:121eb04ef758537019fb58f587aa53c99e5fda4e703993ce2ca01bd4b3926bd4
options: --tmpfs /tmp:exec
steps:
- name: Checkout
-24
View File
@@ -1,27 +1,3 @@
# Continuwuity 26.6.2 (2026-07-12)
## Bugfixes
- Fixed the server returning 500 errors if `admin_console_automatic` is enabled and no TTY is available. Contributed by @s1lv3r. (#1975)
- Fixed `global.oauth.compatibility_mode` being required, despite being ignored, when the `[global.oauth.oidc]` config section is provided.
- Fixed an issue with a migration that could cause user accounts imported from an identity provider to be marked as deactivated when the server started. If you have accounts affected by this issue, use `!admin users reset-password --convert-to-local-account` to reactivate them.
# Continuwuity 26.6.1 (2026-07-12)
## Features
- Added enforcement for new federated invite checks and corrected a bunch of related spec compliance issues along the way. Contributed by @nex. (#1952)
## Bugfixes
- Fixed existing accounts failing to link when logging in with OIDC if `prompt_for_localpart` was `false`. (#1942)
- Authentication is no longer required on the `/_matrix/client/v3/account/3pid/email/requestToken` endpoint. (#1953)
- Fixed newly created rooms failing to sync properly in clients using legacy sync.
- Stopped appservice users from being erroneously marked as deactivated during a 26.6 database migration.
- Whitespace will now automatically be trimmed from the start and end of the `global.oauth.oidc.client_secret_file`.
# Continuwuity 26.6.0 (2026-07-10)
## Features
+2 -2
View File
@@ -26,7 +26,7 @@ ### Pre-commit Checks
```bash
# Install prek using cargo-binstall https://github.com/cargo-bins/cargo-binstall
# Install prek using cargo-binstall
cargo binstall prek
# Install git hooks to run checks automatically
@@ -155,7 +155,7 @@ ### Creating pull requests
Before submitting a pull request, please ensure:
1. Your code passes all CI checks (formatting, linting, typo detection, etc.)
2. Your code follows the [code style guide](docs/development/code_style.mdx)
2. Your code follows the [code style guide](/development/code_style.md)
3. Your commit messages follow the conventional commits format
4. Tests are added for new functionality
5. Documentation is updated if needed
Generated
+243 -146
View File
@@ -259,9 +259,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "aws-lc-rs"
version = "1.17.1"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad"
checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00"
dependencies = [
"aws-lc-sys",
"zeroize",
@@ -269,15 +269,14 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
version = "0.42.0"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444"
checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
"pkg-config",
]
[[package]]
@@ -558,9 +557,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "bytemuck"
version = "1.25.1"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424"
checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
[[package]]
name = "byteorder"
@@ -576,9 +575,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.12.1"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
[[package]]
name = "bytesize"
@@ -637,14 +636,14 @@ checksum = "aa61aec073ec94791433ddf3df2323ff9d1711557c2a0eefb0f99cb4f8dca520"
dependencies = [
"semver",
"serde",
"toml 1.1.3+spec-1.1.0",
"toml 1.1.2+spec-1.1.0",
]
[[package]]
name = "cc"
version = "1.2.67"
version = "1.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -827,7 +826,7 @@ dependencies = [
[[package]]
name = "conduwuit"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"aws-lc-rs",
"clap",
@@ -865,7 +864,7 @@ dependencies = [
[[package]]
name = "conduwuit_admin"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"assign",
"clap",
@@ -891,7 +890,7 @@ dependencies = [
[[package]]
name = "conduwuit_api"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"assign",
"async-trait",
@@ -929,7 +928,7 @@ dependencies = [
[[package]]
name = "conduwuit_build_metadata"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"built",
"cargo_metadata",
@@ -937,7 +936,7 @@ dependencies = [
[[package]]
name = "conduwuit_core"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"argon2",
"arrayvec",
@@ -996,7 +995,7 @@ dependencies = [
"tikv-jemallocator",
"tokio",
"tokio-metrics",
"toml 1.1.3+spec-1.1.0",
"toml 1.1.2+spec-1.1.0",
"tracing",
"tracing-core",
"tracing-subscriber",
@@ -1005,7 +1004,7 @@ dependencies = [
[[package]]
name = "conduwuit_database"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"async-channel",
"conduwuit_core",
@@ -1026,7 +1025,7 @@ dependencies = [
[[package]]
name = "conduwuit_macros"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"cargo_toml",
"itertools 0.15.0",
@@ -1037,7 +1036,7 @@ dependencies = [
[[package]]
name = "conduwuit_router"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"assign",
"axum",
@@ -1075,7 +1074,7 @@ dependencies = [
[[package]]
name = "conduwuit_service"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"askama",
"assign",
@@ -1127,7 +1126,7 @@ dependencies = [
[[package]]
name = "conduwuit_web"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"askama",
"assign",
@@ -1362,18 +1361,18 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.16"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.7"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
@@ -1381,27 +1380,27 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.20"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.13"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26"
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.22"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crossterm"
@@ -1464,9 +1463,9 @@ dependencies = [
[[package]]
name = "ctor"
version = "1.0.8"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb22e947478ccf9dc44d8922042c677a63fbb88f2cb468521d1145816e5087cb"
checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a"
dependencies = [
"link-section",
"linktime-proc-macro",
@@ -1514,14 +1513,38 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "817fa642fb0ee7fe42e95783e00e0969927b96091bdd4b9b1af082acd943913b"
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core 0.20.11",
"darling_macro 0.20.11",
]
[[package]]
name = "darling"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
dependencies = [
"darling_core",
"darling_macro",
"darling_core 0.23.0",
"darling_macro 0.23.0",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
@@ -1537,13 +1560,24 @@ dependencies = [
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core 0.20.11",
"quote",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [
"darling_core",
"darling_core 0.23.0",
"quote",
"syn",
]
@@ -2088,11 +2122,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 6.0.0",
"rand_core 0.10.1",
"wasm-bindgen",
]
[[package]]
@@ -2258,7 +2290,7 @@ dependencies = [
"http",
"httpdate",
"mime",
"sha1 0.10.7",
"sha1 0.10.6",
]
[[package]]
@@ -2443,9 +2475,9 @@ dependencies = [
[[package]]
name = "http-body-util"
version = "0.1.4"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
"futures-core",
@@ -2874,11 +2906,11 @@ dependencies = [
[[package]]
name = "jobserver"
version = "0.1.35"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom 0.4.3",
"getrandom 0.3.4",
"libc",
]
@@ -3028,9 +3060,9 @@ dependencies = [
[[package]]
name = "link-section"
version = "0.19.0"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e333fe507b738576d6da5bb3f1a7d7a1c80307ed9ef31624c057d844c19c93e9"
checksum = "24670b639492630905459a6c7d47f063d33c2d4fcd5362f6e5827c5613976c9f"
[[package]]
name = "linked-hash-map"
@@ -3167,20 +3199,19 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "memchr"
version = "2.8.3"
version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
[[package]]
name = "memory-serve"
version = "2.3.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18718e8f7eefb3194d346474ac95f50ada065ff043e0074e5b8e85b85148122f"
checksum = "81b5bbad2035f57b1e95f66da606832edd935b47d82312e38e1ccffbcfb8a427"
dependencies = [
"axum",
"brotli",
"flate2",
"fs-err",
"mime_guess",
"sha256",
"tracing",
@@ -3364,9 +3395,9 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.4.8"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
checksum = "c863e9ab5e7bf9c99ba75e1050f1e4d624ae87ed3532d6238ffbdc7b585dbbe6"
dependencies = [
"num-integer",
"num-traits",
@@ -3383,7 +3414,7 @@ dependencies = [
"num-integer",
"num-iter",
"num-traits",
"rand 0.8.7",
"rand 0.8.6",
"smallvec",
"zeroize",
]
@@ -3414,10 +3445,11 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.46"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
@@ -3463,7 +3495,7 @@ dependencies = [
"chrono",
"getrandom 0.2.17",
"http",
"rand 0.8.7",
"rand 0.8.6",
"reqwest 0.12.28",
"serde",
"serde_json",
@@ -3668,7 +3700,7 @@ dependencies = [
"oauth2",
"p256",
"p384",
"rand 0.8.7",
"rand 0.8.6",
"rsa",
"serde",
"serde-value",
@@ -3759,7 +3791,7 @@ dependencies = [
"opentelemetry",
"percent-encoding",
"portable-atomic",
"rand 0.9.5",
"rand 0.9.4",
"thiserror 2.0.18",
"tokio",
"tokio-stream",
@@ -3936,7 +3968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"rand 0.8.7",
"rand 0.8.6",
]
[[package]]
@@ -4080,22 +4112,22 @@ dependencies = [
]
[[package]]
name = "proc-macro-error-attr3"
version = "3.0.2"
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34e4dd828515431dd6c4a030d26f7eaed7dd4778226e9d2bb968d65ca4ec3d4d"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error3"
version = "3.0.2"
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee475e440453418ff1335189eddf7101ba502cd818ab7ae04209bc83aa925aa"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr3",
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
@@ -4175,9 +4207,9 @@ checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
[[package]]
name = "pxfm"
version = "0.1.30"
version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f"
[[package]]
name = "quick-error"
@@ -4208,16 +4240,15 @@ dependencies = [
[[package]]
name = "quinn-proto"
version = "0.11.16"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560"
checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e"
dependencies = [
"aws-lc-rs",
"bytes",
"getrandom 0.4.3",
"getrandom 0.3.4",
"lru-slab",
"rand 0.10.2",
"rand_pcg",
"rand 0.9.4",
"ring",
"rustc-hash",
"rustls",
@@ -4231,16 +4262,16 @@ dependencies = [
[[package]]
name = "quinn-udp"
version = "0.5.15"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -4272,9 +4303,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.8.7"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [
"libc",
"rand_chacha 0.3.1",
@@ -4283,9 +4314,9 @@ dependencies = [
[[package]]
name = "rand"
version = "0.9.5"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.5",
@@ -4346,15 +4377,6 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "rand_pcg"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
dependencies = [
"rand_core 0.10.1",
]
[[package]]
name = "recaptcha-verify"
version = "0.2.0"
@@ -4397,9 +4419,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.13.0"
version = "1.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2"
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
dependencies = [
"aho-corasick",
"memchr",
@@ -4409,9 +4431,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.15"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
@@ -4517,9 +4539,9 @@ checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7"
[[package]]
name = "resolvematrix"
version = "1.2.0"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68b9735d8c1096d8152d4dfb5a705c0135dd14d181542fd87301f8d3cb4ed03"
checksum = "a574e37f06e219dcfdb14a4f0abf56e806c4551e030aae53802d2c42b29aea33"
dependencies = [
"futures",
"hickory-resolver",
@@ -4728,7 +4750,7 @@ dependencies = [
"ruma-identifiers-validation",
"serde",
"syn",
"toml 1.1.3+spec-1.1.0",
"toml 1.1.2+spec-1.1.0",
]
[[package]]
@@ -4777,7 +4799,7 @@ dependencies = [
[[package]]
name = "ruminuwuity"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"assign",
"ruma",
@@ -4815,9 +4837,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
version = "0.1.28"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
[[package]]
name = "rustc-hash"
@@ -4849,9 +4871,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.42"
version = "0.23.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f"
dependencies = [
"aws-lc-rs",
"log",
@@ -4926,9 +4948,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.23"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "rustyline-async"
@@ -5114,7 +5136,7 @@ version = "0.48.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8c44ee52fa3d30581f951b57aec754ef9cd70186df5fb788367804360c48097"
dependencies = [
"rand 0.9.5",
"rand 0.9.4",
"sentry-types",
"serde",
"serde_json",
@@ -5187,7 +5209,7 @@ checksum = "1be93ef0435a5ab91f88178fb7681249437e875d004f996ecb35be94dc99a0e0"
dependencies = [
"debugid",
"hex",
"rand 0.9.5",
"rand 0.9.4",
"serde",
"serde_json",
"thiserror 2.0.18",
@@ -5367,7 +5389,7 @@ version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
dependencies = [
"darling",
"darling 0.23.0",
"proc-macro2",
"quote",
"syn",
@@ -5375,9 +5397,9 @@ dependencies = [
[[package]]
name = "sha1"
version = "0.10.7"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
@@ -5631,9 +5653,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.119"
version = "2.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
dependencies = [
"proc-macro2",
"quote",
@@ -5700,9 +5722,9 @@ dependencies = [
[[package]]
name = "termimad"
version = "0.35.1"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53d4b1294b87e81925b7ae7f8f4d000376e3a5b3349d978429665428a793fcb"
checksum = "903906afabf58273a7a90b47e0e60701cbd01731ca72ba88508b38b5539647f4"
dependencies = [
"coolor",
"crokey",
@@ -5766,9 +5788,9 @@ dependencies = [
[[package]]
name = "thread_local"
version = "1.1.10"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
@@ -5843,9 +5865,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.12.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
dependencies = [
"tinyvec_macros",
]
@@ -5944,9 +5966,9 @@ dependencies = [
[[package]]
name = "toml"
version = "1.1.3+spec-1.1.0"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c"
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
dependencies = [
"indexmap 2.14.0",
"serde_core",
@@ -6018,9 +6040,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "toml_writer"
version = "1.1.2+spec-1.1.0"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]]
name = "tonic"
@@ -6208,7 +6230,7 @@ dependencies = [
"futures",
"http",
"parking_lot",
"rand 0.9.5",
"rand 0.9.4",
"serde",
"serde_json",
"thiserror 2.0.18",
@@ -6422,9 +6444,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.23.5"
version = "1.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea5fab0d6c3c01ae70085a09cb03d4c7a1d6314e2b3e075392783396d724ca0a"
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
dependencies = [
"getrandom 0.4.3",
"js-sys",
@@ -6450,12 +6472,13 @@ dependencies = [
[[package]]
name = "validator_derive"
version = "0.20.1"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240e4b81c20a1d6d50d1d7265c658dfbd204e8b9ac4d80f3c931f39462196335"
checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca"
dependencies = [
"darling",
"proc-macro-error3",
"darling 0.20.11",
"once_cell",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
@@ -6756,7 +6779,16 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]]
@@ -6774,14 +6806,31 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
]
[[package]]
@@ -6790,48 +6839,96 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
version = "0.7.15"
@@ -6875,7 +6972,7 @@ dependencies = [
[[package]]
name = "xtask"
version = "26.6.2"
version = "26.6.0"
dependencies = [
"askama",
"cargo_metadata",
@@ -6915,18 +7012,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.54"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.54"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930"
dependencies = [
"proc-macro2",
"quote",
@@ -6995,9 +7092,9 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.22"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd2f034a4bebf216c9e4b7083603e024cf930873fd67830cfb083c9fa33129d9"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zstd"
+8 -2
View File
@@ -12,7 +12,7 @@ license = "Apache-2.0"
# See also `rust-toolchain.toml`
readme = "README.md"
repository = "https://forgejo.ellis.link/continuwuation/continuwuity"
version = "26.6.2"
version = "26.6.0"
[workspace.metadata.crane]
name = "conduwuit"
@@ -141,6 +141,12 @@ features = [
version = "0.23.25"
default-features = false
[workspace.dependencies.aws-lc-sys]
version = "0.41.0"
[workspace.dependencies.aws-lc-rs]
version = "1.17.0"
[workspace.dependencies.reqwest]
version = "0.13.2"
default-features = false
@@ -564,7 +570,7 @@ features = ["std"]
version = "0.3.0"
[workspace.dependencies.resolvematrix]
version = "1.2.0"
version = "1.1.0"
[workspace.dependencies.serde_urlencoded]
version = "0.7.1"
-1
View File
@@ -1 +0,0 @@
Fixed requests returning `500 Internal Server Error` when the header selected by `request_ip_source` is absent, duplicated, or malformed (for example Envoy omitting `X-Envoy-External-Address` on internal requests). The client IP now falls back to the connection peer address instead of failing the request. Contributed by @eleboucher
-1
View File
@@ -1 +0,0 @@
Appservices are now properly able to create devices for E2EE.
-1
View File
@@ -1 +0,0 @@
Resolve alias service by correct name for auto-join. Contributed by @eleboucher
-1
View File
@@ -1 +0,0 @@
Fixed newly joined rooms failing to sync their full state (including the room name) to clients using legacy sync.
+1
View File
@@ -0,0 +1 @@
Stopped appservice users from being erroneously marked as deactivated during a 26.6 database migration.
-1
View File
@@ -1 +0,0 @@
Appservices may now specify both the unstable and stable `device_id` query parameters in a request. The stable parameter will take priority. Contributed by @ginger.
+1
View File
@@ -0,0 +1 @@
Fixed existing accounts failing to link when logging in with OIDC if `prompt_for_localpart` was `false`.
+1
View File
@@ -0,0 +1 @@
Authentication is no longer required on the `/_matrix/client/v3/account/3pid/email/requestToken` endpoint.
-1
View File
@@ -1 +0,0 @@
Fixed the deeplink redirect for deleting devices. Contributed by @koen
-1
View File
@@ -1 +0,0 @@
Fix status code for oauth registration. Contributed by @n00byking
-1
View File
@@ -1 +0,0 @@
Exempt m.room.create from auth_events check. Contributed by @eleboucher
-1
View File
@@ -1 +0,0 @@
Fixed `create` being returned as a supported prompt value regardless of if registration is enabled or not. Contributed by @ginger
-1
View File
@@ -1 +0,0 @@
Updated an out-of-date statement about Oracle Linux release cadences.
-1
View File
@@ -1 +0,0 @@
Fixed high CPU usage when multiple clients from the same account were connected at once. Each sync woke the account's other sync loops, causing them to wake each other in a loop.
@@ -1 +0,0 @@
Fix joining restricted rooms over federation failing with signature verification error.
+2 -2
View File
@@ -15,8 +15,8 @@ ## Overview
:::warning Oracle Linux support
Due to upstream limitations, Terra is only usable on Oracle Linux if you use [upstream EPEL](https://docs.fedoraproject.org/en-US/epel/getting-started/)
rather than Oracle's rebuilds of EPEL. Oracle tends to lag behind on new EL versions, both major and minor—for example, Oracle's release of 10.2 lagged
approximately 1.5 months behind other ELs—so you may encounter further compatibility issues with EPEL.
rather than Oracle's rebuilds of EPEL. Oracle tends to lag behind on new EL versions, both major and minor—for example, as of the time of writing, 10.2 has been
available for over a month through other ELs, but Oracle has not yet released corresponding updates—so you may encounter further compatibility issues with EPEL.
**For this reason, it is recommended that you use another EL distribution if at all possible.**
:::
@@ -6,10 +6,10 @@
"message": "Welcome to Continuwuity! Important announcements about the project will appear here."
},
{
"id": 16,
"id": 15,
"mention_room": true,
"date": "2026-07-13",
"message": "[Continuwuity 26.6.2](https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.6.1) has just been released! This release fixes a severe bug with OIDC that could cause users' accounts to be flagged as deactivated. If you use OIDC, please update as soon as possible."
"date": "2026-07-10",
"message": "[Continuwuity 26.6.0](https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.6.0) is finally out! This is by far our largest release yet, and it wouldn't have been possible without our community. Thank you so much for all of your patience, support, and/or development time over the last three months <3"
}
]
}
Generated
+21 -21
View File
@@ -3,11 +3,11 @@
"advisory-db": {
"flake": false,
"locked": {
"lastModified": 1783840254,
"narHash": "sha256-XjyvZk0f3YiinVHmkGOotmLBAzvK+LwEJYj2QqJ5pn8=",
"lastModified": 1781566179,
"narHash": "sha256-Tqv8I586fYzWpEW/Smq/JqESFa3DVVzVWsnAMtvhy/I=",
"owner": "rustsec",
"repo": "advisory-db",
"rev": "6e3286f4efa8c142fb33e5ea4342c8db6693cf34",
"rev": "74e084413d979d52d2f93b1d93b1ab7b9ee648f5",
"type": "github"
},
"original": {
@@ -18,11 +18,11 @@
},
"crane": {
"locked": {
"lastModified": 1783203018,
"narHash": "sha256-G6R9IT/xwFuu+CYBWDUAok6AdC4ERC4ZfPPFtEpxnZE=",
"lastModified": 1780532242,
"narHash": "sha256-D+BsdpxmtUwtqGoY0IXPhHgTlmqgcZKCEo1oMyn7ep0=",
"owner": "ipetkov",
"repo": "crane",
"rev": "80db5bdc391be8a1794f6d8a2d56e3a84ebcede2",
"rev": "59a82a1222dd3b2080b5cc52a1a2e8d5f1b77f37",
"type": "github"
},
"original": {
@@ -39,11 +39,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1783844668,
"narHash": "sha256-3MOpw4y3reoErRLvFBDz0t9aG6FWXUj7leKvUns5eQA=",
"lastModified": 1781527054,
"narHash": "sha256-1fX9ev2Fh5QoKQ41G9dYutjo5j/jywu6tZse5Eb1Ck4=",
"owner": "nix-community",
"repo": "fenix",
"rev": "4e49ef62eedaa5c149d62b63b3f53844e1cc45d7",
"rev": "8c2e51dffefc040a21975da7abf6f252c8c9b783",
"type": "github"
},
"original": {
@@ -74,11 +74,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1782949081,
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
"lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"type": "github"
},
"original": {
@@ -89,11 +89,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1783776592,
"narHash": "sha256-UgCQzxeWI75XM8G+hPrPh+MKzEPjG3SpAj7dtqSbksA=",
"lastModified": 1781074563,
"narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3",
"rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca",
"type": "github"
},
"original": {
@@ -105,11 +105,11 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1782614948,
"narHash": "sha256-ePjCwr1sNm9NYUqywL7QfK3JnlS015msC+eBu2zKlp8=",
"lastModified": 1777168982,
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "db3f255737b94216eb71cce308e2912cf6bc2d7c",
"rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14",
"type": "github"
},
"original": {
@@ -132,11 +132,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1783779020,
"narHash": "sha256-vpm418WZa9l1KUl6HRs+Ga+SIfE+D2/sV4olwTlilYc=",
"lastModified": 1781453968,
"narHash": "sha256-+V3nK4pCngbmgyVGXY6Kkrlevp4ocPkJJLf2aqwkDNA=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "5be5e89cf0145d73a85c805726821b4adfc3af48",
"rev": "cc272809a173c2c11d0e479d639c811c1eacf049",
"type": "github"
},
"original": {
+2 -2
View File
@@ -43,8 +43,8 @@ let
env = {
CARGO_PROFILE = profile;
RUSTFLAGS = rustflags;
GIT_COMMIT_HASH = self.rev or self.dirtyRev or "";
GIT_COMMIT_HASH_SHORT = self.shortRev or self.dirtyShortRev or "";
GIT_COMMIT_HASH = self.rev or self.dirtyRev;
GIT_COMMIT_HASH_SHORT = self.shortRev or self.dirtyShortRev;
}
// (lib.optionalAttrs (rocksdb != null) {
ROCKSDB_INCLUDE_DIR = "${rocksdb}/include";
+2 -4
View File
@@ -36,16 +36,14 @@
self'.packages.stable-toolchain
);
# extra features via `cargoExtraArgs`
cargoExtraArgs = "-F http3";
default = pkgs.callPackage ./continuwuity.nix {
inherit self craneLib;
liburing = (if isStatic then pkgs.pkgsStatic else pkgs).liburing;
rocksdb = if isStatic then null else self'.packages.rocksdb;
inherit cargoExtraArgs;
# extra features via `cargoExtraArgs`
cargoExtraArgs = "-F http3";
# extra RUSTFLAGS via `rustflags`
# the stuff below is required for http3
rustflags = "--cfg reqwest_unstable";
+1 -1
View File
@@ -12,7 +12,7 @@
target:
target.fromToolchainName {
name = (lib.importTOML "${inputs.self}/rust-toolchain.toml").toolchain.channel;
sha256 = "sha256-A1abGIbOtcBSdrUMhDGrER3pRM1hQP4fp9gh3Y4PKc8=";
sha256 = "sha256-OATSZm98Es5kIFuqaba+UvkQtFsVgJEBMmS+t6od5/U=";
};
in
{
+61 -61
View File
@@ -419,9 +419,9 @@
}
},
"node_modules/@rspress/core": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/@rspress/core/-/core-2.0.18.tgz",
"integrity": "sha512-DBpsr/6XAItQkZPL5FSjthLGuzdG9ks/7EOxkqaaXp93uAT54LO0PD5Dkoy6ydup1PnvkpHyiHpUyDcnHHTVhg==",
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@rspress/core/-/core-2.0.17.tgz",
"integrity": "sha512-oynYHE2aBj7BFzD/UgqKnrj/PyYxfWGrmRPlTmjLL22UPcocdUdFYLb/6nijyseAST59VMr5Gj4wOfFUeJT5vw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -429,7 +429,7 @@
"@mdx-js/react": "^3.1.1",
"@rsbuild/core": "^2.1.5",
"@rsbuild/plugin-react": "~2.1.0",
"@rspress/shared": "2.0.18",
"@rspress/shared": "2.0.17",
"@shikijs/rehype": "^4.2.0",
"@types/unist": "^3.0.3",
"@unhead/react": "^2.1.15",
@@ -472,9 +472,9 @@
}
},
"node_modules/@rspress/plugin-client-redirects": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/@rspress/plugin-client-redirects/-/plugin-client-redirects-2.0.18.tgz",
"integrity": "sha512-q70ufN0S125kl0QlucO7JuiZ0WJaxFOtPPoGu7eWwam9fb+i4YsWnOWtAKSKGH4JP5+897mgpDPP+dmfl9DPCw==",
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@rspress/plugin-client-redirects/-/plugin-client-redirects-2.0.17.tgz",
"integrity": "sha512-HJMlW5xsQe2cAFYNHw3LhMy8WcuqRb4l0ey3E3OIu121ymbSLqIQJVPS/IF/NuoyPoxCAmV58LYIaCxGEriUww==",
"dev": true,
"license": "MIT",
"engines": {
@@ -485,9 +485,9 @@
}
},
"node_modules/@rspress/plugin-sitemap": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/@rspress/plugin-sitemap/-/plugin-sitemap-2.0.18.tgz",
"integrity": "sha512-XfaXzZx+ASN9OMTc0fOvW8QmbmyCyDWkO8xJj4XF4E0YJtsu7j/65+v8/KXXdwpywOfUIQ6M94/meRTy2urNmQ==",
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@rspress/plugin-sitemap/-/plugin-sitemap-2.0.17.tgz",
"integrity": "sha512-MpNxF+hFK7zsk8t+Ta7KYI/Py6Csjyw0AMGrWf3XG/kr0+fisVxbT/jUanXRLSoj2m9VX9CVsi180rPnezUpbQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -498,9 +498,9 @@
}
},
"node_modules/@rspress/shared": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/@rspress/shared/-/shared-2.0.18.tgz",
"integrity": "sha512-GJswqJQCPSxvBt5r+gJzz8Em8EEK/sOmCQjTpemTvldvxr1Lva85BGVnSQZOgofnM0nrjt18kn4mmnuuSstbpA==",
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@rspress/shared/-/shared-2.0.17.tgz",
"integrity": "sha512-ZzpZ4hm5svgwU0w5CpTLZy4vFD3uPo8+gXtWMOREYMzwfzJeHqwOyXwxHf6byGpx85mVK5DQqMDi61meAS7ZUw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -510,14 +510,14 @@
}
},
"node_modules/@shikijs/core": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.3.1.tgz",
"integrity": "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.2.0.tgz",
"integrity": "sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/primitive": "4.3.1",
"@shikijs/types": "4.3.1",
"@shikijs/primitive": "4.2.0",
"@shikijs/types": "4.2.0",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.5"
@@ -527,13 +527,13 @@
}
},
"node_modules/@shikijs/engine-javascript": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.3.1.tgz",
"integrity": "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.2.0.tgz",
"integrity": "sha512-fjETeq1k5ffyXqRgS6+3hpvqseLalp1kjNfRbXpUgWR8FpZ1CmQfiNHovc5lncYjt/Vg5JK/WJEmLahjwMa0og==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.3.1",
"@shikijs/types": "4.2.0",
"@shikijs/vscode-textmate": "^10.0.2",
"oniguruma-to-es": "^4.3.6"
},
@@ -542,13 +542,13 @@
}
},
"node_modules/@shikijs/engine-oniguruma": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.3.1.tgz",
"integrity": "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.2.0.tgz",
"integrity": "sha512-hTorK1dffPkpbMUk6Z+828PgRo7d07HbnizoP0hNPFjhxMHctj0Px/qoHeGMYafc6ju+u9iMldN4JbVzNQM++g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.3.1",
"@shikijs/types": "4.2.0",
"@shikijs/vscode-textmate": "^10.0.2"
},
"engines": {
@@ -556,26 +556,26 @@
}
},
"node_modules/@shikijs/langs": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.3.1.tgz",
"integrity": "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.2.0.tgz",
"integrity": "sha512-bwrVRlJ0wUhZxAbVdvBbv2TTC9yLsh4C/IO5Ofz0T8MQntgDvyVnkbjw9vi50r1kx7RCIJdnJnjZAwmAsXFLZQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.3.1"
"@shikijs/types": "4.2.0"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@shikijs/primitive": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.3.1.tgz",
"integrity": "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.2.0.tgz",
"integrity": "sha512-NOq+DtUkVBJtZMVXL5A0vI0Xk8nvDYaXetFHSJFlOqjDZIVhIPRYFdGkSoElDqNuegikcc3A76SNUa8dTqtAYA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.3.1",
"@shikijs/types": "4.2.0",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
},
@@ -584,16 +584,16 @@
}
},
"node_modules/@shikijs/rehype": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.3.1.tgz",
"integrity": "sha512-oshrlfUF3VPUJfnp5K1lLwsS/SRBKrIxONpdWebSKZXdBE3UsZnxgqpvRUA8UsofS7vmjFOCAHIT71ECbmOxTw==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.2.0.tgz",
"integrity": "sha512-ST3EWye/dwF1gWskczJNBnwFtDzEQ9ceytXZtyc/GfwR5V0qJrkoSGZO55O3SAKDDsXkTDcsfwd9pVe7ROlAHg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.3.1",
"@shikijs/types": "4.2.0",
"@types/hast": "^3.0.4",
"hast-util-to-string": "^3.0.1",
"shiki": "4.3.1",
"shiki": "4.2.0",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0"
},
@@ -602,22 +602,22 @@
}
},
"node_modules/@shikijs/themes": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.3.1.tgz",
"integrity": "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.2.0.tgz",
"integrity": "sha512-RX8IHYeLv8Cu2W6ruc3RxUqWn0IYCqSrMBzi/uRGAmfyDNOnNO5BF/Px7o97n4XTpmFTo5GbRaazuOWj+2ak2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.3.1"
"@shikijs/types": "4.2.0"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@shikijs/types": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.3.1.tgz",
"integrity": "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.2.0.tgz",
"integrity": "sha512-VT/MKtlpOhEPZloSH3Pb9WCZEBDoQVMa9jedp5UAwmJOar1DVc9DRODAxmYPW9M93IK4ryuqRejFfmlvlVDemw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -684,9 +684,9 @@
}
},
"node_modules/@types/hast": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz",
"integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==",
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1076,9 +1076,9 @@
}
},
"node_modules/@ungap/structured-clone": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz",
"integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
"integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
"dev": true,
"license": "ISC"
},
@@ -3589,18 +3589,18 @@
"license": "MIT"
},
"node_modules/shiki": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-4.3.1.tgz",
"integrity": "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-4.2.0.tgz",
"integrity": "sha512-hjNax6o/ylDy9lefQEaSDtzaT3iVNtZ3WmpQnbuQNoG4xvnSKf2kSKbihZVO4JRG1TTMejs7CmNRYlWgAL66pQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/core": "4.3.1",
"@shikijs/engine-javascript": "4.3.1",
"@shikijs/engine-oniguruma": "4.3.1",
"@shikijs/langs": "4.3.1",
"@shikijs/themes": "4.3.1",
"@shikijs/types": "4.3.1",
"@shikijs/core": "4.2.0",
"@shikijs/engine-javascript": "4.2.0",
"@shikijs/engine-oniguruma": "4.2.0",
"@shikijs/langs": "4.2.0",
"@shikijs/themes": "4.2.0",
"@shikijs/types": "4.2.0",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
},
-7
View File
@@ -60,13 +60,6 @@
"matchPackageNames": ["rust", "rustc", "cargo"],
"groupName": "rust-toolchain"
},
{
"description": "Delay major rust toolchain updates",
"matchManagers": ["custom.regex"],
"matchPackageNames": ["rust", "rustc", "cargo"],
"matchUpdateTypes": ["major"],
"minimumReleaseAge": "30 days"
},
{
"description": "Batch minor and patch GitHub Actions updates",
"matchManagers": ["github-actions"],
+1 -1
View File
@@ -10,7 +10,7 @@
[toolchain]
profile = "minimal"
channel = "1.97.1"
channel = "1.97.0"
components = [
# For rust-analyzer
"rust-src",
+3 -3
View File
@@ -486,7 +486,7 @@ pub(super) async fn get_remote_pdu(
.services
.rooms
.event_handler
.parse_incoming_pdu(&response.pdu, None)
.parse_incoming_pdu(&response.pdu)
.boxed()
.await;
@@ -832,7 +832,7 @@ pub(super) async fn force_set_room_state_from_server(
.services
.rooms
.event_handler
.parse_incoming_pdu(&pdu, Some(&room_version_rules))
.parse_incoming_pdu(&pdu)
.await
{
| Ok(t) => t,
@@ -1014,7 +1014,7 @@ pub(super) async fn resolve_true_destination(
let resolver: &MatrixResolver = if no_cache {
&MatrixResolverBuilder::new()
.dangerous_tls_accept_invalid_certs(self.services.server.config.allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure)
.http_client(self.services.client.dns.clone())
.http_client(self.services.client.default.clone())
.build()?
} else {
&self.services.client.matrix_resolver
+1 -1
View File
@@ -54,7 +54,7 @@ pub(super) async fn fetch_support_well_known(&self, server_name: OwnedServerName
let response = self
.services
.client
.external_resource
.default
.get(format!("https://{server_name}/.well-known/matrix/support"))
.send()
.await?;
+9 -5
View File
@@ -1,4 +1,5 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, err, info,
pdu::PartialPdu,
@@ -25,8 +26,8 @@
};
use service::{mailer::messages, uiaa::UiaaInitiator, users::HashedPassword};
use super::DEVICE_ID_LENGTH;
use crate::{Ruma, client_ip::ClientIp, router::ClientIdentity};
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
use crate::{Ruma, router::ClientIdentity};
pub(crate) mod register;
pub(crate) mod threepid;
@@ -42,9 +43,10 @@
///
/// Note: This will not reserve the username, so the username might become
/// invalid when trying to register
#[tracing::instrument(skip_all, name = "register_available", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "register_available", level = "info")]
pub(crate) async fn get_register_available_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_username_availability::v3::Request>,
) -> Result<get_username_availability::v3::Response> {
let _ = services
@@ -78,9 +80,10 @@ pub(crate) async fn get_register_available_route(
/// last seen ts)
/// - Forgets to-device events
/// - Triggers device list updates
#[tracing::instrument(skip_all, name = "change_password", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "change_password", level = "info")]
pub(crate) async fn change_password_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<change_password::v3::Request>,
) -> Result<change_password::v3::Response> {
let identity = if let Some(identity) = body.identity.as_ref() {
@@ -245,9 +248,10 @@ pub(crate) async fn whoami_route(
/// - Forgets all to-device events
/// - Triggers device list updates
/// - Removes ability to log in again
#[tracing::instrument(skip_all, name = "deactivate", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "deactivate", level = "info")]
pub(crate) async fn deactivate_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<deactivate::v3::Request>,
) -> Result<deactivate::v3::Response> {
// Authentication for this endpoint is technically optional,
+10 -11
View File
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, debug_info, info,
utils::{self},
@@ -19,13 +20,10 @@
assign,
};
use serde_json::value::RawValue;
use service::{
mailer::messages,
users::{DeviceToken, HashedPassword},
};
use service::{mailer::messages, users::HashedPassword};
use super::DEVICE_ID_LENGTH;
use crate::{Ruma, client_ip::ClientIp};
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
use crate::Ruma;
/// # `POST /_matrix/client/v3/register`
///
@@ -35,10 +33,10 @@
/// /_matrix/client/v3/register/available`](fn.get_register_available_route.
/// html) to check if the user id is valid and available.
#[allow(clippy::doc_markdown)]
#[tracing::instrument(skip_all, name = "register", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "register", level = "info")]
pub(crate) async fn register_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp, // NOTE: Required for metadata.
ClientIp(client): ClientIp,
body: Ruma<register::v3::Request>,
) -> Result<register::v3::Response> {
if body.kind != RegistrationKind::User {
@@ -121,7 +119,7 @@ pub(crate) async fn register_route(
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
// Generate new token for the device
let new_token = DeviceToken::new_random();
let new_token = utils::random_string(TOKEN_LENGTH);
// Create device for this account
services
@@ -129,7 +127,8 @@ pub(crate) async fn register_route(
.create_device(
&user_id,
&device_id,
Some(new_token.clone()),
&new_token,
None,
body.initial_device_display_name.clone(),
Some(client.to_string()),
)
@@ -143,7 +142,7 @@ pub(crate) async fn register_route(
debug_info!(%user_id, ?device, "New account created via legacy registration");
Ok(assign!(register::v3::Response::new(user_id), {
access_token: token.map(DeviceToken::into_token),
access_token: token,
device_id: device,
refresh_token: None,
expires_in: None,
+10 -5
View File
@@ -1,4 +1,5 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, Result, at};
use futures::StreamExt;
use ruma::{
@@ -11,16 +12,17 @@
assign,
};
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
const MAX_BATCH_EVENTS: usize = 50;
/// # `PUT /_matrix/client/../dehydrated_device`
///
/// Creates or overwrites the user's dehydrated device.
#[tracing::instrument(skip_all)]
#[tracing::instrument(skip_all, fields(%client))]
pub(crate) async fn put_dehydrated_device_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<put_dehydrated_device::Request>,
) -> Result<put_dehydrated_device::Response> {
let device_id = body.device_id.clone();
@@ -36,9 +38,10 @@ pub(crate) async fn put_dehydrated_device_route(
/// # `DELETE /_matrix/client/../dehydrated_device`
///
/// Deletes the user's dehydrated device without replacement.
#[tracing::instrument(skip_all)]
#[tracing::instrument(skip_all, fields(%client))]
pub(crate) async fn delete_dehydrated_device_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<delete_dehydrated_device::Request>,
) -> Result<delete_dehydrated_device::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -53,9 +56,10 @@ pub(crate) async fn delete_dehydrated_device_route(
/// # `GET /_matrix/client/../dehydrated_device`
///
/// Gets the user's dehydrated device
#[tracing::instrument(skip_all)]
#[tracing::instrument(skip_all, fields(%client))]
pub(crate) async fn get_dehydrated_device_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_dehydrated_device::Request>,
) -> Result<get_dehydrated_device::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -68,9 +72,10 @@ pub(crate) async fn get_dehydrated_device_route(
/// # `GET /_matrix/client/../dehydrated_device/{device_id}/events`
///
/// Paginates the events of the dehydrated device.
#[tracing::instrument(skip_all)]
#[tracing::instrument(skip_all, fields(%client))]
pub(crate) async fn get_dehydrated_events_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_events::Request>,
) -> Result<get_events::Response> {
let sender_user = body.identity.expect_sender_user()?;
+12 -4
View File
@@ -1,4 +1,5 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, Result, debug, err, utils};
use futures::StreamExt;
use ruma::{
@@ -8,7 +9,7 @@
},
};
use crate::{Ruma, client::DEVICE_ID_LENGTH, client_ip::ClientIp};
use crate::{Ruma, client::DEVICE_ID_LENGTH};
/// # `GET /_matrix/client/r0/devices`
///
@@ -45,10 +46,10 @@ pub(crate) async fn get_device_route(
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
///
/// Updates the metadata on a given device of the sender user.
#[tracing::instrument(skip_all, name = "update_device", level = "debug")]
#[tracing::instrument(skip_all, fields(%client), name = "update_device", level = "debug")]
pub(crate) async fn update_device_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp, // NOTE: Required for updating device metadata
ClientIp(client): ClientIp,
body: Ruma<update_device::v3::Request>,
) -> Result<update_device::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -88,7 +89,14 @@ pub(crate) async fn update_device_route(
services
.users
.create_device(sender_user, &device_id, None, None, Some(client.to_string()))
.create_device(
sender_user,
&device_id,
&appservice.registration.as_token,
None,
None,
Some(client.to_string()),
)
.await?;
return Ok(update_device::v3::Response::new());
+8 -4
View File
@@ -1,4 +1,5 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, err, info,
utils::{
@@ -27,16 +28,17 @@
};
use tokio::join;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `POST /_matrix/client/v3/publicRooms`
///
/// Lists the public rooms on this server.
///
/// - Rooms are ordered by the number of joined members
#[tracing::instrument(skip_all, name = "publicrooms", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms", level = "info")]
pub(crate) async fn get_public_rooms_filtered_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_public_rooms_filtered::v3::Request>,
) -> Result<get_public_rooms_filtered::v3::Response> {
if let Some(server) = &body.server {
@@ -69,9 +71,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
/// Lists the public rooms on this server.
///
/// - Rooms are ordered by the number of joined members
#[tracing::instrument(skip_all, name = "publicrooms", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms", level = "info")]
pub(crate) async fn get_public_rooms_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_public_rooms::v3::Request>,
) -> Result<get_public_rooms::v3::Response> {
if let Some(server) = &body.server {
@@ -103,9 +106,10 @@ pub(crate) async fn get_public_rooms_route(
/// # `PUT /_matrix/client/r0/directory/list/room/{roomId}`
///
/// Sets the visibility of a given room in the room directory.
#[tracing::instrument(skip_all, name = "room_directory", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "room_directory", level = "info")]
pub(crate) async fn set_room_visibility_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<set_room_visibility::v3::Request>,
) -> Result<set_room_visibility::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
+37 -1
View File
@@ -1,6 +1,7 @@
use std::time::Duration;
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, err,
utils::{self, content_disposition::make_content_disposition, math::ruma_from_usize},
@@ -23,7 +24,7 @@
};
use service::media::mxc::Mxc;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `GET /_matrix/client/v1/media/config`
pub(crate) async fn get_media_config_route(
@@ -41,8 +42,15 @@ pub(crate) async fn get_media_config_route(
///
/// - Some metadata will be saved in the database
/// - Media will be saved in the media/ directory
#[tracing::instrument(
name = "media_upload",
level = "debug",
skip_all,
fields(%client),
)]
pub(crate) async fn create_content_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<create_content::v3::Request>,
) -> Result<create_content::v3::Response> {
let user = body.identity.expect_sender_user()?;
@@ -73,8 +81,15 @@ pub(crate) async fn create_content_route(
/// # `GET /_matrix/client/v1/media/thumbnail/{serverName}/{mediaId}`
///
/// Load media thumbnail from our server or over federation.
#[tracing::instrument(
name = "media_thumbnail_get",
level = "debug",
skip_all,
fields(%client),
)]
pub(crate) async fn get_content_thumbnail_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content_thumbnail::v1::Request>,
) -> Result<get_content_thumbnail::v1::Response> {
let user = body.identity.expect_sender_user()?;
@@ -116,8 +131,15 @@ pub(crate) async fn get_content_thumbnail_route(
/// # `GET /_matrix/client/v1/media/download/{serverName}/{mediaId}`
///
/// Load media from our server or over federation.
#[tracing::instrument(
name = "media_get",
level = "debug",
skip_all,
fields(%client),
)]
pub(crate) async fn get_content_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content::v1::Request>,
) -> Result<get_content::v1::Response> {
let user = body.identity.expect_sender_user()?;
@@ -156,8 +178,15 @@ pub(crate) async fn get_content_route(
/// # `GET /_matrix/client/v1/media/download/{serverName}/{mediaId}/{fileName}`
///
/// Load media from our server or over federation as fileName.
#[tracing::instrument(
name = "media_get_af",
level = "debug",
skip_all,
fields(%client),
)]
pub(crate) async fn get_content_as_filename_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content_as_filename::v1::Request>,
) -> Result<get_content_as_filename::v1::Response> {
let user = body.identity.expect_sender_user()?;
@@ -200,8 +229,15 @@ pub(crate) async fn get_content_as_filename_route(
/// # `GET /_matrix/client/v1/media/preview_url`
///
/// Returns URL preview.
#[tracing::instrument(
name = "url_preview",
level = "debug",
skip_all,
fields(%client),
)]
pub(crate) async fn get_media_preview_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_media_preview::v1::Request>,
) -> Result<get_media_preview::v1::Response> {
let sender_user = body.identity.expect_sender_user()?;
+21 -6
View File
@@ -1,6 +1,7 @@
#![allow(deprecated)]
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, err,
utils::{content_disposition::make_content_disposition, math::ruma_from_usize},
@@ -16,7 +17,7 @@
};
use service::media::mxc::Mxc;
use crate::{Ruma, RumaResponse, client::create_content_route, client_ip::ClientIp};
use crate::{Ruma, RumaResponse, client::create_content_route};
/// # `GET /_matrix/media/v3/config`
///
@@ -49,8 +50,10 @@ pub(crate) async fn get_media_config_legacy_legacy_route(
/// # `GET /_matrix/media/v3/preview_url`
///
/// Returns URL preview.
#[tracing::instrument(skip_all, fields(%client), name = "url_preview_legacy", level = "debug")]
pub(crate) async fn get_media_preview_legacy_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_media_preview::v3::Request>,
) -> Result<get_media_preview::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -92,9 +95,10 @@ pub(crate) async fn get_media_preview_legacy_route(
/// Returns URL preview.
pub(crate) async fn get_media_preview_legacy_legacy_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_media_preview::v3::Request>,
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
get_media_preview_legacy_route(State(services), body)
get_media_preview_legacy_route(State(services), ClientIp(client), body)
.await
.map(RumaResponse)
}
@@ -111,9 +115,10 @@ pub(crate) async fn get_media_preview_legacy_legacy_route(
/// - Media will be saved in the media/ directory
pub(crate) async fn create_content_legacy_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<create_content::v3::Request>,
) -> Result<RumaResponse<create_content::v3::Response>> {
create_content_route(State(services), body)
create_content_route(State(services), ClientIp(client), body)
.await
.map(RumaResponse)
}
@@ -126,8 +131,10 @@ pub(crate) async fn create_content_legacy_route(
/// - Only redirects if `allow_redirect` is true
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy", level = "debug")]
pub(crate) async fn get_content_legacy_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content::v3::Request>,
) -> Result<get_content::v3::Response> {
let mxc = Mxc {
@@ -202,11 +209,13 @@ pub(crate) async fn get_content_legacy_route(
/// - Only redirects if `allow_redirect` is true
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy", level = "debug")]
pub(crate) async fn get_content_legacy_legacy_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content::v3::Request>,
) -> Result<RumaResponse<get_content::v3::Response>> {
get_content_legacy_route(State(services), body)
get_content_legacy_route(State(services), ClientIp(client), body)
.await
.map(RumaResponse)
}
@@ -219,8 +228,10 @@ pub(crate) async fn get_content_legacy_legacy_route(
/// - Only redirects if `allow_redirect` is true
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy", level = "debug")]
pub(crate) async fn get_content_as_filename_legacy_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content_as_filename::v3::Request>,
) -> Result<get_content_as_filename::v3::Response> {
let mxc = Mxc {
@@ -296,9 +307,10 @@ pub(crate) async fn get_content_as_filename_legacy_route(
/// seconds
pub(crate) async fn get_content_as_filename_legacy_legacy_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content_as_filename::v3::Request>,
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
get_content_as_filename_legacy_route(State(services), body)
get_content_as_filename_legacy_route(State(services), ClientIp(client), body)
.await
.map(RumaResponse)
}
@@ -311,8 +323,10 @@ pub(crate) async fn get_content_as_filename_legacy_legacy_route(
/// - Only redirects if `allow_redirect` is true
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
/// seconds
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get_legacy", level = "debug")]
pub(crate) async fn get_content_thumbnail_legacy_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content_thumbnail::v3::Request>,
) -> Result<get_content_thumbnail::v3::Response> {
let mxc = Mxc {
@@ -390,9 +404,10 @@ pub(crate) async fn get_content_thumbnail_legacy_route(
/// seconds
pub(crate) async fn get_content_thumbnail_legacy_legacy_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content_thumbnail::v3::Request>,
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
get_content_thumbnail_legacy_route(State(services), body)
get_content_thumbnail_legacy_route(State(services), ClientIp(client), body)
.await
.map(RumaResponse)
}
+14 -7
View File
@@ -1,8 +1,9 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, debug_error, err, info,
matrix::{event::gen_event_id_canonical_json, pdu::PartialPdu},
trace, warn,
warn,
};
use futures::FutureExt;
use ruma::{
@@ -17,14 +18,15 @@
use service::Services;
use super::banned_room_check;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
///
/// Tries to send an invite event into the room.
#[tracing::instrument(skip_all, name = "invite", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "invite", level = "info")]
pub(crate) async fn invite_user_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<invite_user::v3::Request>,
) -> Result<invite_user::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -40,8 +42,14 @@ pub(crate) async fn invite_user_route(
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
}
banned_room_check(&services, sender_user, Some(&body.room_id), body.room_id.server_name())
.await?;
banned_room_check(
&services,
sender_user,
Some(&body.room_id),
body.room_id.server_name(),
client,
)
.await?;
match &body.recipient {
| invite_user::v3::InvitationRecipient::UserId(InviteUserId {
@@ -158,7 +166,7 @@ pub(crate) async fn invite_helper(
let invite_room_state = services
.rooms
.state
.summary_stripped(&pdu, room_id, recipient_user, true)
.summary_stripped(&pdu, room_id, recipient_user)
.await;
drop(state_lock);
@@ -185,7 +193,6 @@ pub(crate) async fn invite_helper(
.await
.ok();
trace!(?request, "Sending invite");
let response = services
.sending
.send_federation_request(recipient_user.server_name(), request)
+24 -8
View File
@@ -1,4 +1,5 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, debug,
result::FlatOk,
@@ -11,7 +12,7 @@
};
use super::banned_room_check;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
///
@@ -21,9 +22,10 @@
/// rules locally
/// - If the server does not know about the room: asks other servers over
/// federation
#[tracing::instrument(skip_all, name = "join", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "join", level = "info")]
pub(crate) async fn join_room_by_id_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<join_room_by_id::v3::Request>,
) -> Result<join_room_by_id::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -31,8 +33,14 @@ pub(crate) async fn join_room_by_id_route(
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
banned_room_check(&services, sender_user, Some(&body.room_id), body.room_id.server_name())
.await?;
banned_room_check(
&services,
sender_user,
Some(&body.room_id),
body.room_id.server_name(),
client,
)
.await?;
// There is no body.server_name for /roomId/join
let mut servers: Vec<_> = services
@@ -83,9 +91,10 @@ pub(crate) async fn join_room_by_id_route(
/// - If the server does not know about the room: use the server name query
/// param if specified. if not specified, asks other servers over federation
/// via room alias server name and room ID server name
#[tracing::instrument(skip_all, name = "join", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "join", level = "info")]
pub(crate) async fn join_room_by_id_or_alias_route(
State(services): State<crate::State>,
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.identity.expect_sender_user()?;
@@ -96,9 +105,15 @@ pub(crate) async fn join_room_by_id_or_alias_route(
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias.clone()) {
| Ok(room_id) => {
banned_room_check(&services, sender_user, Some(&room_id), room_id.server_name())
.boxed()
.await?;
banned_room_check(
&services,
sender_user,
Some(&room_id),
room_id.server_name(),
client,
)
.boxed()
.await?;
let mut servers = body.via.clone();
if servers.is_empty() {
@@ -144,6 +159,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
sender_user,
Some(&room_id),
Some(room_alias.server_name()),
client,
)
.await?;
+13 -4
View File
@@ -1,6 +1,7 @@
use std::{borrow::Borrow, collections::HashMap, iter::once, sync::Arc};
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, debug, debug_info, debug_warn, err, info,
matrix::{
@@ -39,14 +40,15 @@
};
use super::banned_room_check;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `POST /_matrix/client/*/knock/{roomIdOrAlias}`
///
/// Tries to knock the room to ask permission to join for the sender user.
#[tracing::instrument(skip_all, name = "knock", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "knock", level = "info")]
pub(crate) async fn knock_room_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<knock_room::v3::Request>,
) -> Result<knock_room::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -57,8 +59,14 @@ pub(crate) async fn knock_room_route(
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias.clone()) {
| Ok(room_id) => {
banned_room_check(&services, sender_user, Some(&room_id), room_id.server_name())
.await?;
banned_room_check(
&services,
sender_user,
Some(&room_id),
room_id.server_name(),
client,
)
.await?;
let mut servers = body.via.clone();
servers.extend(
@@ -101,6 +109,7 @@ pub(crate) async fn knock_room_route(
sender_user,
Some(&room_id),
Some(room_alias.server_name()),
client,
)
.await?;
+5 -2
View File
@@ -8,6 +8,8 @@
mod members;
mod unban;
use std::net::IpAddr;
use axum::extract::State;
use conduwuit::{Err, Result, warn};
use futures::{FutureExt, StreamExt};
@@ -56,6 +58,7 @@ pub(crate) async fn banned_room_check(
user_id: &UserId,
room_id: Option<&RoomId>,
server_name: Option<&ServerName>,
client_ip: IpAddr,
) -> Result {
if services.users.is_admin(user_id).await {
return Ok(());
@@ -82,7 +85,7 @@ pub(crate) async fn banned_room_check(
.admin
.send_text(&format!(
"Automatically deactivating user {user_id} due to attempted banned \
room join"
room join from IP {client_ip}"
))
.await;
}
@@ -118,7 +121,7 @@ pub(crate) async fn banned_room_check(
.admin
.send_text(&format!(
"Automatically deactivating user {user_id} due to attempted banned \
room join"
room join from IP {client_ip}"
))
.await;
}
+3 -2
View File
@@ -1,4 +1,5 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Error, Result, at, debug_warn,
matrix::{
@@ -37,7 +38,7 @@
};
use ruminuwuity::invite_permission_config::FilterLevel;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// list of safe and common non-state events to ignore if the user is ignored
const IGNORED_MESSAGE_TYPES: &[TimelineEventType] = &[
@@ -71,7 +72,7 @@
/// where the user was joined, depending on `history_visibility`)
pub(crate) async fn get_message_events_route(
State(services): State<crate::State>,
ClientIp(client_ip): ClientIp, // NOTE: Required for updating device metadata
ClientIp(client_ip): ClientIp,
body: Ruma<get_message_events::v3::Request>,
) -> Result<get_message_events::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
+1 -1
View File
@@ -24,5 +24,5 @@ pub(crate) async fn register_client_route(
.await
.map_err(|err| (StatusCode::BAD_REQUEST, Json(err)).into_response())?;
Ok((StatusCode::CREATED, Json(RegisteredClient { client_id, metadata })).into_response())
Ok(Json(RegisteredClient { client_id, metadata }).into_response())
}
+1 -12
View File
@@ -37,17 +37,6 @@ pub(crate) async fn authorization_server_metadata(services: &Services) -> Value
.join(super::BASE_PATH)
.unwrap();
let prompt_values_supported = if services
.uiaa
.registration_flow_status()
.await
.any_available()
{
json!(["create"])
} else {
json!([])
};
json!({
"account_management_uri": endpoint_base.join(ACCOUNT_MANAGEMENT_PATH).unwrap(),
"account_management_actions_supported": [
@@ -63,7 +52,7 @@ pub(crate) async fn authorization_server_metadata(services: &Services) -> Value
"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": prompt_values_supported,
"prompt_values_supported": ["create"],
"registration_endpoint": endpoint_base.join(CLIENT_REGISTER_PATH).unwrap(),
"response_modes_supported": ["query", "fragment"],
"response_types_supported": ["code"],
+3 -2
View File
@@ -1,6 +1,7 @@
use std::collections::BTreeMap;
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, PduCount, Result, err};
use ruma::{
MilliSecondsSinceUnixEpoch,
@@ -12,7 +13,7 @@
},
};
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers`
///
@@ -116,7 +117,7 @@ pub(crate) async fn set_read_marker_route(
/// Sets private read marker and public read receipt EDU.
pub(crate) async fn create_receipt_route(
State(services): State<crate::State>,
ClientIp(client_ip): ClientIp, // NOTE: Required for updating device metadata
ClientIp(client_ip): ClientIp,
body: Ruma<create_receipt::v3::Request>,
) -> Result<create_receipt::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
+3 -2
View File
@@ -1,10 +1,11 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, Result, matrix::pdu::PartialPdu};
use ruma::{
api::client::redact::redact_event, assign, events::room::redaction::RoomRedactionEventContent,
};
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}`
///
@@ -13,7 +14,7 @@
/// - TODO: Handle txn id
pub(crate) async fn redact_event_route(
State(services): State<crate::State>,
ClientIp(client_ip): ClientIp, // NOTE: required for updating device metadata
ClientIp(client_ip): ClientIp,
body: Ruma<redact_event::v3::Request>,
) -> Result<redact_event::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
+8 -4
View File
@@ -1,6 +1,7 @@
use std::{fmt::Write as _, time::Duration};
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, Event, Result, debug_info, info, matrix::pdu::PduEvent, utils::ReadyExt};
use conduwuit_service::Services;
use ruma::{
@@ -13,7 +14,7 @@
};
use tokio::time::sleep;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
struct Report {
sender: OwnedUserId,
@@ -29,9 +30,10 @@ struct Report {
/// # `POST /_matrix/client/v3/rooms/{roomId}/report`
///
/// Reports an abusive room to homeserver admins
#[tracing::instrument(skip_all, name = "report_room", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "report_room", level = "info")]
pub(crate) async fn report_room_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<report_room::v3::Request>,
) -> Result<report_room::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -83,9 +85,10 @@ pub(crate) async fn report_room_route(
/// # `POST /_matrix/client/v3/rooms/{roomId}/report/{eventId}`
///
/// Reports an inappropriate event to homeserver admins
#[tracing::instrument(skip_all, name = "report_event", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "report_event", level = "info")]
pub(crate) async fn report_event_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<report_content::v3::Request>,
) -> Result<report_content::v3::Response> {
// user authentication
@@ -126,9 +129,10 @@ pub(crate) async fn report_event_route(
Ok(report_content::v3::Response::new())
}
#[tracing::instrument(skip_all, name = "report_user", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "report_user", level = "info")]
pub(crate) async fn report_user_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<report_user::v3::Request>,
) -> Result<report_user::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
+4 -2
View File
@@ -1,16 +1,18 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, Result};
use ruma::api::client::room::get_summary;
use service::rooms::summary::Accessibility;
use crate::{Ruma, client_ip::ClientIp, router::ClientIdentity};
use crate::{Ruma, router::ClientIdentity};
/// # `GET /_matrix/client/v1/room_summary/{roomIdOrAlias}`
///
/// Returns a short description of the state of a room.
#[tracing::instrument(skip_all, name = "room_summary", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "room_summary", level = "info")]
pub(crate) async fn get_room_summary(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_summary::v1::Request>,
) -> Result<get_summary::v1::Response> {
let (room_id, servers) = services
+3 -2
View File
@@ -1,11 +1,12 @@
use std::collections::BTreeMap;
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, Result, err, matrix::pdu::PartialPdu, utils};
use ruma::{api::client::message::send_message_event, events::MessageLikeEventType};
use serde_json::from_str;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`
///
@@ -18,7 +19,7 @@
/// allowed
pub(crate) async fn send_message_event_route(
State(services): State<crate::State>,
ClientIp(client_ip): ClientIp, // NOTE: required for updating device metadata
ClientIp(client_ip): ClientIp,
body: Ruma<send_message_event::v3::Request>,
) -> Result<send_message_event::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
+20 -15
View File
@@ -1,6 +1,7 @@
use std::time::Duration;
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, debug, err, info,
utils::{self, ReadyExt, stream::BroadbandExt},
@@ -28,18 +29,18 @@
},
assign,
};
use service::users::DeviceToken;
use super::DEVICE_ID_LENGTH;
use crate::{Ruma, client_ip::ClientIp};
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
use crate::Ruma;
/// # `GET /_matrix/client/v3/login`
///
/// Get the supported login types of this server. One of these should be used as
/// the `type` field when logging in.
#[tracing::instrument(skip_all, name = "login", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "login", level = "info")]
pub(crate) async fn get_login_types_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
_body: Ruma<get_login_types::v3::Request>,
) -> Result<get_login_types::v3::Response> {
if !services.config.oauth.compatibility_mode().uiaa_available() {
@@ -113,10 +114,10 @@ pub async fn handle_login(
/// Note: You can use [`GET
/// /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
/// supported login types.
#[tracing::instrument(skip_all, name = "login", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "login", level = "info")]
pub(crate) async fn login_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp, // NOTE: Required for device metadata
ClientIp(client): ClientIp,
body: Ruma<login::v3::Request>,
) -> Result<login::v3::Response> {
if !services.config.oauth.compatibility_mode().uiaa_available() {
@@ -195,8 +196,8 @@ pub(crate) async fn login_route(
.clone()
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
// Generate a new token for the device
let token = DeviceToken::new_random();
// Generate a new token for the device (ensuring no collisions)
let token = services.users.generate_unique_token().await;
// Determine if device_id was provided and exists in the db for this user
let device_exists = if body.device_id.is_some() {
@@ -212,7 +213,7 @@ pub(crate) async fn login_route(
if device_exists {
services
.users
.set_token(&user_id, &device_id, token.clone())
.set_token(&user_id, &device_id, &token, None)
.await?;
} else {
services
@@ -220,7 +221,8 @@ pub(crate) async fn login_route(
.create_device(
&user_id,
&device_id,
Some(token.clone()),
&token,
None,
body.initial_device_display_name.clone(),
Some(client.to_string()),
)
@@ -239,7 +241,7 @@ pub(crate) async fn login_route(
info!("{user_id} logged in");
#[allow(deprecated)]
Ok(assign!(login::v3::Response::new(user_id, token.into_token(), device_id), {
Ok(assign!(login::v3::Response::new(user_id, token, device_id), {
well_known: client_discovery_info,
expires_in: None,
home_server: Some(services.config.server_name.clone()),
@@ -253,9 +255,10 @@ pub(crate) async fn login_route(
/// to log in with the m.login.token flow.
///
/// <https://spec.matrix.org/v1.13/client-server-api/#post_matrixclientv1loginget_token>
#[tracing::instrument(skip_all, name = "login_token", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "login_token", level = "info")]
pub(crate) async fn login_token_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_login_token::v1::Request>,
) -> Result<get_login_token::v1::Response> {
if !services.config.login_via_existing_session {
@@ -270,7 +273,7 @@ pub(crate) async fn login_token_route(
.authenticate_password(&body.auth, sender_user, body.identity.sender_device(), None)
.await?;
let login_token = DeviceToken::new_random().into_token();
let login_token = utils::random_string(TOKEN_LENGTH);
let expires_in = services.users.create_login_token(sender_user, &login_token);
Ok(get_login_token::v1::Response::new(
@@ -288,9 +291,10 @@ pub(crate) async fn login_token_route(
/// last seen ts)
/// - Forgets to-device events
/// - Triggers device list updates
#[tracing::instrument(skip_all, name = "logout", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "logout", level = "info")]
pub(crate) async fn logout_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<logout::v3::Request>,
) -> Result<logout::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -335,9 +339,10 @@ pub(crate) async fn logout_route(
/// Note: This is equivalent to calling [`GET
/// /_matrix/client/r0/logout`](fn.logout_route.html) from each device of this
/// user.
#[tracing::instrument(skip_all, name = "logout", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "logout", level = "info")]
pub(crate) async fn logout_all_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<logout_all::v3::Request>,
) -> Result<logout_all::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
+4 -3
View File
@@ -1,6 +1,7 @@
#[cfg(test)]
mod tests;
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, err,
matrix::{Event, pdu::PartialPdu},
@@ -27,14 +28,14 @@
};
use serde_json::{json, value::to_raw_value};
use crate::{Ruma, RumaResponse, client_ip::ClientIp};
use crate::{Ruma, RumaResponse};
/// # `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}`
///
/// Sends a state event into the room.
pub(crate) async fn send_state_event_for_key_route(
State(services): State<crate::State>,
ClientIp(ip): ClientIp, // NOTE: Required for updating device metadata
ClientIp(ip): ClientIp,
body: Ruma<send_state_event::v3::Request>,
) -> Result<send_state_event::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
@@ -71,7 +72,7 @@ pub(crate) async fn send_state_event_for_key_route(
/// Sends a state event into the room.
pub(crate) async fn send_state_event_for_empty_key_route(
State(services): State<crate::State>,
ClientIp(ip): ClientIp, // NOTE: Required for updating device metadata
ClientIp(ip): ClientIp,
body: Ruma<send_state_event::v3::Request>,
) -> Result<RumaResponse<send_state_event::v3::Response>> {
send_state_event_for_key_route(State(services), ClientIp(ip), body)
+4 -15
View File
@@ -269,22 +269,13 @@ async fn build_state_and_timeline(
)
.await?;
let (notification_counts, joined_since_last_sync) = try_join(
let (state_events, notification_counts, joined_since_last_sync) = try_join3(
build_state_events(services, sync_context, room_id, shortstatehashes, &timeline),
build_notification_counts(services, sync_context, room_id, &timeline),
check_joined_since_last_sync(services, shortstatehashes, sync_context),
)
.await?;
let state_events = build_state_events(
services,
sync_context,
room_id,
shortstatehashes,
&timeline,
joined_since_last_sync,
)
.await?;
// the timeline should always include at least one PDU if the syncing user
// joined since the last sync, that being the syncing user's join event. if
// it's empty something is wrong.
@@ -468,7 +459,6 @@ async fn build_state_events(
room_id: &RoomId,
shortstatehashes: ShortStateHashes,
timeline: &TimelinePdus,
joined_since_last_sync: bool,
) -> Result<Vec<PduEvent>> {
let SyncContext {
syncing_user,
@@ -498,10 +488,9 @@ async fn build_state_events(
/*
if `last_sync_end_count` is Some (meaning this is an incremental sync), and `last_sync_end_shortstatehash`
is Some (meaning the syncing user didn't just join this room for the first time ever), and `full_state` is false,
and the user didn't just join the room since the last sync, then use `build_state_incremental`.
then use `build_state_incremental`.
*/
| (Some(_), Some(last_sync_end_shortstatehash))
if !full_state && !joined_since_last_sync =>
| (Some(_), Some(last_sync_end_shortstatehash)) if !full_state =>
build_state_incremental(
services,
syncing_user,
+2 -2
View File
@@ -9,6 +9,7 @@
};
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Result, at, error, extract_variant,
utils::{
@@ -47,7 +48,6 @@
is_ignored_invite,
sync::v3::{joined::load_joined_room, left::load_left_room},
},
client_ip::ClientIp,
};
/// The default maximum number of events to return in the `timeline` key of
@@ -181,7 +181,7 @@ fn lazy_loading_enabled(&self) -> bool {
)]
pub(crate) async fn sync_events_route(
State(services): State<crate::State>,
ClientIp(client_ip): ClientIp, // NOTE: Required for updating device metadata
ClientIp(client_ip): ClientIp,
body: Ruma<sync_events::v3::Request>,
) -> Result<sync_events::v3::Response> {
let sender_user = body.identity.expect_sender_user()?;
+2 -2
View File
@@ -6,6 +6,7 @@
};
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Error, Result, at, error, extract_variant, is_equal_to,
matrix::{Event, TypeStateKey, pdu::PduCount},
@@ -48,7 +49,6 @@
client::{
DEFAULT_BUMP_TYPES, TimelinePdus, ignored_filter, is_ignored_invite, sync::load_timeline,
},
client_ip::ClientIp,
};
type SyncInfo<'a> = (&'a UserId, &'a DeviceId, u64, &'a sync_events::v5::Request);
@@ -67,7 +67,7 @@
/// [MSC4186]: https://github.com/matrix-org/matrix-spec-proposals/pull/4186
pub(crate) async fn sync_events_v5_route(
State(ref services): State<crate::State>,
ClientIp(client_ip): ClientIp, // NOTE: Required for updating device metadata
ClientIp(client_ip): ClientIp,
body: Ruma<sync_events::v5::Request>,
) -> Result<sync_events::v5::Response> {
let sender_user = body.identity.expect_sender_user()?;
+3 -2
View File
@@ -1,15 +1,16 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, Result, utils, utils::math::Tried};
use ruma::api::client::typing::create_typing_event::{self, v3::TypingInfo};
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}`
///
/// Sets the typing state of the sender user.
pub(crate) async fn create_typing_event_route(
State(services): State<crate::State>,
ClientIp(ip): ClientIp, // NOTE: Required for updating device metadata
ClientIp(ip): ClientIp,
body: Ruma<create_typing_event::v3::Request>,
) -> Result<create_typing_event::v3::Response> {
use create_typing_event::v3::Typing;
-135
View File
@@ -1,135 +0,0 @@
use std::{
convert::Infallible,
net::{IpAddr, Ipv4Addr, SocketAddr},
};
use axum::extract::{ConnectInfo, FromRequestParts};
use axum_client_ip::ClientIp as SourcedClientIp;
use conduwuit::debug_warn;
use http::request::Parts;
const UNKNOWN_IP: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
/// [`ClientIp`] extractor that falls back to the connection peer address
/// instead of rejecting the request when `request_ip_source` can't be resolved.
#[derive(Debug, Clone, Copy)]
pub(crate) struct ClientIp(pub IpAddr);
impl<S> FromRequestParts<S> for ClientIp
where
S: Sync,
{
type Rejection = Infallible;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let ip = match SourcedClientIp::from_request_parts(parts, state).await {
| Ok(SourcedClientIp(ip)) => ip,
| Err(rejection) => {
debug_warn!(
%rejection,
"Could not resolve client IP from request_ip_source; using peer address"
);
parts
.extensions
.get::<ConnectInfo<SocketAddr>>()
.map_or(UNKNOWN_IP, |ConnectInfo(addr)| addr.ip())
},
};
Ok(Self(ip))
}
}
#[cfg(test)]
mod tests {
use std::net::Ipv6Addr;
use axum_client_ip::ClientIpSource;
use super::*;
const PEER: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(198, 51, 100, 7)), 8448);
const PEER_V6: SocketAddr =
SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0x2001, 0xDB8, 0, 0, 0, 0, 0, 1)), 8448);
fn parts(
source: Option<ClientIpSource>,
peer: Option<SocketAddr>,
headers: &[(&str, &str)],
) -> Parts {
let mut builder = http::Request::builder();
for (name, value) in headers {
builder = builder.header(*name, *value);
}
let (mut parts, ()) = builder.body(()).unwrap().into_parts();
if let Some(source) = source {
parts.extensions.insert(source);
}
if let Some(peer) = peer {
parts.extensions.insert(ConnectInfo(peer));
}
parts
}
async fn extract(mut parts: Parts) -> IpAddr {
let ClientIp(ip) = ClientIp::from_request_parts(&mut parts, &()).await.unwrap();
ip
}
#[tokio::test]
async fn resolves_from_configured_source() {
let parts =
parts(Some(ClientIpSource::XRealIp), Some(PEER), &[("x-real-ip", "203.0.113.5")]);
assert_eq!(extract(parts).await, IpAddr::V4(Ipv4Addr::new(203, 0, 113, 5)));
}
#[tokio::test]
async fn resolves_ipv6_from_configured_source() {
let parts =
parts(Some(ClientIpSource::XRealIp), Some(PEER), &[("x-real-ip", "2001:db8::2")]);
assert_eq!(
extract(parts).await,
IpAddr::V6(Ipv6Addr::new(0x2001, 0xDB8, 0, 0, 0, 0, 0, 2))
);
}
#[tokio::test]
async fn falls_back_to_peer_when_header_missing() {
let parts = parts(Some(ClientIpSource::XRealIp), Some(PEER), &[]);
assert_eq!(extract(parts).await, PEER.ip());
}
#[tokio::test]
async fn falls_back_to_ipv6_peer_when_header_missing() {
let parts = parts(Some(ClientIpSource::XRealIp), Some(PEER_V6), &[]);
assert_eq!(extract(parts).await, PEER_V6.ip());
}
#[tokio::test]
async fn falls_back_to_peer_when_header_unparsable() {
let parts =
parts(Some(ClientIpSource::XRealIp), Some(PEER), &[("x-real-ip", "not-an-ip")]);
assert_eq!(extract(parts).await, PEER.ip());
}
#[tokio::test]
async fn falls_back_to_peer_when_source_unset() {
let parts = parts(None, Some(PEER), &[("x-real-ip", "203.0.113.5")]);
assert_eq!(extract(parts).await, PEER.ip());
}
#[tokio::test]
async fn falls_back_to_unspecified_without_peer() {
let parts = parts(Some(ClientIpSource::XRealIp), None, &[]);
assert_eq!(extract(parts).await, UNKNOWN_IP);
}
}
-2
View File
@@ -11,8 +11,6 @@
pub mod router;
pub mod server;
pub(crate) mod client_ip;
pub mod admin;
pub(crate) use self::router::{Ruma, RumaResponse, State};
+3 -2
View File
@@ -15,9 +15,10 @@
#[derive(Deserialize)]
pub(crate) struct AuthQueryParams {
pub(super) user_id: Option<String>,
/// Device ID for appservice device masquerading (MSC3202/MSC4190).
/// Can be provided as `device_id` or `org.matrix.msc3202.device_id`.
#[serde(alias = "org.matrix.msc3202.device_id")]
pub(super) device_id: Option<String>,
#[serde(rename = "org.matrix.msc3202.device_id")]
pub(super) legacy_device_id: Option<String>,
}
/// Extractor for Ruma request structs
+18 -22
View File
@@ -219,29 +219,25 @@ async fn verify<B: AsRef<[u8]> + Sync>(
// MSC3202/MSC4190: Handle device_id masquerading for appservices.
// The device_id can be provided via `device_id` or
// `org.matrix.msc3202.device_id` query parameter.
let sender_device = if let Some(device_id) = query
.device_id
.or(query.legacy_device_id)
.as_deref()
.map(Into::into)
{
// Verify the device exists for this user
if services
.users
.get_device_metadata(&sender_user, device_id)
.await
.is_err()
{
return Err!(Request(Forbidden(
"Device does not exist for user or appservice cannot masquerade as this \
device."
)));
}
let sender_device =
if let Some(device_id) = query.device_id.as_deref().map(Into::into) {
// Verify the device exists for this user
if services
.users
.get_device_metadata(&sender_user, device_id)
.await
.is_err()
{
return Err!(Request(Forbidden(
"Device does not exist for user or appservice cannot masquerade as \
this device."
)));
}
Some(device_id.to_owned())
} else {
None
};
Some(device_id.to_owned())
} else {
None
};
Ok(ClientIdentity::Appservice {
sender_user,
+119 -301
View File
@@ -1,47 +1,57 @@
use std::collections::{HashMap, hash_map::Entry};
use axum::extract::State;
use axum_client_ip::ClientIp;
use base64::{Engine as _, engine::general_purpose};
use conduwuit::{
Err, Error, EventTypeExt, PduEvent, Result, debug, err, error,
matrix::{Event, StateKey},
result::FlatOk,
state_res, trace,
utils::hash::sha256,
Err, Error, PduEvent, Result, err, error,
matrix::{Event, event::gen_event_id},
utils::{self, hash::sha256},
warn,
};
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedUserId, ServerName,
UserId,
CanonicalJsonValue, UserId,
api::{
error::{ErrorKind, IncompatibleRoomVersionErrorData},
federation::membership::{RawStrippedState, create_invite},
},
events::{StateEventType, room::member::MembershipState},
room_version_rules::RoomVersionRules,
events::room::member::{MembershipState, RoomMemberEventContent},
serde::JsonObject,
};
use serde::Deserialize;
use crate::{Ruma, client_ip::ClientIp, server::utils::validate_any_membership_event};
use crate::Ruma;
/// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
///
/// Invites a remote user to a room.
#[tracing::instrument(skip_all, name = "invite", level = "info")]
#[tracing::instrument(skip_all, fields(%client), name = "invite", level = "info")]
pub(crate) async fn create_invite_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<create_invite::v2::Request>,
) -> Result<create_invite::v2::Response> {
// ACL check origin
services
.rooms
.event_handler
.acl_check(&body.identity, &body.room_id)
.await?;
if !services.server.supported_room_version(&body.room_version) {
return Err(Error::BadRequest(
ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData::new(
body.room_version.clone(),
)),
"This server does not support that room version",
"Server does not support this room version.",
));
}
let room_version_rules = body.room_version.rules().unwrap();
if let Some(server) = body.room_id.server_name() {
if services.moderation.is_remote_server_forbidden(server) {
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
}
if services
.moderation
.is_remote_server_forbidden(&body.identity)
@@ -51,61 +61,90 @@ pub(crate) async fn create_invite_route(
body.identity, body.room_id
);
return Err!(Request(Forbidden("Federation denied with {}", body.identity)));
}
// First, validate the invite room state, so we can compare with the create
// event.
debug!(
event_id=%body.event_id,
room_id=%body.room_id,
room_version=?body.room_version,
via=?body.via,
"Validating invite room state for invite request"
);
let (create_event_id, state) = validate_invite_state(
&services,
&body.invite_room_state,
&room_version_rules,
body.room_id.clone(),
)
.await?;
let create_event_json = state
.get(&StateEventType::RoomCreate.with_state_key(""))
.expect("must have create event in invite state by this point");
// We can now perform the banned remote server check with the create event.
// N.B. this checks the sender field, which is technically incorrect for rooms
// v10 and below. This usually isn't the case though so sue me
let creator = create_event_json
.get("sender")
.and_then(|v| v.as_str())
.map(UserId::parse)
.flat_ok()
.expect("must have valid sender in create event");
if services
.moderation
.is_remote_server_forbidden(creator.server_name())
{
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
// And then we can validate the member event itself
let (mut signed_event, sender_user, recipient_user) = validate_invite_membership_event(
&services,
&body.event,
&room_version_rules,
&body.identity,
create_event_id.clone(),
body.room_id.clone(),
body.event_id.clone(),
let mut signed_event = utils::to_canonical_object(&body.event)
.map_err(|_| err!(Request(InvalidParam("Invite event is invalid."))))?;
// Ensure this is a membership event
if signed_event
.get("type")
.expect("event must have a type")
.as_str()
.expect("type must be a string")
!= "m.room.member"
{
return Err!(Request(BadJson(
"Not allowed to send non-membership event to invite endpoint."
)));
}
let content: RoomMemberEventContent = serde_json::from_value(
signed_event
.get("content")
.ok_or_else(|| err!(Request(BadJson("Event missing content property"))))?
.clone()
.into(),
)
.await?;
.map_err(|e| err!(Request(BadJson(warn!("Event content is empty or invalid: {e}")))))?;
// Ensure this is an invite membership event
if content.membership != MembershipState::Invite {
return Err!(Request(BadJson(
"Not allowed to send a non-invite membership event to invite endpoint."
)));
}
// Ensure the sending user isn't a lying bozo
let sender_user = signed_event
.get("sender")
.and_then(|v| v.as_str())
.map(UserId::parse)
.and_then(Result::ok)
.ok_or_else(|| err!(Request(InvalidParam("Invalid sender property"))))?;
if sender_user.server_name() != body.identity {
return Err!(Request(Forbidden("Sender's server does not match the origin server.",)));
}
// Ensure the target user belongs to this server
let recipient_user = signed_event
.get("state_key")
.and_then(|v| v.as_str())
.map(UserId::parse)
.and_then(Result::ok)
.ok_or_else(|| err!(Request(InvalidParam("Invalid state_key property"))))?;
if !services
.globals
.server_is_ours(recipient_user.server_name())
{
return Err!(Request(InvalidParam("User does not belong to this homeserver.")));
}
// Make sure we're not ACL'ed from their room.
services
.rooms
.event_handler
.acl_check(recipient_user.server_name(), &body.room_id)
.await?;
services
.server_keys
.hash_and_sign_event(&mut signed_event, &room_version_rules)
.map_err(|e| err!(Request(InvalidParam("Failed to sign event: {e}"))))?;
// Generate event id
let event_id = gen_event_id(&signed_event, &room_version_rules)?;
// Add event_id back
signed_event.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.to_string()));
if services.rooms.metadata.is_banned(&body.room_id).await
&& !services.users.is_admin(&recipient_user).await
{
return Err!(Request(Forbidden("That room is banned on this homeserver.")));
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
}
if services.config.block_non_admin_invites && !services.users.is_admin(&recipient_user).await
@@ -122,57 +161,30 @@ pub(crate) async fn create_invite_route(
return Err!(Request(Forbidden("Invite rejected by antispam service.")));
}
// If we're already in the room, ensure that neither the origin nor ourselves
// are ACL'd.
let resident = services
.rooms
.state_cache
.server_in_room(services.globals.server_name(), &body.room_id)
.await;
if resident {
services
.rooms
.event_handler
.acl_check(&body.identity, &body.room_id)
.await?;
services
.rooms
.event_handler
.acl_check(recipient_user.server_name(), &body.room_id)
.await
.map_err(|_| err!(Request(Forbidden("This server is ACL'd from that room"))))?;
}
services
.server_keys
.hash_and_sign_event(&mut signed_event, &room_version_rules)
.map_err(|e| err!(Request(InvalidParam("Failed to sign event: {e}"))))?;
// Add event_id back
signed_event
.insert("event_id".to_owned(), CanonicalJsonValue::String(body.event_id.to_string()));
let mut invite_state = body.invite_room_state.clone();
let pdu = PduEvent::from_id_val(&body.event_id, signed_event.clone())
.expect("must be able to create PDU object");
invite_state.push(RawStrippedState::Pdu(serde_json::value::to_raw_value(&signed_event)?));
let mut event: JsonObject = serde_json::from_str(body.event.get())
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?;
event.insert("event_id".to_owned(), "$placeholder".into());
let pdu: PduEvent = serde_json::from_value(event.into())
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?;
invite_state.push(RawStrippedState::Pdu(
serde_json::value::to_raw_value(&pdu).expect("PDU was just created, it must be valid"),
));
// If we are active in the room, the remote server will notify us about the
// join/invite through /send. If we are not in the room, we need to manually
// record the invited state for client /sync through update_membership(), and
// send the invite PDU to the relevant appservices.
if !resident {
// We will start by recording the room's create event as an outlier.
// This will allow us to recognise it later in case the sender revokes the
// invite over federation later. We could store more state from the invite
// request, but we will get that during send_join anyway.
// This is safe to just add directly as an outlier as we already auth checked it
// during validation.
services
.rooms
.outlier
.add_pdu_outlier(&create_event_id, create_event_json);
if !services
.rooms
.state_cache
.server_in_room(services.globals.server_name(), &body.room_id)
.await
{
services
.rooms
.state_cache
@@ -228,197 +240,3 @@ pub(crate) async fn create_invite_route(
.await,
))
}
/// Validates the *membership event* in the invite request, per the steps listed
/// under the invite endpoint's [spec].
///
/// Returns the validated JSON body, sender user ID, and recipient user ID.
///
/// Since this function performs a PDU format check, the create event must be
/// known ahead of time. This implies validating the invite state before the
/// invite event itself.
///
/// [spec]: https://spec.matrix.org/v1.19/server-server-api/#put_matrixfederationv2inviteroomideventid
async fn validate_invite_membership_event(
services: &crate::State,
body: &serde_json::value::RawValue,
room_version_rules: &RoomVersionRules,
origin: &ServerName,
create_event_id: OwnedEventId,
room_id: OwnedRoomId,
event_id: OwnedEventId,
) -> Result<(CanonicalJsonObject, OwnedUserId, OwnedUserId)> {
trace!(?body, "Invite membership event");
let (pdu, target_membership, sender_user, recipient_user) = validate_any_membership_event(
services,
body,
room_version_rules,
create_event_id,
room_id,
event_id,
)
.await?;
// Ensure the sender belongs to the remote that is sending the invite
if sender_user.server_name() != origin {
return Err!(Request(Forbidden("Sender belongs to a different server")));
}
// Ensure the target user belongs to this server
if !services
.globals
.server_is_ours(recipient_user.server_name())
{
return Err!(Request(InvalidParam("Recipient does not belong to this homeserver")));
}
if target_membership != MembershipState::Invite {
return Err!(Request(BadJson("Invalid membership (expected `invite`)")));
}
Ok((pdu, sender_user, recipient_user))
}
/// Validates the *invite state* of an invite request, per the steps listed
/// under the endpoint's [spec].
///
/// Returns the create event's event ID, and the partial state map.
///
/// [spec]: https://spec.matrix.org/v1.19/server-server-api/#put_matrixfederationv2inviteroomideventid
async fn validate_invite_state(
services: &crate::State,
invite_state: &[RawStrippedState],
room_version_rules: &RoomVersionRules,
room_id: OwnedRoomId,
) -> Result<(OwnedEventId, HashMap<(StateEventType, StateKey), CanonicalJsonObject>)> {
trace!(?invite_state, "Raw invite state");
let mut invite_state_map: HashMap<(StateEventType, StateKey), _> =
HashMap::with_capacity(invite_state.len());
let mut create_event_id: Option<OwnedEventId> = None;
for (idx, invite_state_event) in invite_state.iter().cloned().enumerate() {
trace!(%idx, ?invite_state_event, "Invite state event");
// Stripped state hasn't been sent over federation since v1.16.
let RawStrippedState::Pdu(raw_pdu) = invite_state_event else {
debug!(%idx, "Invite state event is not a PDU");
return Err!(Request(InvalidParam(
"PDU in invite state (index {idx}) violates the room event format"
)));
};
let (state_event_room_id, state_event_id, state_event_json) = services
.rooms
.event_handler
.parse_incoming_pdu(&raw_pdu, Some(room_version_rules))
.await
.map_err(|e| {
err!(Request(InvalidParam(debug_warn!("Invalid PDU in invite state: {e}"))))
})?;
if state_event_room_id != room_id {
return Err!(Request(InvalidParam(debug_warn!(
%state_event_room_id,
%room_id,
"PDU in invite state ({state_event_id}) belongs to the wrong room"
))));
}
services
.server_keys
.verify_event(&state_event_json, room_version_rules)
.await
.map_err(|e| {
err!(Request(InvalidParam("Signature verification failed on invite event: {e}")))
})?;
let Some(state_key) = state_event_json.get("state_key").and_then(|k| k.as_str()) else {
return Err!(Request(InvalidParam(debug_info!(
"PDU in invite state ({state_event_id}) is not a state event"
))));
};
let Some(event_type) = state_event_json.get("type").and_then(|k| k.as_str()) else {
return Err!(Request(InvalidParam(debug_warn!(
"PDU in invite state ({state_event_id}) is not an event?"
))));
};
let key = StateEventType::from(event_type).with_state_key(state_key);
match invite_state_map.entry(key) {
| Entry::Occupied(entry) =>
return Err!(Request(InvalidParam(
"Duplicate state events in invite state for state key: {:?}",
entry.key(),
))),
| Entry::Vacant(entry) => {
if entry.key().0 == StateEventType::RoomCreate {
// Ensure this is a legal create event.
let pdu_event =
PduEvent::from_id_val(&state_event_id, state_event_json.clone())
.expect("must be able to create pdu event from event json");
debug!("Validating discovered create event in invite room state");
validate_invite_create_event(&pdu_event, room_version_rules).await?;
create_event_id = Some(state_event_id);
}
entry.insert(state_event_json);
},
}
}
let Some(create_event_id) = create_event_id else {
return Err!(Request(InvalidParam(debug_warn!(
parsed_state=?invite_state_map,
"Invite state does not contain the m.room.create event"
))));
};
invite_state_map.iter().try_for_each(|(key, event_json)| {
service::rooms::event_handler::Service::pdu_format_check_1(
event_json,
room_version_rules,
&create_event_id,
)
.map_err(|e| {
err!(Request(InvalidParam(
"PDU in invite state for {key:?} violates the room event format: {e}"
)))
})
})?;
Ok((create_event_id, invite_state_map))
}
#[derive(Deserialize)]
struct MFederate {
#[serde(rename = "m.federate")]
mfederate: Option<bool>,
}
/// Validates that a create event is suitable for the invite, namely:
///
/// 1. It passes auth checks (aka is valid)
/// 2. The room is federated (there's no point persisting unfederated rooms)
async fn validate_invite_create_event(
pdu: &PduEvent,
room_version_rules: &RoomVersionRules,
) -> Result {
if !state_res::auth_check(
room_version_rules,
pdu,
None,
|_, _| async {
unreachable!("No state should be fetched when processing a lone create event");
},
pdu,
)
.await
.unwrap_or_default()
{
return Err!(Request(InvalidParam("m.room.create event fails auth check")));
}
let can_federate = pdu.get_content::<MFederate>()?.mfederate;
if !can_federate.unwrap_or(true) {
return Err!(Request(InvalidParam(
"Cannot receive invites to a room with m.federate=false"
)));
}
Ok(())
}
+8
View File
@@ -75,6 +75,14 @@ pub(crate) async fn create_join_event_template_route(
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
if let Some(server) = body.room_id.server_name() {
if services.moderation.is_remote_server_forbidden(server) {
return Err!(Request(Forbidden(warn!(
"Room ID server name {server} is banned on this homeserver."
))));
}
}
let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
let room_version_rules = room_version.rules().unwrap();
if !body.ver.contains(&room_version) {
+6
View File
@@ -58,6 +58,12 @@ pub(crate) async fn create_knock_event_template_route(
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
if let Some(server) = body.room_id.server_name() {
if services.moderation.is_remote_server_forbidden(server) {
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
}
let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
let room_version_rules = room_version.rules().unwrap();
-1
View File
@@ -27,7 +27,6 @@ pub(crate) async fn create_leave_event_template_route(
{
info!(
origin = body.identity.as_str(),
room_id = %body.room_id,
"Refusing to serve make_leave for room we aren't participating in"
);
return Err!(Request(NotFound("This server is not participating in that room.")));
+16 -3
View File
@@ -1,4 +1,5 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, Result, utils::content_disposition::make_content_disposition};
use conduwuit_service::media::{Dim, FileMeta};
use ruma::api::federation::authenticated_media::{
@@ -6,14 +7,20 @@
};
use service::media::mxc::Mxc;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `GET /_matrix/federation/v1/media/download/{mediaId}`
///
/// Load media from our server.
#[tracing::instrument(name = "media_get", level = "debug", skip_all)]
#[tracing::instrument(
name = "media_get",
level = "debug",
skip_all,
fields(%client)
)]
pub(crate) async fn get_content_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content::v1::Request>,
) -> Result<get_content::v1::Response> {
let mxc = Mxc {
@@ -47,9 +54,15 @@ pub(crate) async fn get_content_route(
/// # `GET /_matrix/federation/v1/media/thumbnail/{mediaId}`
///
/// Load media thumbnail from our server.
#[tracing::instrument(name = "media_thumbnail_get", level = "debug", skip_all)]
#[tracing::instrument(
name = "media_thumbnail_get",
level = "debug",
skip_all,
fields(%client)
)]
pub(crate) async fn get_content_thumbnail_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_content_thumbnail::v1::Request>,
) -> Result<get_content_thumbnail::v1::Response> {
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
+6 -3
View File
@@ -1,4 +1,5 @@
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{Err, Result, err};
use ruma::{
api::federation::directory::{get_public_rooms, get_public_rooms_filtered},
@@ -6,14 +7,15 @@
directory::Filter,
};
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
/// # `POST /_matrix/federation/v1/publicRooms`
///
/// Lists the public rooms on this server.
#[tracing::instrument(name = "publicrooms", level = "debug", skip_all)]
#[tracing::instrument(name = "publicrooms", level = "debug", skip_all, fields(%client))]
pub(crate) async fn get_public_rooms_filtered_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_public_rooms_filtered::v1::Request>,
) -> Result<get_public_rooms_filtered::v1::Response> {
if !services
@@ -46,9 +48,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
/// # `GET /_matrix/federation/v1/publicRooms`
///
/// Lists the public rooms on this server.
#[tracing::instrument(name = "publicrooms", level = "debug", skip_all)]
#[tracing::instrument(name = "publicrooms", level = "debug", skip_all, fields(%client))]
pub(crate) async fn get_public_rooms_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<get_public_rooms::v1::Request>,
) -> Result<get_public_rooms::v1::Response> {
if !services
+35 -15
View File
@@ -1,9 +1,11 @@
use std::{
collections::{BTreeMap, HashMap},
net::IpAddr,
time::{Duration, Instant},
};
use axum::extract::State;
use axum_client_ip::ClientIp;
use conduwuit::{
Err, Error, Result, debug, debug_error, debug_warn, err, error,
result::LogErr,
@@ -45,7 +47,7 @@
use tokio::sync::watch::{Receiver, Sender};
use tracing::instrument;
use crate::{Ruma, client_ip::ClientIp};
use crate::Ruma;
type ResolvedMap = BTreeMap<OwnedEventId, Result>;
type Pdu = (OwnedRoomId, OwnedEventId, CanonicalJsonObject);
@@ -55,6 +57,7 @@
/// Push EDUs and PDUs to this server.
pub(crate) async fn send_transaction_message_route(
State(services): State<crate::State>,
ClientIp(client): ClientIp,
body: Ruma<send_transaction_message::v1::Request>,
) -> Result<send_transaction_message::v1::Response> {
if body.identity != body.body.origin {
@@ -95,7 +98,7 @@ pub(crate) async fn send_transaction_message_route(
services
.server
.runtime()
.spawn(process_inbound_transaction(services, body, txn_key, sender));
.spawn(process_inbound_transaction(services, body, client, txn_key, sender));
// and wait for it
wait_for_result(receiver).await
},
@@ -138,6 +141,7 @@ async fn wait_for_result(
async fn process_inbound_transaction(
services: crate::State,
body: Ruma<send_transaction_message::v1::Request>,
client: IpAddr,
txn_key: TxnKey,
sender: Sender<WrappedTransactionResponse>,
) {
@@ -146,7 +150,7 @@ async fn process_inbound_transaction(
.pdus
.iter()
.stream()
.broad_then(|pdu| services.rooms.event_handler.parse_incoming_pdu(pdu, None))
.broad_then(|pdu| services.rooms.event_handler.parse_incoming_pdu(pdu))
.inspect_err(|e| warn!("Could not parse incoming PDU: {e}"))
.ready_filter_map(Result::ok);
@@ -159,7 +163,7 @@ async fn process_inbound_transaction(
.stream();
debug!(pdus = body.pdus.len(), edus = body.edus.len(), "Processing transaction",);
let results = match handle(&services, &body.identity, pdus, edus).await {
let results = match handle(&services, &client, &body.identity, pdus, edus).await {
| Ok(results) => results,
| Err(err) => {
fail_federation_txn(services, &txn_key, &sender, err);
@@ -232,6 +236,7 @@ fn transaction_error_to_response(err: &TransactionError) -> Error {
}
async fn handle(
services: &Services,
client: &IpAddr,
origin: &ServerName,
pdus: impl Stream<Item = Pdu> + Send,
edus: impl Stream<Item = Edu> + Send,
@@ -252,7 +257,7 @@ async fn handle(
.into_iter()
.try_stream()
.broad_and_then(|(room_id, pdus): (_, Vec<_>)| {
handle_room(services, origin, room_id, pdus.into_iter())
handle_room(services, client, origin, room_id, pdus.into_iter())
.map_ok(Vec::into_iter)
.map_ok(IterStream::try_stream)
})
@@ -262,7 +267,7 @@ async fn handle(
.await?;
// Evaluate EDUs after PDUs in case some of the PDUs then forbid some EDUs.
edus.for_each_concurrent(automatic_width(), |edu| handle_edu(services, origin, edu))
edus.for_each_concurrent(automatic_width(), |edu| handle_edu(services, client, origin, edu))
.boxed()
.await;
@@ -271,6 +276,7 @@ async fn handle(
async fn handle_room(
services: &Services,
_client: &IpAddr,
origin: &ServerName,
room_id: OwnedRoomId,
pdus: impl Iterator<Item = Pdu> + Send,
@@ -317,25 +323,25 @@ async fn handle_room(
Ok(results)
}
async fn handle_edu(services: &Services, origin: &ServerName, edu: Edu) {
async fn handle_edu(services: &Services, client: &IpAddr, origin: &ServerName, edu: Edu) {
match edu {
| Edu::Presence(presence) if services.server.config.allow_incoming_presence =>
handle_edu_presence(services, origin, presence).await,
handle_edu_presence(services, client, origin, presence).await,
| Edu::Receipt(receipt) if services.server.config.allow_incoming_read_receipts =>
handle_edu_receipt(services, origin, receipt).await,
handle_edu_receipt(services, client, origin, receipt).await,
| Edu::Typing(typing) if services.server.config.allow_incoming_typing =>
handle_edu_typing(services, origin, typing).await,
handle_edu_typing(services, client, origin, typing).await,
| Edu::DeviceListUpdate(content) =>
handle_edu_device_list_update(services, origin, content).await,
handle_edu_device_list_update(services, client, origin, content).await,
| Edu::DirectToDevice(content) =>
handle_edu_direct_to_device(services, origin, content).await,
handle_edu_direct_to_device(services, client, origin, content).await,
| Edu::SigningKeyUpdate(content) =>
handle_edu_signing_key_update(services, origin, content).await,
handle_edu_signing_key_update(services, client, origin, content).await,
| Edu::_Custom(ref _custom) => debug_warn!(?edu, "received custom/unknown EDU"),
@@ -345,6 +351,7 @@ async fn handle_edu(services: &Services, origin: &ServerName, edu: Edu) {
async fn handle_edu_presence(
services: &Services,
_client: &IpAddr,
origin: &ServerName,
presence: PresenceContent,
) {
@@ -385,7 +392,12 @@ async fn handle_edu_presence_update(
.ok();
}
async fn handle_edu_receipt(services: &Services, origin: &ServerName, receipt: ReceiptContent) {
async fn handle_edu_receipt(
services: &Services,
_client: &IpAddr,
origin: &ServerName,
receipt: ReceiptContent,
) {
receipt
.receipts
.into_iter()
@@ -480,7 +492,12 @@ async fn handle_edu_receipt_room_user(
.await;
}
async fn handle_edu_typing(services: &Services, origin: &ServerName, typing: TypingContent) {
async fn handle_edu_typing(
services: &Services,
_client: &IpAddr,
origin: &ServerName,
typing: TypingContent,
) {
if typing.user_id.server_name() != origin {
debug_warn!(
%typing.user_id, %origin,
@@ -540,6 +557,7 @@ async fn handle_edu_typing(services: &Services, origin: &ServerName, typing: Typ
async fn handle_edu_device_list_update(
services: &Services,
_client: &IpAddr,
origin: &ServerName,
content: DeviceListUpdateContent,
) {
@@ -558,6 +576,7 @@ async fn handle_edu_device_list_update(
async fn handle_edu_direct_to_device(
services: &Services,
_client: &IpAddr,
origin: &ServerName,
content: DirectDeviceContent,
) {
@@ -677,6 +696,7 @@ async fn handle_edu_direct_to_device_event(
async fn handle_edu_signing_key_update(
services: &Services,
_client: &IpAddr,
origin: &ServerName,
content: SigningKeyUpdateContent,
) {
+115 -67
View File
@@ -2,36 +2,34 @@
use axum::extract::State;
use conduwuit::{
Err, Event, Result, at, debug, err, info, trace,
Err, Event, Result, at, debug, err, info,
matrix::event::gen_event_id_canonical_json,
trace,
utils::stream::{BroadbandExt, IterStream, TryBroadbandExt},
warn,
};
use conduwuit_service::Services;
use futures::{FutureExt, StreamExt, TryStreamExt};
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, RoomId, ServerName, UserId,
CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, ServerName,
api::federation::membership::create_join_event,
assign,
events::{
TimelineEventType,
StateEventType, TimelineEventType,
room::member::{MembershipState, RoomMemberEventContent},
},
room_version_rules::RoomVersionRules,
};
use serde_json::value::to_raw_value;
use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
use crate::{Ruma, server::utils::validate_any_membership_event};
use crate::Ruma;
/// Creates a join membership event for the target, returning a computed
/// response with room state, auth chains, etc.
/// helper method for /send_join v1 and v2
#[tracing::instrument(skip(services, pdu, omit_members), fields(room_id = room_id.as_str(), origin = origin.as_str()), level = "info")]
async fn create_join_event(
services: &Services,
origin: &ServerName,
room_id: &RoomId,
event_id: &EventId,
room_version_rules: &RoomVersionRules,
mut pdu: CanonicalJsonObject,
pdu: &RawJsonValue,
omit_members: bool,
) -> Result<create_join_event::v2::RoomState> {
if !services.rooms.metadata.exists(room_id).await {
@@ -45,7 +43,6 @@ async fn create_join_event(
{
info!(
origin = origin.as_str(),
room_id = %room_id,
"Refusing to serve send_join for room we aren't participating in"
);
return Err!(Request(NotFound("This server is not participating in that room.")));
@@ -67,25 +64,94 @@ async fn create_join_event(
.await
.map_err(|e| err!(Request(NotFound(error!("Room has no state: {e}")))))?;
let sender = pdu
.get("sender")
.and_then(|v| v.as_str())
.map(UserId::parse)
.and_then(Result::ok)
.expect("sender was already validated");
// We do not add the event_id field to the pdu here because of signature and
// hashes checks
trace!("Getting room version");
let room_version = services.rooms.state.get_room_version(room_id).await?;
let room_version_rules = room_version.rules().unwrap();
trace!("Generating event ID and converting to canonical json");
let Ok((event_id, mut value)) = gen_event_id_canonical_json(pdu, &room_version_rules) else {
// Event could not be converted to canonical json
return Err!(Request(BadJson("Could not convert event to canonical json.")));
};
let event_room_id: OwnedRoomId = serde_json::from_value(
value
.get("room_id")
.ok_or_else(|| err!(Request(BadJson("Event missing room_id property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("room_id field is not a valid room ID: {e}")))))?;
if event_room_id != room_id {
return Err!(Request(BadJson("Event room_id does not match request path room ID.")));
}
let event_type: StateEventType = serde_json::from_value(
value
.get("type")
.ok_or_else(|| err!(Request(BadJson("Event missing type property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("Event has invalid state event type: {e}")))))?;
if event_type != StateEventType::RoomMember {
return Err!(Request(BadJson(
"Not allowed to send non-membership state event to join endpoint."
)));
}
let content: RoomMemberEventContent = serde_json::from_value(
pdu.get("content")
value
.get("content")
.ok_or_else(|| err!(Request(BadJson("Event missing content property"))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("Event content is empty or invalid: {e}")))))?;
if content.membership != MembershipState::Join {
return Err!(Request(BadJson(
"Not allowed to send a non-join membership event to join endpoint."
)));
}
let sender: OwnedUserId = serde_json::from_value(
value
.get("sender")
.ok_or_else(|| err!(Request(BadJson("Event missing sender property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("sender property is not a valid user ID: {e}")))))?;
// check if origin server is trying to send for another server
if sender.server_name() != origin {
return Err!(Request(Forbidden("Not allowed to join on behalf of another server.")));
}
let state_key: OwnedUserId = serde_json::from_value(
value
.get("state_key")
.ok_or_else(|| err!(Request(BadJson("Event missing state_key property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("State key is not a valid user ID: {e}")))))?;
if state_key != sender {
return Err!(Request(BadJson("State key does not match sender user.")));
}
if let Some(authorising_user) = content.join_authorized_via_users_server {
if !room_version_rules.authorization.restricted_join_rule {
use ruma::RoomVersionId::*;
if matches!(room_version, V1 | V2 | V3 | V4 | V5 | V6 | V7) {
return Err!(Request(InvalidParam(
"Room version does not support restricted rooms but \
"Room version {room_version} does not support restricted rooms but \
join_authorised_via_users_server ({authorising_user}) was found in the event."
)));
}
@@ -108,7 +174,8 @@ async fn create_join_event(
they cannot authorise your join."
)));
}
if !super::user_can_perform_restricted_join(services, &sender, room_id).await? {
if !super::user_can_perform_restricted_join(services, &state_key, room_id).await? {
return Err!(Request(UnableToAuthorizeJoin(
"Joining user did not pass restricted room's rules."
)));
@@ -116,7 +183,7 @@ async fn create_join_event(
services
.server_keys
.hash_and_sign_event(&mut pdu, room_version_rules)
.hash_and_sign_event(&mut value, &room_version_rules)
.map_err(|e| {
err!(Request(InvalidParam(warn!("Failed to sign send_join event: {e}"))))
})?;
@@ -133,7 +200,7 @@ async fn create_join_event(
let pdu_id = services
.rooms
.event_handler
.handle_incoming_pdu(sender.server_name(), room_id, event_id, pdu.clone(), false)
.handle_incoming_pdu(sender.server_name(), room_id, &event_id, value.clone(), false)
.boxed()
.await?
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
@@ -153,12 +220,14 @@ async fn create_join_event(
.iter()
.try_stream()
.broad_filter_map(|event_id| async move {
if omit_members && let Ok(e) = event_id.as_ref() {
let pdu = services.rooms.timeline.get_pdu(e).await;
if pdu.is_ok_and(|p| *p.kind() == TimelineEventType::RoomMember) {
trace!("omitting member event {e:?} from returned state");
// skip members
return None;
if omit_members {
if let Ok(e) = event_id.as_ref() {
let pdu = services.rooms.timeline.get_pdu(e).await;
if pdu.is_ok_and(|p| *p.kind() == TimelineEventType::RoomMember) {
trace!("omitting member event {e:?} from returned state");
// skip members
return None;
}
}
}
Some(event_id)
@@ -220,7 +289,7 @@ async fn create_join_event(
Ok(assign!(create_join_event::v2::RoomState::new(), {
auth_chain,
state,
event: to_raw_value(&CanonicalJsonValue::Object(pdu)).ok(),
event: to_raw_value(&CanonicalJsonValue::Object(value)).ok(),
members_omitted: omit_members,
servers_in_room,
}))
@@ -240,45 +309,24 @@ pub(crate) async fn create_join_event_v2_route(
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
let create_event = services
.rooms
.state_accessor
.get_room_create_event(&body.room_id)
.await;
let room_version_rules = room_version.rules().unwrap();
let (value, membership, sender, target) = validate_any_membership_event(
&services,
&body.pdu,
&room_version_rules,
create_event.event_id.clone(),
body.room_id.clone(),
body.event_id.clone(),
)
.await?;
if membership != MembershipState::Join {
return Err!(Request(InvalidParam("Invalid membership (expected `join`)")));
}
if sender.server_name() != body.identity {
return Err!(Request(InvalidParam("Sender belongs to a different server")));
}
if sender != target {
return Err!(Request(InvalidParam("Sender does not match state key")));
if let Some(server) = body.room_id.server_name() {
if services.moderation.is_remote_server_forbidden(server) {
warn!(
"Server {} tried joining room ID {} through us which has a server name that is \
globally forbidden. Rejecting.",
body.identity, &body.room_id,
);
return Err!(Request(Forbidden(warn!(
"Room ID server name {server} is banned on this homeserver."
))));
}
}
let now = Instant::now();
let room_state = create_join_event(
&services,
&body.identity,
&body.room_id,
&body.event_id,
&room_version_rules,
value,
body.omit_members,
)
.boxed()
.await?;
let room_state =
create_join_event(&services, &body.identity, &body.room_id, &body.pdu, body.omit_members)
.boxed()
.await?;
info!(
"Finished sending a join for {} in {} in {:?}",
body.identity,
+97 -29
View File
@@ -1,11 +1,21 @@
use axum::extract::State;
use conduwuit::{Err, Event, Result, debug_info, err, matrix::pdu::PduEvent, warn};
use conduwuit::{
Err, Result, err, info,
matrix::{event::gen_event_id_canonical_json, pdu::PduEvent},
warn,
};
use futures::FutureExt;
use ruma::{
api::federation::membership::create_knock_event, events::room::member::MembershipState,
OwnedUserId,
api::federation::membership::create_knock_event,
events::{
StateEventType,
room::member::{MembershipState, RoomMemberEventContent},
},
serde::JsonObject,
};
use crate::{Ruma, server::utils::validate_any_membership_event};
use crate::Ruma;
/// # `PUT /_matrix/federation/v1/send_knock/{roomId}/{eventId}`
///
@@ -23,7 +33,18 @@ pub(crate) async fn create_knock_event_v1_route(
forbidden. Rejecting.",
body.identity, &body.room_id,
);
return Err!(Request(Forbidden("Federation denied with {}", body.identity)));
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
if let Some(server) = body.room_id.server_name() {
if services.moderation.is_remote_server_forbidden(server) {
warn!(
"Server {} tried knocking room ID {} which has a server name that is globally \
forbidden. Rejecting.",
body.identity, &body.room_id,
);
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
}
if !services.rooms.metadata.exists(&body.room_id).await {
@@ -36,9 +57,8 @@ pub(crate) async fn create_knock_event_v1_route(
.server_in_room(services.globals.server_name(), &body.room_id)
.await
{
debug_info!(
info!(
origin = body.identity.as_str(),
room_id = %body.room_id,
"Refusing to serve send_knock for room we aren't participating in"
);
return Err!(Request(NotFound("This server is not participating in that room.")));
@@ -52,40 +72,88 @@ pub(crate) async fn create_knock_event_v1_route(
.await?;
let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
let create_event = services
.rooms
.state_accessor
.get_room_create_event(&body.room_id)
.await;
let room_version_rules = room_version.rules().unwrap();
if !room_version_rules.authorization.knocking {
return Err!(Request(Forbidden("Room version does not support knocking.")));
}
let (mut event, target_membership, sender, target) = validate_any_membership_event(
&services,
&body.pdu,
&room_version_rules,
create_event.event_id().to_owned(),
body.room_id.clone(),
body.event_id.clone(),
let Ok((event_id, value)) = gen_event_id_canonical_json(&body.pdu, &room_version_rules)
else {
// Event could not be converted to canonical json
return Err!(Request(InvalidParam("Could not convert event to canonical json.")));
};
let event_type: StateEventType = serde_json::from_value(
value
.get("type")
.ok_or_else(|| err!(Request(InvalidParam("Event has no event type."))))?
.clone()
.into(),
)
.await?;
.map_err(|e| err!(Request(InvalidParam("Event has invalid event type: {e}"))))?;
if target_membership != MembershipState::Knock {
return Err!(Request(InvalidParam("Invalid membership (expected `knock`)")));
if event_type != StateEventType::RoomMember {
return Err!(Request(InvalidParam(
"Not allowed to send non-membership state event to knock endpoint.",
)));
}
let content: RoomMemberEventContent = serde_json::from_value(
value
.get("content")
.ok_or_else(|| err!(Request(InvalidParam("Membership event has no content"))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(InvalidParam("Event has invalid membership content: {e}"))))?;
if content.membership != MembershipState::Knock {
return Err!(Request(InvalidParam(
"Not allowed to send a non-knock membership event to knock endpoint."
)));
}
// ACL check sender server name
let sender: OwnedUserId = serde_json::from_value(
value
.get("sender")
.ok_or_else(|| err!(Request(InvalidParam("Event has no sender user ID."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson("Event sender is not a valid user ID: {e}"))))?;
services
.rooms
.event_handler
.acl_check(sender.server_name(), &body.room_id)
.await?;
// check if origin server is trying to send for another server
if sender.server_name() != body.identity {
return Err!(Request(InvalidParam("Sender belongs to a different server")));
}
if sender != target {
return Err!(Request(InvalidParam("Sender does not match state key")));
return Err!(Request(BadJson("Not allowed to knock on behalf of another server/user.")));
}
event.insert("event_id".to_owned(), body.event_id.as_str().into());
let state_key: OwnedUserId = serde_json::from_value(
value
.get("state_key")
.ok_or_else(|| err!(Request(InvalidParam("Event does not have a state_key"))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson("Event does not have a valid state_key: {e}"))))?;
let pdu = PduEvent::from_id_val(&body.event_id, event.clone())
if state_key != sender {
return Err!(Request(InvalidParam("state_key does not match sender user of event.")));
}
let mut event: JsonObject = serde_json::from_str(body.pdu.get())
.map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?;
event.insert("event_id".to_owned(), "$placeholder".into());
let pdu: PduEvent = serde_json::from_value(event.into())
.map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?;
let mutex_lock = services
@@ -98,7 +166,7 @@ pub(crate) async fn create_knock_event_v1_route(
let pdu_id = services
.rooms
.event_handler
.handle_incoming_pdu(sender.server_name(), &body.room_id, &body.event_id, event, false)
.handle_incoming_pdu(sender.server_name(), &body.room_id, &event_id, value.clone(), false)
.boxed()
.await?
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
@@ -113,7 +181,7 @@ pub(crate) async fn create_knock_event_v1_route(
let knock_room_state = services
.rooms
.state
.summary_stripped(&pdu, &body.room_id, &sender, true)
.summary_stripped(&pdu, &body.room_id, &sender)
.await;
Ok(create_knock_event::v1::Response::new(knock_room_state))
+104 -31
View File
@@ -1,11 +1,18 @@
use axum::extract::State;
use conduwuit::{Err, Result, debug_info, err};
use conduwuit::{Err, Result, err, info, matrix::event::gen_event_id_canonical_json};
use conduwuit_service::Services;
use futures::FutureExt;
use ruma::{
api::federation::membership::create_leave_event, events::room::member::MembershipState,
OwnedRoomId, OwnedUserId, RoomId, ServerName,
api::federation::membership::create_leave_event,
events::{
StateEventType,
room::member::{MembershipState, RoomMemberEventContent},
},
};
use serde_json::value::RawValue as RawJsonValue;
use crate::{Ruma, server::utils::validate_any_membership_event};
use crate::Ruma;
/// # `PUT /_matrix/federation/v2/send_leave/{roomId}/{eventId}`
///
@@ -14,8 +21,17 @@ pub(crate) async fn create_leave_event_v2_route(
State(services): State<crate::State>,
body: Ruma<create_leave_event::v2::Request>,
) -> Result<create_leave_event::v2::Response> {
let room_id = body.room_id.as_ref();
let origin = &body.identity;
create_leave_event(&services, &body.identity, &body.room_id, &body.pdu).await?;
Ok(create_leave_event::v2::Response::new())
}
async fn create_leave_event(
services: &Services,
origin: &ServerName,
room_id: &RoomId,
pdu: &RawJsonValue,
) -> Result {
if !services.rooms.metadata.exists(room_id).await {
return Err!(Request(NotFound("Room is unknown to this server.")));
}
@@ -26,10 +42,9 @@ pub(crate) async fn create_leave_event_v2_route(
.server_in_room(services.globals.server_name(), room_id)
.await
{
debug_info!(
info!(
origin = origin.as_str(),
room_id = %room_id,
"Refusing to send_leave for room we aren't participating in"
"Refusing to serve backfill for room we aren't participating in"
);
return Err!(Request(NotFound("This server is not participating in that room.")));
}
@@ -41,31 +56,91 @@ pub(crate) async fn create_leave_event_v2_route(
.acl_check(origin, room_id)
.await?;
// We do not add the event_id field to the pdu here because of signature and
// hashes checks
let room_version = services.rooms.state.get_room_version(room_id).await?;
let create_event = services
.rooms
.state_accessor
.get_room_create_event(room_id)
.await;
let room_version_rules = room_version.rules().unwrap();
let (value, membership, sender, target) = validate_any_membership_event(
&services,
&body.pdu,
&room_version_rules,
create_event.event_id.clone(),
body.room_id.clone(),
body.event_id.clone(),
let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_rules) else {
// Event could not be converted to canonical json
return Err!(Request(BadJson("Could not convert event to canonical json.")));
};
let event_room_id: OwnedRoomId = serde_json::from_value(
serde_json::to_value(
value
.get("room_id")
.ok_or_else(|| err!(Request(BadJson("Event missing room_id property."))))?,
)
.expect("CanonicalJson is valid json value"),
)
.await?;
if membership != MembershipState::Leave {
return Err!(Request(InvalidParam("Invalid membership (expected `leave`)")));
.map_err(|e| err!(Request(BadJson(warn!("room_id field is not a valid room ID: {e}")))))?;
if event_room_id != room_id {
return Err!(Request(BadJson("Event room_id does not match request path room ID.")));
}
if sender.server_name() != body.identity {
return Err!(Request(InvalidParam("Sender belongs to a different server")));
let content: RoomMemberEventContent = serde_json::from_value(
value
.get("content")
.ok_or_else(|| err!(Request(BadJson("Event missing content property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("Event content is empty or invalid: {e}")))))?;
if content.membership != MembershipState::Leave {
return Err!(Request(BadJson(
"Not allowed to send a non-leave membership event to leave endpoint."
)));
}
if sender != target {
return Err!(Request(InvalidParam("Sender does not match state key")));
let event_type: StateEventType = serde_json::from_value(
value
.get("type")
.ok_or_else(|| err!(Request(BadJson("Event missing type property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("Event has invalid state event type: {e}")))))?;
if event_type != StateEventType::RoomMember {
return Err!(Request(BadJson(
"Not allowed to send non-membership state event to leave endpoint."
)));
}
// ACL check sender server name
let sender: OwnedUserId = serde_json::from_value(
value
.get("sender")
.ok_or_else(|| err!(Request(BadJson("Event missing sender property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("sender property is not a valid user ID: {e}")))))?;
services
.rooms
.event_handler
.acl_check(sender.server_name(), room_id)
.await?;
if sender.server_name() != origin {
return Err!(Request(BadJson("Not allowed to leave on behalf of another server/user.")));
}
let state_key: OwnedUserId = serde_json::from_value(
value
.get("state_key")
.ok_or_else(|| err!(Request(BadJson("Event missing state_key property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("State key is not a valid user ID: {e}")))))?;
if state_key != sender {
return Err!(Request(BadJson("State key does not match sender user.")));
}
let mutex_lock = services
@@ -78,7 +153,7 @@ pub(crate) async fn create_leave_event_v2_route(
let pdu_id = services
.rooms
.event_handler
.handle_incoming_pdu(origin, room_id, &body.event_id, value, false)
.handle_incoming_pdu(origin, room_id, &event_id, value, false)
.boxed()
.await?
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
@@ -89,7 +164,5 @@ pub(crate) async fn create_leave_event_v2_route(
.sending
.send_pdu_room(room_id, &pdu_id)
.boxed()
.await?;
Ok(create_leave_event::v2::Response::new())
.await
}
+2 -136
View File
@@ -1,10 +1,7 @@
use conduwuit::{Err, Result, err, is_false};
use conduwuit::{Err, Result, is_false};
use conduwuit_service::Services;
use futures::{FutureExt, future::OptionFuture, join};
use ruma::{
CanonicalJsonObject, EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, ServerName,
UserId, events::room::member::MembershipState, room_version_rules::RoomVersionRules,
};
use ruma::{EventId, RoomId, ServerName};
pub(super) struct AccessCheck<'a> {
pub(super) services: &'a Services,
@@ -66,134 +63,3 @@ pub(super) async fn assert(&self) -> Result {
Ok(())
}
}
/// Performs validation on a membership event that should be run on any event a
/// remote is trying to send via us.
///
/// ## Checks performed
///
/// 1. PDU room ID matches request path room ID
/// 2. PDU event ID matches request path event ID
/// 3. Signature check
/// 4. Event type check
/// 5. `sender` field presence (and parsing)
/// 6. `state_key` field presence (and parsing)
/// 7. PDU room format check (PDU check 1)
///
/// ## Returns
///
/// A resulting tuple of (PDU JSON, target membership state, sender, recipient).
pub(crate) async fn validate_any_membership_event(
services: &crate::State,
body: &serde_json::value::RawValue,
room_version_rules: &RoomVersionRules,
create_event_id: OwnedEventId,
expected_room_id: OwnedRoomId,
expected_event_id: OwnedEventId,
) -> Result<(CanonicalJsonObject, MembershipState, OwnedUserId, OwnedUserId)> {
let (template_room_id, template_event_id, mut pdu) = services
.rooms
.event_handler
.parse_incoming_pdu(body, Some(room_version_rules))
.await
.map_err(|e| err!(Request(BadJson("Invalid membership PDU: {e}"))))?;
if template_room_id != expected_room_id {
return Err!(Request(InvalidParam("Membership event does not belong to requested room")));
}
if template_event_id != expected_event_id {
return Err!(Request(InvalidParam(debug_warn!(
%template_event_id,
%expected_event_id,
"Membership event ID does not match provided event ID"
))));
}
// Only `join` events carry `join_authorised_via_users_server`; co-sign
// restricted joins so verification passes. Authorisation is enforced in
// create_join_event.
let membership_is_join = pdu
.get("content")
.and_then(|v| v.as_object())
.and_then(|c| c.get("membership"))
.and_then(|v| v.as_str())
.is_some_and(|m| m == "join");
let authorising_user = pdu
.get("content")
.and_then(|v| v.as_object())
.and_then(|c| c.get("join_authorised_via_users_server"))
.and_then(|v| v.as_str())
.map(UserId::parse)
.and_then(Result::ok);
if room_version_rules.authorization.restricted_join_rule
&& membership_is_join
&& let Some(authorising_user) = authorising_user
&& services.globals.user_is_local(&authorising_user)
{
services
.server_keys
.hash_and_sign_event(&mut pdu, room_version_rules)
.map_err(|e| {
err!(Request(InvalidParam("Failed to sign restricted join event: {e}")))
})?;
}
services
.server_keys
.verify_event(&pdu, room_version_rules)
.await
.map_err(|e| {
err!(Request(InvalidParam("Signature verification failed on membership event: {e}")))
})?;
// Ensure this is a membership event
if pdu
.get("type")
.expect("event must have a type")
.as_str()
.expect("type must be a string")
!= "m.room.member"
{
return Err!(Request(BadJson(
"Not allowed to send non-membership event to this endpoint"
)));
}
let membership = pdu
.get("content")
.ok_or_else(|| err!(Request(BadJson("Event missing content property"))))?
.as_object()
.ok_or_else(|| err!(Request(BadJson("Event content is not an object"))))?
.get("membership")
.ok_or_else(|| err!(Request(BadJson("Event missing membership property"))))?
.as_str()
.ok_or_else(|| err!(Request(BadJson("Event is not a string"))))?
.to_owned();
let sender_user = pdu
.get("sender")
.and_then(|v| v.as_str())
.map(UserId::parse)
.and_then(Result::ok)
.ok_or_else(|| err!(Request(InvalidParam("Invalid sender property"))))?;
let recipient_user = pdu
.get("state_key")
.and_then(|v| v.as_str())
.map(UserId::parse)
.and_then(Result::ok)
.ok_or_else(|| err!(Request(InvalidParam("Invalid state_key property"))))?;
// Do a quick format check. The spec doesn't suggest this, but it's probably
// a good idea nonetheless.
service::rooms::event_handler::Service::pdu_format_check_1(
&pdu,
room_version_rules,
&create_event_id,
)
.map_err(|e| {
err!(Request(InvalidParam("Membership event violates the room event format: {e}")))
})?;
Ok((pdu, membership.into(), sender_user, recipient_user))
}
-1
View File
@@ -2442,7 +2442,6 @@ pub struct OauthConfig {
/// legacy authentication will be unable to log in.
///
/// default: "hybrid"
#[serde(default)]
compatibility_mode: OAuthMode,
/// display: hidden
+1 -1
View File
@@ -42,12 +42,12 @@ assets = [
[features]
default = [
"standard",
"release_max_log_level",
"ring",
"bindgen-runtime", # replace with bindgen-static on alpine
]
standard = [
"brotli_compression",
"direct_tls",
"element_hacks",
"gzip_compression",
"io_uring",
+8 -22
View File
@@ -1,10 +1,10 @@
use std::{any::Any, sync::Arc, time::Duration};
use axum::{
Router, extract,
extract::{DefaultBodyLimit, FromRequestParts, MatchedPath},
Router,
extract::{DefaultBodyLimit, MatchedPath},
};
use axum_client_ip::{ClientIp, ClientIpSource};
use axum_client_ip::ClientIpSource;
use conduwuit::{Result, Server, debug, error};
use conduwuit_service::{Services, state::Guard};
use http::{
@@ -20,6 +20,7 @@
timeout::{RequestBodyTimeoutLayer, ResponseBodyTimeoutLayer, TimeoutLayer},
trace::{DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer},
};
use tracing::Level;
use crate::{request, router};
@@ -66,16 +67,15 @@ pub(crate) fn build(services: &Arc<Services>) -> Result<(Router, Guard)> {
let services_ = services.clone();
let layers = layers
.layer(SetSensitiveHeadersLayer::new([header::AUTHORIZATION]))
.layer(client_ip_layer.into_extension())
.layer(
TraceLayer::new_for_http()
.make_span_with(tracing_span::<_>)
.on_failure(DefaultOnFailure::new().level(tracing::Level::ERROR))
.on_request(DefaultOnRequest::new().level(tracing::Level::TRACE))
.on_response(DefaultOnResponse::new().level(tracing::Level::DEBUG)),
.on_failure(DefaultOnFailure::new().level(Level::ERROR))
.on_request(DefaultOnRequest::new().level(Level::TRACE))
.on_response(DefaultOnResponse::new().level(Level::DEBUG)),
)
.layer(axum::middleware::from_fn(request_ip))
.layer(axum::middleware::from_fn_with_state(Arc::clone(services), request::handle))
.layer(client_ip_layer.into_extension())
.layer(ResponseBodyTimeoutLayer::new(Duration::from_secs(
server.config.client_response_timeout,
)))
@@ -230,25 +230,11 @@ fn tracing_span<T>(request: &http::Request<T>) -> tracing::Span {
parent: None,
debug::INFO_SPAN_LEVEL,
"router",
ip=tracing::field::Empty,
method = %request.method(),
%path,
}
}
/// Annotates the tracing span with the client IP
async fn request_ip(
request: extract::Request,
next: axum::middleware::Next,
) -> axum::response::Response {
let (mut parts, body) = request.into_parts();
if let Ok(ip) = ClientIp::from_request_parts(&mut parts, &()).await {
let span = tracing::Span::current();
span.record("ip", ip.0.to_string());
}
next.run(extract::Request::from_parts(parts, body)).await
}
fn request_path_str<T>(request: &http::Request<T>) -> &str {
request
.uri()
+2 -10
View File
@@ -1,6 +1,4 @@
use std::io::IsTerminal;
use conduwuit::{Err, Result, debug, debug_info, error, info, warn};
use conduwuit::{Err, Result, debug, debug_info, error, info};
use ruma::events::room::message::RoomMessageEventContent;
use tokio::time::{Duration, sleep};
@@ -9,16 +7,10 @@
pub(super) const SIGNAL: &str = "SIGUSR2";
impl super::Service {
/// Possibly spawn the terminal console at startup if configured and a TTY
/// is available.
/// Possibly spawn the terminal console at startup if configured.
pub(super) async fn console_auto_start(&self) {
#[cfg(feature = "console")]
if self.services.server.config.admin_console_automatic {
if !std::io::stdin().is_terminal() {
warn!("Console enabled without a tty available; Not enabling console");
return;
}
// Allow more of the startup sequence to execute before spawning
tokio::task::yield_now().await;
self.console.start().await;
+1 -1
View File
@@ -134,7 +134,7 @@ async fn check(&self) -> Result<()> {
let response = self
.services
.client
.external_resource
.default
.get(CHECK_FOR_ANNOUNCEMENTS_URL)
.send()
.await?
+19 -14
View File
@@ -16,9 +16,9 @@ pub struct Service {
pub matrix_resolver: Arc<MatrixResolver>,
pub dns_resolver: Arc<TokioResolver>,
pub dns: reqwest::Client,
pub default: reqwest::Client,
pub url_preview: reqwest::Client,
pub external_resource: reqwest::Client,
pub extern_media: reqwest::Client,
pub federation: reqwest::Client,
pub federation_slow: reqwest::Client,
pub sender: reqwest::Client,
@@ -35,17 +35,17 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
let dns_resolver = get_dns_resolver(args.server)?;
let dns_client = base(&args.server.config)?
.connect_timeout(Duration::from_secs(args.server.config.well_known_conn_timeout))
.read_timeout(Duration::from_secs(args.server.config.well_known_timeout))
.timeout(Duration::from_secs(args.server.config.well_known_timeout))
.pool_max_idle_per_host(0)
.redirect(redirect::Policy::limited(4))
.build()?;
let matrix_resolver = Arc::new(MatrixResolverBuilder::new()
.dangerous_tls_accept_invalid_certs(args.server.config.allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure)
.http_client(dns_client.clone())
.http_client(
base(&args.server.config)?
.connect_timeout(Duration::from_secs(args.server.config.well_known_conn_timeout))
.read_timeout(Duration::from_secs(args.server.config.well_known_timeout))
.timeout(Duration::from_secs(args.server.config.well_known_timeout))
.pool_max_idle_per_host(0)
.redirect(redirect::Policy::limited(4))
.build()?
)
.dns_resolver(dns_resolver.clone())
.build()?);
let matrix_dns_resolver = matrix_resolver.create_dns_resolver();
@@ -69,19 +69,23 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
matrix_resolver,
dns_resolver,
dns: dns_client,
default: base(config)?
.dns_resolver(matrix_dns_resolver.clone())
.build()?,
url_preview: base(config)
.and_then(|builder| {
builder_interface(builder, url_preview_bind_iface.as_deref())
})?
.local_address(url_preview_bind_addr)
.dns_resolver(matrix_dns_resolver.clone())
.timeout(Duration::from_secs(config.url_preview_timeout))
.redirect(redirect::Policy::limited(3))
.user_agent(url_preview_user_agent)
.build()?,
external_resource: base(config)?
extern_media: base(config)?
.dns_resolver(matrix_dns_resolver.clone())
.redirect(redirect::Policy::limited(3))
.build()?,
@@ -124,7 +128,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
.build()?,
appservice: base(config)?
.dns_resolver(matrix_dns_resolver)
.dns_resolver(matrix_dns_resolver.clone())
.connect_timeout(Duration::from_secs(5))
.read_timeout(Duration::from_secs(config.appservice_timeout))
.timeout(Duration::from_secs(config.appservice_timeout))
@@ -134,6 +138,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
.build()?,
pusher: base(config)?
.dns_resolver(matrix_dns_resolver)
.connect_timeout(Duration::from_secs(config.pusher_conn_timeout))
.timeout(Duration::from_secs(config.pusher_timeout))
.pool_max_idle_per_host(1)
+1 -1
View File
@@ -260,7 +260,7 @@ async fn location_request(&self, location: &str) -> Result<FileMeta> {
let response = self
.services
.client
.external_resource
.extern_media
.get(location)
.send()
.await?;
+2 -4
View File
@@ -829,7 +829,7 @@ async fn fix_local_invite_state(services: &Services) -> Result {
&& services.globals.user_is_local(&membership_event.sender) {
// build and save stripped state for their invite in the database
let stripped_state = services.rooms.state.summary_stripped(&membership_event, &room_id, &user_id, false).await;
let stripped_state = services.rooms.state.summary_stripped(&membership_event, &room_id, &user_id).await;
userroomid_invitestate.put((&user_id, &room_id), Json(stripped_state));
fixed = fixed.saturating_add(1);
}
@@ -882,7 +882,7 @@ async fn split_userid_password(services: &Services) -> Result {
drop(cork);
info!(?remote_users, "Split userid_password.");
db["global"].insert(SPLIT_USERID_PASSWORD, []);
db["global"].insert(FIXED_LOCAL_INVITE_STATE_MARKER, []);
db.db.sort()?;
Ok(())
}
@@ -895,7 +895,5 @@ async fn obliterate_roomsynctoken_shortstatehash_with_extreme_prejudice(
info!("Cleared roomsynctoken_shortstatehash.");
services.db["global"].insert(DROP_ROOMSYNCTOKEN_SHORTSTATEHASH, []);
Ok(())
}
+9 -7
View File
@@ -25,7 +25,7 @@
OAuthError, ResponseMode, Scope, TokenRequest, TokenResponse, TokenType,
},
},
users::{self, DeviceToken},
users,
};
pub mod client_metadata;
@@ -343,7 +343,7 @@ async fn create_session(
client_name: Option<String>,
client_id: String,
) -> Result<TokenResponse, OAuthError> {
let access_token = DeviceToken::new_random().with_max_age(Self::ACCESS_TOKEN_MAX_AGE);
let access_token = Self::generate_token();
let refresh_token = Self::generate_token();
let device_id = requested_scopes
@@ -376,7 +376,8 @@ async fn create_session(
.create_device(
&authorizing_user,
device_id,
Some(access_token.clone()),
&access_token,
Some(Self::ACCESS_TOKEN_MAX_AGE),
client_name,
None,
)
@@ -412,7 +413,7 @@ async fn create_session(
);
Ok(TokenResponse {
access_token: access_token.into_token(),
access_token,
token_type: TokenType::Bearer,
expires_in: Self::ACCESS_TOKEN_MAX_AGE.as_secs(),
scope: requested_scopes.iter().join(" "),
@@ -448,7 +449,7 @@ async fn refresh_session(
assert_eq!(&client_id, &session_info.client_id, "session info client id mismatch");
let new_access_token = DeviceToken::new_random().with_max_age(Self::ACCESS_TOKEN_MAX_AGE);
let new_access_token = Self::generate_token();
let new_refresh_token = Self::generate_token();
let scope = session_info.scopes.iter().join(" ");
session_info
@@ -460,7 +461,8 @@ async fn refresh_session(
.set_token(
&refresh_token_info.user_id,
&refresh_token_info.device_id,
new_access_token.clone(),
&new_access_token,
Some(Self::ACCESS_TOKEN_MAX_AGE),
)
.await
.expect("should be able to set token");
@@ -477,7 +479,7 @@ async fn refresh_session(
.raw_put(&new_refresh_token, Json(refresh_token_info));
Ok(TokenResponse {
access_token: new_access_token.into_token(),
access_token: new_access_token,
token_type: TokenType::Bearer,
expires_in: Self::ACCESS_TOKEN_MAX_AGE.as_secs(),
scope,
-1
View File
@@ -151,7 +151,6 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
config: config.clone(),
client_secret: if let Some(client_secret_file) = &config.client_secret_file {
std::fs::read_to_string(client_secret_file)
.map(|client_secret| client_secret.trim().to_owned())
.map(ClientSecret::new)
.map_err(|err| err!("Failed to read OIDC client secret file: {err}"))?
} else if let Some(client_secret) = &config.client_secret {
@@ -241,7 +241,7 @@ pub async fn get_missing_events(
latest_events.clear();
for raw_event in response.events {
let (_, event_id, pdu_json) = self.parse_incoming_pdu(&raw_event, None).await?;
let (_, event_id, pdu_json) = self.parse_incoming_pdu(&raw_event).await?;
let pdu = PduEvent::from_id_val(&event_id, pdu_json).map_err(|e| {
err!(Request(BadJson("Failed to parse gapfilled event {event_id}: {e}")))
})?;
@@ -384,9 +384,8 @@ async fn fetch_event_via(
.send_federation_request(&remote, get_event::v1::Request::new(event_id.clone()))
.await?;
let (_, calculated_event_id, value) = self
.parse_incoming_pdu(&res.pdu, Some(room_version_rules))
.await?;
let (calculated_event_id, value) =
Self::parse_incoming_pdu_with_known_room(&res.pdu, room_version_rules)?;
if calculated_event_id != event_id {
Err!(Request(BadJson(warn!(
+8 -10
View File
@@ -48,16 +48,14 @@ pub(super) async fn fetch_and_persist_event_auth<Pdu>(
let mut auth_chain_map = HashMap::with_capacity(event_auth.auth_chain.len());
for auth_pdu_json in event_auth.auth_chain {
let (auth_event_room_id, auth_event_id, auth_pdu_json) = match self
.parse_incoming_pdu(&auth_pdu_json, Some(room_version_rules))
.await
{
| Ok(parsed) => parsed,
| Err(e) => {
warn!(error=?e, "Dropping auth chain event as it could not be parsed");
continue;
},
};
let (auth_event_room_id, auth_event_id, auth_pdu_json) =
match self.parse_incoming_pdu(&auth_pdu_json).await {
| Ok(parsed) => parsed,
| Err(e) => {
warn!(error=?e, "Dropping auth chain event as it could not be parsed");
continue;
},
};
if let Err(e) = Self::pdu_format_check_1(
&auth_pdu_json,
room_version_rules,
@@ -314,7 +314,7 @@ pub(super) async fn fetch_full_state(
.iter()
.stream()
.broad_filter_map(|raw_event_json| async {
if let Some(parsed) = self.parse_incoming_pdu(raw_event_json, None).await.ok()
if let Some(parsed) = self.parse_incoming_pdu(raw_event_json).await.ok()
&& parsed.0 == room_id
{
Some(parsed)
@@ -351,7 +351,7 @@ pub(super) async fn fetch_full_state(
.iter()
.stream()
.broad_filter_map(|raw_event_json| async {
if let Some(parsed) = self.parse_incoming_pdu(raw_event_json, None).await.ok()
if let Some(parsed) = self.parse_incoming_pdu(raw_event_json).await.ok()
&& parsed.0 == room_id
{
Some(parsed)
@@ -14,12 +14,10 @@ impl super::Service {
/// This performs steps 1 through 4 of [S-S section 5.1][spec], returning
/// the parsed PDU and modified JSON object.
///
/// **External callers likely want `handle_incoming_pdu` instead.**
///
/// [spec]: https://spec.matrix.org/v1.19/server-server-api/#checks-performed-on-receipt-of-a-pdu
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(name = "handle_outlier", skip_all)]
pub async fn handle_outlier_pdu<'a, Pdu>(
pub(super) async fn handle_outlier_pdu<'a, Pdu>(
&self,
origin: &'a ServerName,
create_event: &'a Pdu,
@@ -56,6 +54,7 @@ pub async fn handle_outlier_pdu<'a, Pdu>(
})?;
value.remove("unsigned");
let room_version_rules = get_room_version_rules(create_event)?;
// 2. Check signatures, otherwise drop.
// 3. Check content hash, redacting the event if it fails.
@@ -82,14 +82,25 @@ pub(super) fn expect_event_id_array(
}
impl super::Service {
/// Parses an incoming PDU JSON object, generating an event ID for it. Does
/// not insert the event ID into the returned object. Does not discover the
/// room ID.
pub(super) fn parse_incoming_pdu_with_known_room(
pdu: &RawJsonValue,
room_version_rules: &RoomVersionRules,
) -> Result<(OwnedEventId, CanonicalJsonObject)> {
let (event_id, value) =
gen_event_id_canonical_json(pdu, room_version_rules).map_err(|e| {
err!(Request(InvalidParam("Could not convert event to canonical json: {e}")))
})?;
// NOTE: validation checks are now performed by `pdu_format_check_1`.
Ok((event_id, value))
}
/// Parses an incoming PDU JSON object, generating an event ID for it and
/// attempts to discover the associated room ID. Does not insert the event
/// ID into the returned object.
pub async fn parse_incoming_pdu(
&self,
pdu: &RawJsonValue,
room_version_rules: Option<&RoomVersionRules>,
) -> Result<Parsed> {
pub async fn parse_incoming_pdu(&self, pdu: &RawJsonValue) -> Result<Parsed> {
let value = serde_json::from_str::<CanonicalJsonObject>(pdu.get()).map_err(|e| {
err!(BadServerResponse(debug_warn!("Error parsing incoming event {e:?}")))
})?;
@@ -100,20 +111,17 @@ pub async fn parse_incoming_pdu(
let room_id = extract_room_id(event_type, &value)?;
let room_version_rules = match room_version_rules {
| Some(r) => r,
| None => &self
.services
.state
.get_room_version(&room_id)
.await
.unwrap_or(RoomVersionId::V1)
.rules()
.expect("room version must be supported"),
};
let room_version_rules = self
.services
.state
.get_room_version(&room_id)
.await
.unwrap_or(RoomVersionId::V1)
.rules()
.unwrap();
let (event_id, value) =
gen_event_id_canonical_json(pdu, room_version_rules).map_err(|e| {
gen_event_id_canonical_json(pdu, &room_version_rules).map_err(|e| {
err!(Request(InvalidParam("Could not convert event to canonical json: {e}")))
})?;
// NOTE: validation checks are now performed by `pdu_format_check_1`.
+8 -27
View File
@@ -6,9 +6,8 @@
};
use futures::future::ready;
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, ServerName,
api::error::ErrorKind, canonical_json::redact, events::StateEventType,
room_version_rules::RoomVersionRules,
CanonicalJsonObject, EventId, OwnedEventId, ServerName, api::error::ErrorKind,
canonical_json::redact, events::StateEventType, room_version_rules::RoomVersionRules,
};
use crate::rooms::{
@@ -19,7 +18,7 @@ impl super::Service {
/// Checks that the PDU conforms to the PDU format (check 1). This is
/// already mostly done during deserialisation, so this function just checks
/// that the PDU isn't a too large.
pub fn pdu_format_check_1(
pub(super) fn pdu_format_check_1(
pdu_json: &CanonicalJsonObject,
room_version_rules: &RoomVersionRules,
create_event_id: &EventId,
@@ -43,27 +42,9 @@ pub fn pdu_format_check_1(
return Err!(Request(BadJson("PDU has too many auth events")));
}
// The m.room.create event is the genesis event and has empty auth_events
// by definition, so it is exempt from the checks below requiring or
// forbidding the create event in auth_events (it cannot reference itself).
let Some(event_type) = pdu_json.get("type").and_then(CanonicalJsonValue::as_str) else {
return Err!(Request(BadJson("PDU is missing a type")));
};
let state_key = pdu_json
.get("state_key")
.and_then(CanonicalJsonValue::as_str);
let is_create_event = event_type == "m.room.create" && state_key == Some("");
if !is_create_event {
let create_event_in_auth_events = auth_events.iter().any(|id| id == create_event_id);
if !event_format.allow_room_create_in_auth_events && create_event_in_auth_events {
return Err!(Request(BadJson("PDU references a create event")));
} else if event_format.allow_room_create_in_auth_events
&& !create_event_in_auth_events
{
return Err!(Request(BadJson("PDU does not reference the room create event")));
}
let create_event_in_auth_events = auth_events.iter().any(|id| id == create_event_id);
if !event_format.allow_room_create_in_auth_events && create_event_in_auth_events {
return Err!(Request(BadJson("PDU references a create event")));
}
let prev_events = expect_event_id_array(pdu_json, "prev_events")?;
@@ -78,7 +59,7 @@ pub fn pdu_format_check_1(
/// the content hash verification fails (check 3), returning the
/// potentially modified JSON. Returns an error if the PDU cannot be
/// redacted, or fails signature verification.
pub async fn signature_hash_check_2_3(
pub(super) async fn signature_hash_check_2_3(
&self,
pdu_json: CanonicalJsonObject,
room_version_rules: &RoomVersionRules,
@@ -111,7 +92,7 @@ pub async fn signature_hash_check_2_3(
/// If the auth check fails, false is returned, otherwise true.
///
/// [spec]: https://spec.matrix.org/v1.19/server-server-api/#checks-performed-on-receipt-of-a-pdu
pub async fn auth_state_check_4(
pub(super) async fn auth_state_check_4(
&self,
pdu: &PduEvent,
room_version_rules: &RoomVersionRules,
+3 -18
View File
@@ -1,7 +1,7 @@
use std::{collections::HashMap, fmt::Write, sync::Arc};
use async_trait::async_trait;
use conduwuit::{debug, utils::stream::WidebandExt};
use conduwuit::debug;
use conduwuit_core::{
Event, PduEvent, Result, err,
result::FlatOk,
@@ -30,7 +30,7 @@
short::{ShortEventId, ShortStateHash},
state_compressor::{CompressedState, parse_compressed_state_event},
},
sending, sync,
sync,
};
pub struct Service {
@@ -45,7 +45,6 @@ struct Services {
state_cache: Dep<rooms::state_cache::Service>,
state_accessor: Dep<rooms::state_accessor::Service>,
state_compressor: Dep<rooms::state_compressor::Service>,
sending: Dep<sending::Service>,
sync: Dep<sync::Service>,
timeline: Dep<rooms::timeline::Service>,
}
@@ -72,7 +71,6 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
state_compressor: args
.depend::<rooms::state_compressor::Service>("rooms::state_compressor"),
sending: args.depend::<sending::Service>("sending"),
sync: args.depend::<sync::Service>("sync"),
timeline: args.depend::<rooms::timeline::Service>("rooms::timeline"),
},
@@ -309,7 +307,6 @@ pub async fn summary_stripped(
event: &PduEvent,
room_id: &RoomId,
target_user: &UserId,
federation: bool,
) -> Vec<RawStrippedState> {
let mut state_events = [
(&StateEventType::RoomCreate, ""),
@@ -346,20 +343,8 @@ pub async fn summary_stripped(
.await
.into_iter()
.filter_map(Result::ok)
.stream()
.wide_then(async |pdu| {
let formatted = if federation {
self.services
.sending
.convert_to_outgoing_federation_event(pdu.to_canonical_object())
.await
} else {
serde_json::value::to_raw_value(&pdu).unwrap()
};
RawStrippedState::Pdu(formatted)
})
.map(|pdu| RawStrippedState::Pdu(serde_json::value::to_raw_value(&pdu).unwrap()))
.collect()
.await
}
/// Set the state hash to a new version, but does not update state_cache.
+1 -1
View File
@@ -114,7 +114,7 @@ pub async fn update_membership(
let invite_state = if is_local {
self.services
.state
.summary_stripped(pdu, room_id, user_id, false)
.summary_stripped(pdu, room_id, user_id)
.await
} else {
vec![]
+2 -5
View File
@@ -166,11 +166,8 @@ pub async fn get_remote_pdu(&self, room_id: &RoomId, event_id: &EventId) -> Resu
/// Backfills a single PDU.
#[tracing::instrument(skip(self, pdu), level = "debug")]
pub async fn backfill_pdu(&self, origin: &ServerName, pdu: Box<RawJsonValue>) -> Result<()> {
let (room_id, event_id, value) = self
.services
.event_handler
.parse_incoming_pdu(&pdu, None)
.await?;
let (room_id, event_id, value) =
self.services.event_handler.parse_incoming_pdu(&pdu).await?;
// Lock so we cannot backfill the same pdu twice at the same time
let mutex_lock = self
-15
View File
@@ -308,21 +308,6 @@ pub async fn create_hash_and_sign_event(
| _ => Err!(Request(Unknown(warn!("Signing event failed: {e}")))),
};
}
// Evil hack because only the PDU JSON gets signed
pdu.hashes = serde_json::from_value(serde_json::to_value(
pdu_json
.get("hashes")
.expect("must have hashes after signing")
.clone(),
)?)?;
pdu.signatures = serde_json::from_value(serde_json::to_value(
pdu_json
.get("signatures")
.expect("must have signatures after signing")
.clone(),
)?)?;
// Generate event id
pdu.event_id = gen_event_id(&pdu_json, &room_version_rules)?;
pdu_json
+7 -89
View File
@@ -5,7 +5,6 @@
};
use conduwuit::{Err, Error, Result, error, utils};
use futures::StreamExt;
use lettre::Address;
use ruma::{
DeviceId, UserId,
@@ -26,7 +25,7 @@
use tokio::sync::Mutex;
use crate::{
Dep, config, firstrun, globals,
Dep, config, globals,
oauth::{self, OAuthTicket},
registration_tokens, threepid, users,
};
@@ -37,54 +36,25 @@ pub struct Service {
}
struct Services {
config: Dep<config::Service>,
firstrun: Dep<firstrun::Service>,
globals: Dep<globals::Service>,
oauth: Dep<oauth::Service>,
users: Dep<users::Service>,
config: Dep<config::Service>,
registration_tokens: Dep<registration_tokens::Service>,
threepid: Dep<threepid::Service>,
users: Dep<users::Service>,
}
#[derive(Debug)]
pub enum TrustedFlowStatus {
Unavailable,
Available,
}
#[derive(Debug)]
pub enum UntrustedFlowStatus {
Unavailable,
Available {
require_email: bool,
},
}
pub struct FlowStatus {
pub trusted: TrustedFlowStatus,
pub untrusted: UntrustedFlowStatus,
}
impl FlowStatus {
#[must_use]
pub fn any_available(&self) -> bool {
matches!(self.trusted, TrustedFlowStatus::Available)
|| matches!(self.untrusted, UntrustedFlowStatus::Available { .. })
}
oauth: Dep<oauth::Service>,
}
impl crate::Service for Service {
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
Ok(Arc::new(Self {
services: Services {
config: args.depend::<config::Service>("config"),
firstrun: args.depend::<firstrun::Service>("firstrun"),
globals: args.depend::<globals::Service>("globals"),
oauth: args.depend::<oauth::Service>("oauth"),
users: args.depend::<users::Service>("users"),
config: args.depend::<config::Service>("config"),
registration_tokens: args
.depend::<registration_tokens::Service>("registration_tokens"),
threepid: args.depend::<threepid::Service>("threepid"),
users: args.depend::<users::Service>("users"),
oauth: args.depend::<oauth::Service>("oauth"),
},
uiaa_sessions: Mutex::new(HashMap::new()),
}))
@@ -625,56 +595,4 @@ async fn check_stage(
Ok((completed_auth_type, session_metadata))
}
pub async fn registration_flow_status(&self) -> FlowStatus {
// 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 =
self.services.config.allow_registration || self.services.firstrun.is_first_run();
// Trusted flow is only available if any registration tokens exist
let trusted = {
if !allow_registration {
TrustedFlowStatus::Unavailable
} else if self
.services
.registration_tokens
.iterate_tokens()
.next()
.await
.is_some()
{
TrustedFlowStatus::Available
} else {
TrustedFlowStatus::Unavailable
}
};
// Untrusted flow is available if email is required for registration,
// or reCAPTCHA is configured, or open registration is enabled
let untrusted = {
let require_email = self
.services
.config
.smtp
.as_ref()
.is_some_and(|smtp| smtp.require_email_for_registration);
if !allow_registration || self.services.firstrun.is_first_run() {
UntrustedFlowStatus::Unavailable
} else if self.services.config.recaptcha_private_site_key.is_some() || require_email {
UntrustedFlowStatus::Available { require_email }
} else if self
.services
.config
.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
{
UntrustedFlowStatus::Available { require_email: false }
} else {
UntrustedFlowStatus::Unavailable
}
};
FlowStatus { trusted, untrusted }
}
}
+1
View File
@@ -41,6 +41,7 @@ pub async fn set_dehydrated_device(&self, user_id: &UserId, request: Request) ->
self.create_device(
user_id,
&request.device_id,
"",
None,
request.initial_device_display_name.clone(),
None,
+31 -38
View File
@@ -11,44 +11,20 @@
use futures::{Stream, StreamExt};
use ruma::{
DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, UserId,
api::client::device::Device, assign, events::AnyToDeviceEvent, serde::Raw, uint,
api::client::device::Device, events::AnyToDeviceEvent, serde::Raw, uint,
};
use serde_json::json;
use crate::users::increment;
#[must_use]
#[derive(Clone)]
pub struct DeviceToken {
token: String,
max_age: Option<Duration>,
}
impl DeviceToken {
const DEVICE_ID_LENGTH: usize = 10;
pub fn new(token: String) -> Self { Self { token, max_age: None } }
pub fn new_random() -> Self { Self::new(utils::random_string(Self::DEVICE_ID_LENGTH)) }
pub fn with_max_age(self, max_age: Duration) -> Self {
assign!(self, { max_age: Some(max_age) })
}
#[must_use]
pub fn into_token(self) -> String { self.token }
}
impl super::Service {
/// Adds a new device to a user.
///
/// If no `token` is provided, the device will not be able to be logged
/// into.
pub async fn create_device(
&self,
user_id: &UserId,
device_id: &DeviceId,
token: Option<DeviceToken>,
token: &str,
token_max_age: Option<Duration>,
initial_device_display_name: Option<String>,
client_ip: Option<String>,
) -> Result<()> {
@@ -62,12 +38,8 @@ pub async fn create_device(
increment(&self.db.userid_devicelistversion, user_id.as_bytes());
self.db.userdeviceid_metadata.put(key, Json(device));
if let Some(token) = token {
self.set_token(user_id, device_id, token).await?;
}
Ok(())
self.set_token(user_id, device_id, token, token_max_age)
.await
}
/// Removes a device from a user.
@@ -125,12 +97,31 @@ pub async fn get_token(&self, user_id: &UserId, device_id: &DeviceId) -> Result<
self.db.userdeviceid_token.qry(&key).await.deserialized()
}
/// Generate a unique access token that doesn't collide with existing tokens
pub async fn generate_unique_token(&self) -> String {
loop {
let token = utils::random_string(32);
// Check for collision with existing appservice and user tokens
let (appservice, usr) = tokio::join!(
self.services.appservice.find_from_token(&token),
self.db.token_userdeviceid.get(&token)
);
if appservice.is_ok() || usr.is_ok() {
continue;
}
return token;
}
}
/// Replaces the access token of one device.
pub async fn set_token(
&self,
user_id: &UserId,
device_id: &DeviceId,
DeviceToken { token, max_age }: DeviceToken,
token: &str,
token_max_age: Option<Duration>,
) -> Result<()> {
let key = (user_id, device_id);
if self.db.userdeviceid_metadata.qry(&key).await.is_err() {
@@ -145,7 +136,7 @@ pub async fn set_token(
if self
.services
.appservice
.find_from_token(&token)
.find_from_token(token)
.await
.is_ok()
{
@@ -162,10 +153,10 @@ pub async fn set_token(
}
// Assign token to user device combination
self.db.userdeviceid_token.put_raw(key, &token);
self.db.token_userdeviceid.raw_put(&token, key);
self.db.userdeviceid_token.put_raw(key, token);
self.db.token_userdeviceid.raw_put(token, key);
if let Some(max_age) = max_age {
if let Some(max_age) = token_max_age {
let expires = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("system time should not be before the epoch")
@@ -253,6 +244,8 @@ pub async fn remove_to_device_events<Until>(
self.db.todeviceid_events.del(key);
})
.await;
self.services.sync.wake(user_id).await;
}
/// Updates device metadata and increments the device list version.
+1 -2
View File
@@ -14,7 +14,6 @@
utils::{self},
};
use database::Map;
pub use device::DeviceToken;
pub use profile::ProfileFieldChange;
use ruma::{UserId, api::error::ErrorKind, encryption::CrossSigningKey, serde::Raw};
use serde::{Deserialize, Serialize};
@@ -109,7 +108,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
services: Services {
account_data: args.depend::<account_data::Service>("account_data"),
admin: args.depend::<admin::Service>("admin"),
alias: args.depend::<alias::Service>("rooms::alias"),
alias: args.depend::<alias::Service>("alias"),
appservice: args.depend::<appservice::Service>("appservice"),
config: args.depend::<config::Service>("config"),
firstrun: args.depend::<firstrun::Service>("firstrun"),

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