Compare commits

..
Author SHA1 Message Date
Renovate Bot ba0ec6ba70 chore(deps): update https://github.com/actions/setup-node action to v7 2026-07-15 05:05:16 +00:00
Erwan Leboucher 1ac05838dd docs: Add back binstall with a link to the deps 2026-07-14 19:44:28 +02:00
Erwan Leboucher 7ecd2aa5e2 fix(pdu): Exempt m.room.create from auth_events check 2026-07-14 19:27:46 +02:00
Renovate BotandEllis Git 69a3145983 chore(deps): update rust-zerover-patch-updates 2026-07-14 13:10:32 +00:00
N00byKing 73b7553b1e chore: Add changelog entry for #1984 2026-07-14 13:40:28 +02:00
N00byKing 232ec3f620 fix: Return 201 instead of 200 on oauth registration
See RFC 7591 at 3.2.1: "The server responds with an HTTP 201 Created status code [...]"

Fixes a failure in matrix-dart-sdk
2026-07-14 11:00:34 +02:00
N00byKing e52bbeaf94 fix: Correct link for code style guide in CONTRIBUTING.md 2026-07-14 08:59:08 +00:00
Renovate Bot ad1c194fb0 chore(deps): update github-actions-digest 2026-07-14 05:01:52 +00:00
GingerandEllis Git 5e2dcd8e79 chore: News fragment 2026-07-13 20:38:10 +00:00
GingerandEllis Git 24c6474bd1 fix: Allow unstable and stable device id query together 2026-07-13 20:38:10 +00:00
gingerandEllis Git 642d05048b chore: announce 2026-07-13 18:54:04 +00:00
Koen OostveenandEllis Git 446ae93ad7 fix(web): Change incorrect deeplink for deleting a device 2026-07-13 13:45:46 +00:00
Renovate BotandEllis Git 9ca0774b2d chore(deps): lock file maintenance 2026-07-13 13:44:47 +00:00
Ginger ce494eb0c0 fix: Properly sync newly created rooms 2026-07-13 08:50:53 -04:00
Ginger 1f5e178c3f fix: Properly handle appservice device creation 2026-07-13 08:50:48 -04:00
Renovate Bot ddd050d402 chore(deps): update ghcr.io/renovatebot/renovate docker tag to v43.260.2 2026-07-13 05:03:24 +00:00
Ginger 6ad3e679bc chore: Release 2026-07-12 17:34:16 -04:00
Ginger 65812bb246 chore: Update changelog 2026-07-12 17:33:52 -04:00
theS1LV3R 9f3a7994c7 chore: Changelog 2026-07-12 20:22:19 +02:00
theS1LV3R baef3289fe fix(service/admin): Prevent console from being spawned when no TTY is available 2026-07-12 20:22:18 +02:00
47 changed files with 835 additions and 1437 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@50414676f9f5d50a65992c6dd2ed02641263226c # v2
uses: https://github.com/taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # 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@718ea10b132b3b2eba29c1007bb80653f286566b # v3
uses: https://github.com/softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # 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@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: https://github.com/actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
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@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
uses: https://github.com/actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: "22"
+1 -1
View File
@@ -218,7 +218,7 @@ jobs:
path: binaries
merge-multiple: true
- name: Create Release and Upload
uses: https://github.com/softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3
uses: https://github.com/softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # 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.252.1@sha256:121eb04ef758537019fb58f587aa53c99e5fda4e703993ce2ca01bd4b3926bd4
image: ghcr.io/renovatebot/renovate:43.260.2@sha256:60d830108d9ff6408d11a55d5f110ee0976bed78a6b82b08211e3ddb72f72952
options: --tmpfs /tmp:exec
steps:
- name: Checkout
+9
View File
@@ -1,3 +1,12 @@
# 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
+2 -2
View File
@@ -26,7 +26,7 @@ ### Pre-commit Checks
```bash
# Install prek using cargo-binstall
# Install prek using cargo-binstall https://github.com/cargo-bins/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](/development/code_style.md)
2. Your code follows the [code style guide](docs/development/code_style.mdx)
3. Your commit messages follow the conventional commits format
4. Tests are added for new functionality
5. Documentation is updated if needed
Generated
+135 -232
View File
@@ -259,9 +259,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "aws-lc-rs"
version = "1.17.0"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00"
checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad"
dependencies = [
"aws-lc-sys",
"zeroize",
@@ -269,14 +269,15 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
version = "0.41.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
"pkg-config",
]
[[package]]
@@ -557,9 +558,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "bytemuck"
version = "1.25.0"
version = "1.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424"
[[package]]
name = "byteorder"
@@ -575,9 +576,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.12.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "bytesize"
@@ -641,9 +642,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.65"
version = "1.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -826,7 +827,7 @@ dependencies = [
[[package]]
name = "conduwuit"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"aws-lc-rs",
"clap",
@@ -864,7 +865,7 @@ dependencies = [
[[package]]
name = "conduwuit_admin"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"assign",
"clap",
@@ -890,7 +891,7 @@ dependencies = [
[[package]]
name = "conduwuit_api"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"assign",
"async-trait",
@@ -928,7 +929,7 @@ dependencies = [
[[package]]
name = "conduwuit_build_metadata"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"built",
"cargo_metadata",
@@ -936,7 +937,7 @@ dependencies = [
[[package]]
name = "conduwuit_core"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"argon2",
"arrayvec",
@@ -1004,7 +1005,7 @@ dependencies = [
[[package]]
name = "conduwuit_database"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"async-channel",
"conduwuit_core",
@@ -1025,7 +1026,7 @@ dependencies = [
[[package]]
name = "conduwuit_macros"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"cargo_toml",
"itertools 0.15.0",
@@ -1036,7 +1037,7 @@ dependencies = [
[[package]]
name = "conduwuit_router"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"assign",
"axum",
@@ -1074,7 +1075,7 @@ dependencies = [
[[package]]
name = "conduwuit_service"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"askama",
"assign",
@@ -1126,7 +1127,7 @@ dependencies = [
[[package]]
name = "conduwuit_web"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"askama",
"assign",
@@ -1361,18 +1362,18 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
@@ -1380,27 +1381,27 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
version = "0.9.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.12"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
[[package]]
name = "crossterm"
@@ -1463,9 +1464,9 @@ dependencies = [
[[package]]
name = "ctor"
version = "1.0.7"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a"
checksum = "fb22e947478ccf9dc44d8922042c677a63fbb88f2cb468521d1145816e5087cb"
dependencies = [
"link-section",
"linktime-proc-macro",
@@ -1513,38 +1514,14 @@ 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 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",
"darling_core",
"darling_macro",
]
[[package]]
@@ -1560,24 +1537,13 @@ 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 0.23.0",
"darling_core",
"quote",
"syn",
]
@@ -2122,9 +2088,11 @@ 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]]
@@ -2290,7 +2258,7 @@ dependencies = [
"http",
"httpdate",
"mime",
"sha1 0.10.6",
"sha1 0.10.7",
]
[[package]]
@@ -2475,9 +2443,9 @@ dependencies = [
[[package]]
name = "http-body-util"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
dependencies = [
"bytes",
"futures-core",
@@ -2906,11 +2874,11 @@ dependencies = [
[[package]]
name = "jobserver"
version = "0.1.34"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [
"getrandom 0.3.4",
"getrandom 0.4.3",
"libc",
]
@@ -3060,9 +3028,9 @@ dependencies = [
[[package]]
name = "link-section"
version = "0.18.3"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24670b639492630905459a6c7d47f063d33c2d4fcd5362f6e5827c5613976c9f"
checksum = "e333fe507b738576d6da5bb3f1a7d7a1c80307ed9ef31624c057d844c19c93e9"
[[package]]
name = "linked-hash-map"
@@ -3199,19 +3167,20 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "memchr"
version = "2.8.2"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "memory-serve"
version = "2.1.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81b5bbad2035f57b1e95f66da606832edd935b47d82312e38e1ccffbcfb8a427"
checksum = "18718e8f7eefb3194d346474ac95f50ada065ff043e0074e5b8e85b85148122f"
dependencies = [
"axum",
"brotli",
"flate2",
"fs-err",
"mime_guess",
"sha256",
"tracing",
@@ -3395,9 +3364,9 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c863e9ab5e7bf9c99ba75e1050f1e4d624ae87ed3532d6238ffbdc7b585dbbe6"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [
"num-integer",
"num-traits",
@@ -3414,7 +3383,7 @@ dependencies = [
"num-integer",
"num-iter",
"num-traits",
"rand 0.8.6",
"rand 0.8.7",
"smallvec",
"zeroize",
]
@@ -3445,11 +3414,10 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.45"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
@@ -3495,7 +3463,7 @@ dependencies = [
"chrono",
"getrandom 0.2.17",
"http",
"rand 0.8.6",
"rand 0.8.7",
"reqwest 0.12.28",
"serde",
"serde_json",
@@ -3700,7 +3668,7 @@ dependencies = [
"oauth2",
"p256",
"p384",
"rand 0.8.6",
"rand 0.8.7",
"rsa",
"serde",
"serde-value",
@@ -3791,7 +3759,7 @@ dependencies = [
"opentelemetry",
"percent-encoding",
"portable-atomic",
"rand 0.9.4",
"rand 0.9.5",
"thiserror 2.0.18",
"tokio",
"tokio-stream",
@@ -3968,7 +3936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"rand 0.8.6",
"rand 0.8.7",
]
[[package]]
@@ -4112,22 +4080,22 @@ dependencies = [
]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
name = "proc-macro-error-attr3"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
checksum = "34e4dd828515431dd6c4a030d26f7eaed7dd4778226e9d2bb968d65ca4ec3d4d"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
name = "proc-macro-error3"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
checksum = "5ee475e440453418ff1335189eddf7101ba502cd818ab7ae04209bc83aa925aa"
dependencies = [
"proc-macro-error-attr2",
"proc-macro-error-attr3",
"proc-macro2",
"quote",
"syn",
@@ -4207,9 +4175,9 @@ checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
[[package]]
name = "pxfm"
version = "0.1.29"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f"
checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
[[package]]
name = "quick-error"
@@ -4240,15 +4208,16 @@ dependencies = [
[[package]]
name = "quinn-proto"
version = "0.11.15"
version = "0.11.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e"
checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560"
dependencies = [
"aws-lc-rs",
"bytes",
"getrandom 0.3.4",
"getrandom 0.4.3",
"lru-slab",
"rand 0.9.4",
"rand 0.10.2",
"rand_pcg",
"ring",
"rustc-hash",
"rustls",
@@ -4262,16 +4231,16 @@ dependencies = [
[[package]]
name = "quinn-udp"
version = "0.5.14"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.60.2",
"windows-sys 0.61.2",
]
[[package]]
@@ -4303,9 +4272,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.8.6"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
dependencies = [
"libc",
"rand_chacha 0.3.1",
@@ -4314,9 +4283,9 @@ dependencies = [
[[package]]
name = "rand"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.5",
@@ -4377,6 +4346,15 @@ 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"
@@ -4419,9 +4397,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.12.4"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2"
dependencies = [
"aho-corasick",
"memchr",
@@ -4431,9 +4409,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.14"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db"
dependencies = [
"aho-corasick",
"memchr",
@@ -4799,7 +4777,7 @@ dependencies = [
[[package]]
name = "ruminuwuity"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"assign",
"ruma",
@@ -4837,9 +4815,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
version = "0.1.27"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb"
[[package]]
name = "rustc-hash"
@@ -4871,9 +4849,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.41"
version = "0.23.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f"
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
dependencies = [
"aws-lc-rs",
"log",
@@ -4948,9 +4926,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.22"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "rustyline-async"
@@ -5136,7 +5114,7 @@ version = "0.48.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8c44ee52fa3d30581f951b57aec754ef9cd70186df5fb788367804360c48097"
dependencies = [
"rand 0.9.4",
"rand 0.9.5",
"sentry-types",
"serde",
"serde_json",
@@ -5209,7 +5187,7 @@ checksum = "1be93ef0435a5ab91f88178fb7681249437e875d004f996ecb35be94dc99a0e0"
dependencies = [
"debugid",
"hex",
"rand 0.9.4",
"rand 0.9.5",
"serde",
"serde_json",
"thiserror 2.0.18",
@@ -5389,7 +5367,7 @@ version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
dependencies = [
"darling 0.23.0",
"darling",
"proc-macro2",
"quote",
"syn",
@@ -5397,9 +5375,9 @@ dependencies = [
[[package]]
name = "sha1"
version = "0.10.6"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
@@ -5722,9 +5700,9 @@ dependencies = [
[[package]]
name = "termimad"
version = "0.35.0"
version = "0.35.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "903906afabf58273a7a90b47e0e60701cbd01731ca72ba88508b38b5539647f4"
checksum = "d53d4b1294b87e81925b7ae7f8f4d000376e3a5b3349d978429665428a793fcb"
dependencies = [
"coolor",
"crokey",
@@ -5788,9 +5766,9 @@ dependencies = [
[[package]]
name = "thread_local"
version = "1.1.9"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
dependencies = [
"cfg-if",
]
@@ -5865,9 +5843,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.11.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f"
dependencies = [
"tinyvec_macros",
]
@@ -6230,7 +6208,7 @@ dependencies = [
"futures",
"http",
"parking_lot",
"rand 0.9.4",
"rand 0.9.5",
"serde",
"serde_json",
"thiserror 2.0.18",
@@ -6444,9 +6422,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.23.4"
version = "1.23.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
checksum = "ea5fab0d6c3c01ae70085a09cb03d4c7a1d6314e2b3e075392783396d724ca0a"
dependencies = [
"getrandom 0.4.3",
"js-sys",
@@ -6472,13 +6450,12 @@ dependencies = [
[[package]]
name = "validator_derive"
version = "0.20.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca"
checksum = "240e4b81c20a1d6d50d1d7265c658dfbd204e8b9ac4d80f3c931f39462196335"
dependencies = [
"darling 0.20.11",
"once_cell",
"proc-macro-error2",
"darling",
"proc-macro-error3",
"proc-macro2",
"quote",
"syn",
@@ -6779,16 +6756,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"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",
"windows-targets",
]
[[package]]
@@ -6806,31 +6774,14 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"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",
"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",
]
[[package]]
@@ -6839,96 +6790,48 @@ 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"
@@ -6972,7 +6875,7 @@ dependencies = [
[[package]]
name = "xtask"
version = "26.6.1"
version = "26.6.2"
dependencies = [
"askama",
"cargo_metadata",
@@ -7012,18 +6915,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.52"
version = "0.8.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.52"
version = "0.8.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
dependencies = [
"proc-macro2",
"quote",
@@ -7092,9 +6995,9 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.21"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
checksum = "bd2f034a4bebf216c9e4b7083603e024cf930873fd67830cfb083c9fa33129d9"
[[package]]
name = "zstd"
+1 -1
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.1"
version = "26.6.2"
[workspace.metadata.crane]
name = "conduwuit"
+1
View File
@@ -0,0 +1 @@
Appservices are now properly able to create devices for E2EE.
-1
View File
@@ -1 +0,0 @@
Fixed `global.oauth.compatibility_mode` being required, despite being ignored, when the `[global.oauth.oidc]` config section is provided.
-1
View File
@@ -1 +0,0 @@
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.
+1
View File
@@ -0,0 +1 @@
Fixed newly created rooms failing to sync properly in clients using legacy sync.
+1
View File
@@ -0,0 +1 @@
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 the deeplink redirect for deleting devices. Contributed by @koen
+1
View File
@@ -0,0 +1 @@
Fix status code for oauth registration. Contributed by @n00byking
+1
View File
@@ -0,0 +1 @@
Exempt m.room.create from auth_events check. Contributed by @eleboucher
@@ -6,10 +6,10 @@
"message": "Welcome to Continuwuity! Important announcements about the project will appear here."
},
{
"id": 15,
"id": 16,
"mention_room": true,
"date": "2026-07-10",
"message": "[Continuwuity 26.6.1](https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.6.1) has just been released! This release contains a bunch of bugfixes for bugs found initially after 26.6.0's release - please read the changelog before updating!"
"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."
}
]
}
Generated
+21 -21
View File
@@ -3,11 +3,11 @@
"advisory-db": {
"flake": false,
"locked": {
"lastModified": 1781566179,
"narHash": "sha256-Tqv8I586fYzWpEW/Smq/JqESFa3DVVzVWsnAMtvhy/I=",
"lastModified": 1783840254,
"narHash": "sha256-XjyvZk0f3YiinVHmkGOotmLBAzvK+LwEJYj2QqJ5pn8=",
"owner": "rustsec",
"repo": "advisory-db",
"rev": "74e084413d979d52d2f93b1d93b1ab7b9ee648f5",
"rev": "6e3286f4efa8c142fb33e5ea4342c8db6693cf34",
"type": "github"
},
"original": {
@@ -18,11 +18,11 @@
},
"crane": {
"locked": {
"lastModified": 1780532242,
"narHash": "sha256-D+BsdpxmtUwtqGoY0IXPhHgTlmqgcZKCEo1oMyn7ep0=",
"lastModified": 1783203018,
"narHash": "sha256-G6R9IT/xwFuu+CYBWDUAok6AdC4ERC4ZfPPFtEpxnZE=",
"owner": "ipetkov",
"repo": "crane",
"rev": "59a82a1222dd3b2080b5cc52a1a2e8d5f1b77f37",
"rev": "80db5bdc391be8a1794f6d8a2d56e3a84ebcede2",
"type": "github"
},
"original": {
@@ -39,11 +39,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1781527054,
"narHash": "sha256-1fX9ev2Fh5QoKQ41G9dYutjo5j/jywu6tZse5Eb1Ck4=",
"lastModified": 1783844668,
"narHash": "sha256-3MOpw4y3reoErRLvFBDz0t9aG6FWXUj7leKvUns5eQA=",
"owner": "nix-community",
"repo": "fenix",
"rev": "8c2e51dffefc040a21975da7abf6f252c8c9b783",
"rev": "4e49ef62eedaa5c149d62b63b3f53844e1cc45d7",
"type": "github"
},
"original": {
@@ -74,11 +74,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"lastModified": 1782949081,
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
"type": "github"
},
"original": {
@@ -89,11 +89,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1781074563,
"narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=",
"lastModified": 1783776592,
"narHash": "sha256-UgCQzxeWI75XM8G+hPrPh+MKzEPjG3SpAj7dtqSbksA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca",
"rev": "e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3",
"type": "github"
},
"original": {
@@ -105,11 +105,11 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1777168982,
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
"lastModified": 1782614948,
"narHash": "sha256-ePjCwr1sNm9NYUqywL7QfK3JnlS015msC+eBu2zKlp8=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14",
"rev": "db3f255737b94216eb71cce308e2912cf6bc2d7c",
"type": "github"
},
"original": {
@@ -132,11 +132,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1781453968,
"narHash": "sha256-+V3nK4pCngbmgyVGXY6Kkrlevp4ocPkJJLf2aqwkDNA=",
"lastModified": 1783779020,
"narHash": "sha256-vpm418WZa9l1KUl6HRs+Ga+SIfE+D2/sV4olwTlilYc=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "cc272809a173c2c11d0e479d639c811c1eacf049",
"rev": "5be5e89cf0145d73a85c805726821b4adfc3af48",
"type": "github"
},
"original": {
+48 -48
View File
@@ -510,14 +510,14 @@
}
},
"node_modules/@shikijs/core": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.2.0.tgz",
"integrity": "sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.3.1.tgz",
"integrity": "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/primitive": "4.2.0",
"@shikijs/types": "4.2.0",
"@shikijs/primitive": "4.3.1",
"@shikijs/types": "4.3.1",
"@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.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.2.0.tgz",
"integrity": "sha512-fjETeq1k5ffyXqRgS6+3hpvqseLalp1kjNfRbXpUgWR8FpZ1CmQfiNHovc5lncYjt/Vg5JK/WJEmLahjwMa0og==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.3.1.tgz",
"integrity": "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.2.0",
"@shikijs/types": "4.3.1",
"@shikijs/vscode-textmate": "^10.0.2",
"oniguruma-to-es": "^4.3.6"
},
@@ -542,13 +542,13 @@
}
},
"node_modules/@shikijs/engine-oniguruma": {
"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==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.3.1.tgz",
"integrity": "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.2.0",
"@shikijs/types": "4.3.1",
"@shikijs/vscode-textmate": "^10.0.2"
},
"engines": {
@@ -556,26 +556,26 @@
}
},
"node_modules/@shikijs/langs": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.2.0.tgz",
"integrity": "sha512-bwrVRlJ0wUhZxAbVdvBbv2TTC9yLsh4C/IO5Ofz0T8MQntgDvyVnkbjw9vi50r1kx7RCIJdnJnjZAwmAsXFLZQ==",
"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==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.2.0"
"@shikijs/types": "4.3.1"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@shikijs/primitive": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.2.0.tgz",
"integrity": "sha512-NOq+DtUkVBJtZMVXL5A0vI0Xk8nvDYaXetFHSJFlOqjDZIVhIPRYFdGkSoElDqNuegikcc3A76SNUa8dTqtAYA==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.3.1.tgz",
"integrity": "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.2.0",
"@shikijs/types": "4.3.1",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
},
@@ -584,16 +584,16 @@
}
},
"node_modules/@shikijs/rehype": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.2.0.tgz",
"integrity": "sha512-ST3EWye/dwF1gWskczJNBnwFtDzEQ9ceytXZtyc/GfwR5V0qJrkoSGZO55O3SAKDDsXkTDcsfwd9pVe7ROlAHg==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.3.1.tgz",
"integrity": "sha512-oshrlfUF3VPUJfnp5K1lLwsS/SRBKrIxONpdWebSKZXdBE3UsZnxgqpvRUA8UsofS7vmjFOCAHIT71ECbmOxTw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.2.0",
"@shikijs/types": "4.3.1",
"@types/hast": "^3.0.4",
"hast-util-to-string": "^3.0.1",
"shiki": "4.2.0",
"shiki": "4.3.1",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0"
},
@@ -602,22 +602,22 @@
}
},
"node_modules/@shikijs/themes": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.2.0.tgz",
"integrity": "sha512-RX8IHYeLv8Cu2W6ruc3RxUqWn0IYCqSrMBzi/uRGAmfyDNOnNO5BF/Px7o97n4XTpmFTo5GbRaazuOWj+2ak2w==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.3.1.tgz",
"integrity": "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@shikijs/types": "4.2.0"
"@shikijs/types": "4.3.1"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@shikijs/types": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.2.0.tgz",
"integrity": "sha512-VT/MKtlpOhEPZloSH3Pb9WCZEBDoQVMa9jedp5UAwmJOar1DVc9DRODAxmYPW9M93IK4ryuqRejFfmlvlVDemw==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.3.1.tgz",
"integrity": "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -684,9 +684,9 @@
}
},
"node_modules/@types/hast": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz",
"integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1076,9 +1076,9 @@
}
},
"node_modules/@ungap/structured-clone": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
"integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
"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==",
"dev": true,
"license": "ISC"
},
@@ -3589,18 +3589,18 @@
"license": "MIT"
},
"node_modules/shiki": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-4.2.0.tgz",
"integrity": "sha512-hjNax6o/ylDy9lefQEaSDtzaT3iVNtZ3WmpQnbuQNoG4xvnSKf2kSKbihZVO4JRG1TTMejs7CmNRYlWgAL66pQ==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-4.3.1.tgz",
"integrity": "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@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/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/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
},
+3 -13
View File
@@ -514,12 +514,7 @@ pub(super) async fn force_join_list_of_local_users(
.services
.rooms
.membership
.join_room(
&user_id,
&room_id,
Some(String::from(BULK_JOIN_REASON)),
servers.clone(),
)
.join_room(&user_id, &room_id, Some(String::from(BULK_JOIN_REASON)), &servers)
.await
{
| Ok(_res) => {
@@ -601,12 +596,7 @@ pub(super) async fn force_join_all_local_users(
.services
.rooms
.membership
.join_room(
user_id,
&room_id,
Some(String::from(BULK_JOIN_REASON)),
servers.clone(),
)
.join_room(user_id, &room_id, Some(String::from(BULK_JOIN_REASON)), &servers)
.await
{
| Ok(_res) => {
@@ -653,7 +643,7 @@ pub(super) async fn force_join_room(
self.services
.rooms
.membership
.join_room(&user_id, &room_id, None, servers)
.join_room(&user_id, &room_id, None, &servers)
.await?;
self.write_str(&format!("{user_id} has been joined to {room_id}."))
+1 -1
View File
@@ -26,7 +26,7 @@
};
use service::{mailer::messages, uiaa::UiaaInitiator, users::HashedPassword};
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
use super::DEVICE_ID_LENGTH;
use crate::{Ruma, router::ClientIdentity};
pub(crate) mod register;
+8 -6
View File
@@ -20,9 +20,12 @@
assign,
};
use serde_json::value::RawValue;
use service::{mailer::messages, users::HashedPassword};
use service::{
mailer::messages,
users::{DeviceToken, HashedPassword},
};
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
use super::DEVICE_ID_LENGTH;
use crate::Ruma;
/// # `POST /_matrix/client/v3/register`
@@ -119,7 +122,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 = utils::random_string(TOKEN_LENGTH);
let new_token = DeviceToken::new_random();
// Create device for this account
services
@@ -127,8 +130,7 @@ pub(crate) async fn register_route(
.create_device(
&user_id,
&device_id,
&new_token,
None,
Some(new_token.clone()),
body.initial_device_display_name.clone(),
Some(client.to_string()),
)
@@ -142,7 +144,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,
access_token: token.map(DeviceToken::into_token),
device_id: device,
refresh_token: None,
expires_in: None,
+1 -8
View File
@@ -89,14 +89,7 @@ pub(crate) async fn update_device_route(
services
.users
.create_device(
sender_user,
&device_id,
&appservice.registration.as_token,
None,
None,
Some(client.to_string()),
)
.create_device(sender_user, &device_id, None, None, Some(client.to_string()))
.await?;
return Ok(update_device::v3::Response::new());
+2 -2
View File
@@ -75,7 +75,7 @@ pub(crate) async fn join_room_by_id_route(
let room_id = services
.rooms
.membership
.join_room(sender_user, &body.room_id, body.reason.clone(), servers)
.join_room(sender_user, &body.room_id, body.reason.clone(), &servers)
.boxed()
.await?;
@@ -195,7 +195,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
let room_id = services
.rooms
.membership
.join_room(sender_user, &room_id, body.reason.clone(), servers)
.join_room(sender_user, &room_id, body.reason.clone(), &servers)
.boxed()
.await?;
+8 -8
View File
@@ -141,7 +141,7 @@ pub(crate) async fn knock_room_route(
},
};
knock_room_by_id_helper(&services, sender_user, &room_id, body.reason.clone(), servers)
knock_room_by_id_helper(&services, sender_user, &room_id, body.reason.clone(), &servers)
.boxed()
.await
}
@@ -151,7 +151,7 @@ async fn knock_room_by_id_helper(
sender_user: &UserId,
room_id: &RoomId,
reason: Option<String>,
servers: Vec<OwnedServerName>,
servers: &[OwnedServerName],
) -> Result<knock_room::v3::Response> {
let state_lock = services.rooms.state.mutex.lock(room_id).await;
@@ -242,7 +242,7 @@ async fn knock_room_by_id_helper(
match services
.rooms
.membership
.join_room(sender_user, room_id, reason.clone(), servers.clone())
.join_room(sender_user, room_id, reason.clone(), servers)
.await
{
| Ok(_) => return Ok(knock_room::v3::Response::new(room_id.to_owned())),
@@ -326,7 +326,7 @@ async fn knock_room_helper_local(
sender_user: &UserId,
room_id: &RoomId,
reason: Option<String>,
servers: Vec<OwnedServerName>,
servers: &[OwnedServerName],
state_lock: RoomMutexGuard,
) -> Result {
debug_info!("We can knock locally");
@@ -486,7 +486,7 @@ async fn knock_room_helper_remote(
sender_user: &UserId,
room_id: &RoomId,
reason: Option<String>,
servers: Vec<OwnedServerName>,
servers: &[OwnedServerName],
state_lock: RoomMutexGuard,
) -> Result {
info!("Knocking {room_id} over federation.");
@@ -694,7 +694,7 @@ async fn make_knock_request(
services: &Services,
sender_user: &UserId,
room_id: &RoomId,
servers: Vec<OwnedServerName>,
servers: &[OwnedServerName],
) -> Result<(federation::membership::prepare_knock_event::v1::Response, OwnedServerName)> {
let mut make_knock_response_and_server =
Err!(BadServerResponse("No server available to assist in knocking."));
@@ -702,7 +702,7 @@ async fn make_knock_request(
let mut make_knock_counter: usize = 0;
for remote_server in servers {
if services.globals.server_is_ours(&remote_server) {
if services.globals.server_is_ours(remote_server) {
continue;
}
@@ -716,7 +716,7 @@ async fn make_knock_request(
let make_knock_response = services
.sending
.send_federation_request(&remote_server, request)
.send_federation_request(remote_server, request)
.await;
trace!("make_knock response: {make_knock_response:?}");
+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(Json(RegisteredClient { client_id, metadata }).into_response())
Ok((StatusCode::CREATED, Json(RegisteredClient { client_id, metadata })).into_response())
}
+8 -8
View File
@@ -29,8 +29,9 @@
},
assign,
};
use service::users::DeviceToken;
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
use super::DEVICE_ID_LENGTH;
use crate::Ruma;
/// # `GET /_matrix/client/v3/login`
@@ -196,8 +197,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 (ensuring no collisions)
let token = services.users.generate_unique_token().await;
// Generate a new token for the device
let token = DeviceToken::new_random();
// Determine if device_id was provided and exists in the db for this user
let device_exists = if body.device_id.is_some() {
@@ -213,7 +214,7 @@ pub(crate) async fn login_route(
if device_exists {
services
.users
.set_token(&user_id, &device_id, &token, None)
.set_token(&user_id, &device_id, token.clone())
.await?;
} else {
services
@@ -221,8 +222,7 @@ pub(crate) async fn login_route(
.create_device(
&user_id,
&device_id,
&token,
None,
Some(token.clone()),
body.initial_device_display_name.clone(),
Some(client.to_string()),
)
@@ -241,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, device_id), {
Ok(assign!(login::v3::Response::new(user_id, token.into_token(), device_id), {
well_known: client_discovery_info,
expires_in: None,
home_server: Some(services.config.server_name.clone()),
@@ -273,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 = utils::random_string(TOKEN_LENGTH);
let login_token = DeviceToken::new_random().into_token();
let expires_in = services.users.create_login_token(sender_user, &login_token);
Ok(get_login_token::v1::Response::new(
+2 -3
View File
@@ -15,10 +15,9 @@
#[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
+22 -18
View File
@@ -219,25 +219,29 @@ 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.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
.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."
)));
}
Some(device_id.to_owned())
} else {
None
};
Some(device_id.to_owned())
} else {
None
};
Ok(ClientIdentity::Appservice {
sender_user,
+1 -2
View File
@@ -307,7 +307,7 @@ async fn validate_invite_state(
"PDU in invite state (index {idx}) violates the room event format"
)));
};
let (state_event_room_id, state_event_id, mut state_event_json) = services
let (state_event_room_id, state_event_id, state_event_json) = services
.rooms
.event_handler
.parse_incoming_pdu(&raw_pdu, Some(room_version_rules))
@@ -351,7 +351,6 @@ async fn validate_invite_state(
entry.key(),
))),
| Entry::Vacant(entry) => {
state_event_json.insert("event_id".to_owned(), state_event_id.to_string().into());
if entry.key().0 == StateEventType::RoomCreate {
// Ensure this is a legal create event.
let pdu_event =
+1 -2
View File
@@ -91,7 +91,7 @@ pub(crate) async fn validate_any_membership_event(
expected_room_id: OwnedRoomId,
expected_event_id: OwnedEventId,
) -> Result<(CanonicalJsonObject, MembershipState, OwnedUserId, OwnedUserId)> {
let (template_room_id, template_event_id, mut pdu) = services
let (template_room_id, template_event_id, pdu) = services
.rooms
.event_handler
.parse_incoming_pdu(body, Some(room_version_rules))
@@ -164,6 +164,5 @@ pub(crate) async fn validate_any_membership_event(
err!(Request(InvalidParam("Membership event violates the room event format: {e}")))
})?;
// pdu.insert("event_id".to_owned(), template_event_id.to_string().into());
Ok((pdu, membership.into(), sender_user, recipient_user))
}
+10 -2
View File
@@ -1,4 +1,6 @@
use conduwuit::{Err, Result, debug, debug_info, error, info};
use std::io::IsTerminal;
use conduwuit::{Err, Result, debug, debug_info, error, info, warn};
use ruma::events::room::message::RoomMessageEventContent;
use tokio::time::{Duration, sleep};
@@ -7,10 +9,16 @@
pub(super) const SIGNAL: &str = "SIGUSR2";
impl super::Service {
/// Possibly spawn the terminal console at startup if configured.
/// Possibly spawn the terminal console at startup if configured and a TTY
/// is available.
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;
+7 -9
View File
@@ -25,7 +25,7 @@
OAuthError, ResponseMode, Scope, TokenRequest, TokenResponse, TokenType,
},
},
users,
users::{self, DeviceToken},
};
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 = Self::generate_token();
let access_token = DeviceToken::new_random().with_max_age(Self::ACCESS_TOKEN_MAX_AGE);
let refresh_token = Self::generate_token();
let device_id = requested_scopes
@@ -376,8 +376,7 @@ async fn create_session(
.create_device(
&authorizing_user,
device_id,
&access_token,
Some(Self::ACCESS_TOKEN_MAX_AGE),
Some(access_token.clone()),
client_name,
None,
)
@@ -413,7 +412,7 @@ async fn create_session(
);
Ok(TokenResponse {
access_token,
access_token: access_token.into_token(),
token_type: TokenType::Bearer,
expires_in: Self::ACCESS_TOKEN_MAX_AGE.as_secs(),
scope: requested_scopes.iter().join(" "),
@@ -449,7 +448,7 @@ async fn refresh_session(
assert_eq!(&client_id, &session_info.client_id, "session info client id mismatch");
let new_access_token = Self::generate_token();
let new_access_token = DeviceToken::new_random().with_max_age(Self::ACCESS_TOKEN_MAX_AGE);
let new_refresh_token = Self::generate_token();
let scope = session_info.scopes.iter().join(" ");
session_info
@@ -461,8 +460,7 @@ async fn refresh_session(
.set_token(
&refresh_token_info.user_id,
&refresh_token_info.device_id,
&new_access_token,
Some(Self::ACCESS_TOKEN_MAX_AGE),
new_access_token.clone(),
)
.await
.expect("should be able to set token");
@@ -479,7 +477,7 @@ async fn refresh_session(
.raw_put(&new_refresh_token, Json(refresh_token_info));
Ok(TokenResponse {
access_token: new_access_token,
access_token: new_access_token.into_token(),
token_type: TokenType::Bearer,
expires_in: Self::ACCESS_TOKEN_MAX_AGE.as_secs(),
scope,
+25 -14
View File
@@ -6,8 +6,9 @@
};
use futures::future::ready;
use ruma::{
CanonicalJsonObject, EventId, OwnedEventId, ServerName, api::error::ErrorKind,
canonical_json::redact, events::StateEventType, room_version_rules::RoomVersionRules,
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, ServerName,
api::error::ErrorKind, canonical_json::redact, events::StateEventType,
room_version_rules::RoomVersionRules,
};
use crate::rooms::{
@@ -42,11 +43,27 @@ pub fn pdu_format_check_1(
return Err!(Request(BadJson("PDU has too many auth events")));
}
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")));
// 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 prev_events = expect_event_id_array(pdu_json, "prev_events")?;
@@ -66,12 +83,6 @@ pub async fn signature_hash_check_2_3(
pdu_json: CanonicalJsonObject,
room_version_rules: &RoomVersionRules,
) -> Result<CanonicalJsonObject> {
#[cfg(debug_assertions)]
{
if pdu_json.contains_key("event_id") {
conduwuit::warn!("Checking signature of a PDU that has an `event_id` present");
}
}
match self
.services
.server_keys
@@ -130,7 +141,7 @@ pub async fn auth_state_check_4(
/// Returns a boolean indicating whether the event is authorised, and also
/// the resolved state before the event for later use. Returns an error if
/// state fetching or auth checking fails.
pub async fn state_before_check_5(
pub(super) async fn state_before_check_5(
&self,
incoming_pdu: &PduEvent,
room_version_rules: &RoomVersionRules,
-84
View File
@@ -1,84 +0,0 @@
use conduwuit::{Result, err, utils, utils::TryFutureExtExt};
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, OwnedUserId, RoomId, UserId,
canonical_json::{redact, to_canonical_value},
events::{
StateEventType,
room::member::{MembershipState, RoomMemberEventContent},
},
room_version_rules::RoomVersionRules,
};
impl super::Service {
/// Creates a membership event locally but seeds it with data from a remote
/// template. This means we retain total control over the created PDU, but
/// still use the data from the remote server that we have to blindly trust,
/// like auth/prev events and depth.
pub(super) async fn seed_local_membership_pdu(
&self,
room_id: &RoomId,
user_id: &UserId,
membership: MembershipState,
reason: Option<String>,
template: CanonicalJsonObject,
room_version_rules: &RoomVersionRules,
) -> Result<CanonicalJsonObject> {
// First we redact the PDU to remove any unrecognised fields.
let mut template = redact(template, &room_version_rules.redaction, None)
.map_err(|e| err!(Request(BadJson("Failed to redact membership template: {e:?}"))))?;
// Remove fields that are not necessary but not covered by redaction.
template.remove("event_id");
template.remove("hashes");
template.remove("prev_state");
template.remove("origin");
template.remove("membership");
template.remove("redacts");
// Force set some fields to known good values.
let join_authorized_via_users_server = {
if room_version_rules
.signatures
.check_join_authorised_via_users_server
{
template
.get("content")
.map(|s| {
s.as_object()?
.get("join_authorised_via_users_server")?
.as_str()
})
.and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok())
} else {
None
}
};
let mut content = RoomMemberEventContent::new(membership);
let (dn, av) = tokio::join!(
self.services.users.displayname(user_id).ok(),
self.services.users.avatar_url(user_id).ok()
);
content.displayname = dn;
content.avatar_url = av;
content.reason = reason;
content
.join_authorized_via_users_server
.clone_from(&join_authorized_via_users_server);
template.insert("content".to_owned(), to_canonical_value(content)?);
template.insert(
"origin_server_ts".to_owned(),
CanonicalJsonValue::Integer(
utils::millis_since_unix_epoch()
.try_into()
.expect("Timestamp is valid js_int value"),
),
);
template.insert("room_id".to_owned(), room_id.as_str().into());
template.insert("sender".to_owned(), user_id.to_string().into());
template.insert("state_key".to_owned(), user_id.to_string().into());
template.insert("type".to_owned(), StateEventType::RoomMember.to_string().into());
Ok(template)
}
}
-782
View File
@@ -1,782 +0,0 @@
use std::{
borrow::Borrow,
collections::{BTreeMap, HashMap},
sync::Arc,
time::Instant,
};
use assign::assign;
use conduwuit::{
Err, Event, PduEvent, Result, debug, debug_error, debug_info, err, error, info,
matrix::{StateKey, event::gen_event_id},
state_res,
state_res::EventTypeExt,
trace,
utils::{
IterStream,
stream::{BroadbandExt, WidebandExt, automatic_width},
},
warn,
};
use futures::{StreamExt, future::ready};
use ruma::{
CanonicalJsonObject, EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, ServerName,
UserId,
api::{
error::{ErrorKind, IncompatibleRoomVersionErrorData},
federation::membership::{create_join_event, prepare_join_event},
},
events::{StateEventType, room::member::MembershipState},
room_version_rules::RoomVersionRules,
};
use serde_json::value::to_raw_value;
use crate::rooms::{
event_handler::{DagBuilderTree, build_local_dag},
membership::validate_remote_member_event_stub,
state::RoomMutexGuard,
state_compressor::{CompressedState, HashSetCompressStateEvent},
};
type StateTypeKey = (StateEventType, StateKey);
pub enum MakeJoinResult {
/// A make_join request returned and the response was validated
/// successfully.
Success((CanonicalJsonObject, RoomVersionId)),
/// A make_join response indicated further attempts to join should not be
/// made (e.g. Forbidden).
Fatal(conduwuit::Error),
/// A make_join request failed (e.g. remote server unreachable, or unable to
/// fulfill restricted join requirements), but another join attempt through
/// another remote should be attempted.
Retry,
}
fn state_key_from_json(obj: &CanonicalJsonObject) -> Option<(StateEventType, StateKey)> {
let event_type = StateEventType::from(obj.get("type")?.as_str()?);
let state_key = StateKey::from_str(obj.get("state_key")?.as_str()?);
Some((event_type, state_key))
}
impl super::Service {
/// Performs `POST /_matrix/federation/v1/make_join/{room_id}/{user_id}`.
///
/// If the request is successful, the resulting template is validated to
/// ensure the remote server isn't returning a bad event, and then the
/// template and room version are returned.
/// If validation fails, `Ok(None)` is returned, in
///
/// If the request is unsuccessful, either an `Err` or `Ok(None)` will be
/// returned, depending on the reason for the failure. If the join loop
/// should terminate (for example, a `M_FORBIDDEN` error was returned,
/// indicating we cannot join), then an `Err` will be returned. If the join
/// loop should continue because the request failed due to conditional
/// problems (e.g. server offline or unable to grant join), then `Ok(None)`
/// will be returned.
pub async fn make_join(
&self,
room_id: &RoomId,
user_id: &UserId,
via: &ServerName,
) -> MakeJoinResult {
assert!(
self.services.globals.user_is_local(user_id),
"can only make_join for local users"
);
let request = assign!(
prepare_join_event::v1::Request::new(room_id.to_owned(), user_id.to_owned()),
{ ver: self.services.server.supported_room_versions().collect() }
);
info!("Asking {via} for make_join");
let make_join_response = self
.services
.sending
.send_federation_request(via, request)
.await
.inspect(|_| info!("{via} finished make_join successfully"))
.inspect_err(|e| debug_info!("{via} failed to make_join: {e:?}"));
let make_join_response = match make_join_response {
| Ok(r) => r,
| Err(e) => return Self::handle_make_join_error(room_id, via, e),
};
// Make sure the remote server hasn't sent us garbage
let Ok(template) = serde_json::from_str(make_join_response.event.get())
.inspect_err(|e| warn!("{via} returned an invalid membership template: {e}"))
else {
return MakeJoinResult::Retry;
};
if let Err(e) =
validate_remote_member_event_stub(&MembershipState::Join, user_id, room_id, &template)
{
warn!("{via} returned an illegal membership template event: {e:?}");
return MakeJoinResult::Retry;
}
MakeJoinResult::Success((
template,
make_join_response.room_version.unwrap_or(RoomVersionId::V1),
))
}
/// Handles an error response from make_join, determining whether it should
/// be fatal (and returning `MakeJoinResult::Fatal(e)`), or
/// temporary/situational and returning `MakeJoinResult::Retry`.
fn handle_make_join_error(
room_id: &RoomId,
via: &ServerName,
e: conduwuit::Error,
) -> MakeJoinResult {
match e.kind() {
| ErrorKind::UnableToAuthorizeJoin => {
info!(
"{via} was unable to verify the joining user satisfied restricted join \
requirements: {e}."
);
MakeJoinResult::Retry
},
| ErrorKind::UnableToGrantJoin => {
info!(
"{via} believes the joining user satisfies restricted join rules, but is \
unable to authorise a join for us."
);
MakeJoinResult::Retry
},
| ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData {
room_version,
..
}) => {
warn!(
"{via} reports the room we are trying to join is v{room_version}, which we \
do not support."
);
MakeJoinResult::Fatal(e)
},
| ErrorKind::Forbidden => {
warn!("{via} refuses to let us join: {e}.");
MakeJoinResult::Fatal(e)
},
| ErrorKind::NotFound => {
info!("{via} does not know about {room_id}: {e}.");
MakeJoinResult::Retry
},
| ErrorKind::Unknown if e.status_code().is_server_error() => {
match e.status_code() {
| http::StatusCode::BAD_GATEWAY | http::StatusCode::SERVICE_UNAVAILABLE =>
info!("{via} is unavailable: {e}"),
| http::StatusCode::GATEWAY_TIMEOUT =>
info!("{via} timed out while handling make_join: {e}"),
| _ => info!("{via} encountered an internal server error: {e}."),
}
MakeJoinResult::Retry
},
| _ => {
info!("{via} unexpectedly failed to make_join: {e}.");
MakeJoinResult::Retry
},
}
}
pub async fn send_join(
&self,
room_id: OwnedRoomId,
event_id: OwnedEventId,
event: &CanonicalJsonObject,
via: &ServerName,
room_version_rules: &RoomVersionRules,
state_lock: &RoomMutexGuard,
) -> Result<Option<()>> {
let send_join_request = create_join_event::v2::Request::new(
room_id.clone(),
event_id.clone(),
self.services
.sending
.convert_to_outgoing_federation_event(event.clone())
.await,
);
info!("Asking {via} for send_join in room {room_id}");
let Ok(send_join_response) = self
.services
.sending
.send_slow_federation_request(via, send_join_request)
.await
.inspect(|resp| {
info!(
"{via} finished send_join successfully, returning {} state events and {} \
authentication events.",
resp.room_state.state.len(),
resp.room_state.auth_chain.len(),
);
})
.inspect_err(|e| debug_error!("{via} failed to send_join: {e:?}"))
else {
return Ok(None); // Try another server
};
self.services
.short
.get_or_create_shortroomid(&room_id)
.await;
info!("Acquiring server signing keys for room state events");
self.services
.server_keys
.acquire_events_pubkeys(
send_join_response
.room_state
.state
.iter()
.chain(send_join_response.room_state.auth_chain.iter()),
)
.await;
info!("Building state map");
let (create_event, untrusted_state_before) = match self
.build_state_map(&send_join_response, &room_id, room_version_rules)
.await
{
| Ok(result) => result,
| Err(e) => {
error!("Failed to build state map: {e}");
// This usually happens when the remote server sends a malformed response (e.g.
// forgotten m.room.create event). In this case, we might succeed with another
// server, so we will return Ok(None) to encourage the caller to re-attempt.
return Ok(None);
},
};
info!(
"Handling returned room state authentication events ({} events)",
send_join_response.room_state.auth_chain.len()
);
let auth_map = self
.process_send_join_auth_chain(
&send_join_response,
&room_id,
room_version_rules,
&create_event,
)
.await?;
info!("Handling returned room state ({} events)", untrusted_state_before.len());
let untrusted_state_before_by_id = untrusted_state_before
.values()
.map(|value| {
(
gen_event_id(value, room_version_rules)
.expect("must be able to generate event ID"),
value,
)
})
.collect::<HashMap<_, _>>();
let state_before = self
.process_send_join_state(
untrusted_state_before_by_id,
&auth_map,
room_version_rules,
&create_event,
)
.await?;
let state_before_by_id = state_before
.values()
.map(|value| (value.event_id().to_owned(), value))
.collect::<HashMap<_, _>>();
info!("Authorizing join event");
let final_membership_event =
if let Some(event) = send_join_response.room_state.event.as_ref() {
event
} else {
&to_raw_value(&event)
.expect("must be able to convert CanonicalJsonObject to RawJsonValue")
};
let (membership_pdu, membership_pdu_json) = self
.process_send_join_membership_event(
final_membership_event,
&event_id,
&state_before,
&state_before_by_id,
room_version_rules,
)
.await?;
// We've successfully joined now, and can update our local state cache.
info!("Compressing resolved state");
let compressed_state_map = state_before
.into_iter()
.stream()
.broad_filter_map(|(key, pdu)| async move {
let ssk = self
.services
.short
.get_or_create_shortstatekey(&key.0, key.1.as_str())
.await;
Some((ssk, pdu.event_id.clone()))
})
.collect::<Vec<_>>()
.await;
let compressed: CompressedState = self
.services
.state_compressor
.compress_state_events(
compressed_state_map
.iter()
.map(|(ssk, eid)| (ssk, eid.borrow())),
)
.collect()
.await;
debug!("Saving compressed state");
let HashSetCompressStateEvent {
shortstatehash: statehash_before_join,
added,
removed,
} = self
.services
.state_compressor
.save_state(&room_id, Arc::new(compressed))
.await?;
debug!("Updating state cache");
self.services
.state
.force_state(&room_id, statehash_before_join, added, removed, state_lock)
.await?;
let statehash_after_join = self
.services
.state
.append_to_state(&membership_pdu, &room_id)
.await?;
debug!("Promoting membership event");
self.services
.timeline
.append_pdu(
&membership_pdu,
membership_pdu_json,
std::iter::once(membership_pdu.event_id()),
state_lock,
&room_id,
)
.await
.inspect(|_| info!("Finished joining {room_id}"))?;
self.services
.metadata
.maybe_set_mindepth(&room_id, membership_pdu.depth.into())
.await;
info!("Setting final room state for new room");
// We set the room state after inserting the pdu, so that we never have a moment
// in time where events in the current room state do not exist
self.services
.state
.set_room_state(&room_id, statehash_after_join, state_lock);
Ok(Some(()))
}
async fn build_state_map(
&self,
response: &create_join_event::v2::Response,
room_id: &RoomId,
room_version_rules: &RoomVersionRules,
) -> Result<(PduEvent, HashMap<StateTypeKey, CanonicalJsonObject>)> {
// NOTE: this is untrusted as we haven't performed any of the PDU checks yet.
// Events in this map should not be persisted blindly.
let untrusted_state_before = response
.room_state
.state
.iter()
.stream()
.wide_filter_map(async |pdu| {
let (event_room_id, event_id, value) = self
.services
.event_handler
.parse_incoming_pdu(pdu, Some(room_version_rules))
.await
.inspect_err(|e| warn!("Invalid PDU in room state (dropping): {e:?}"))
.ok()?;
if event_room_id != room_id {
warn!(%event_id, expected=%room_id, actual=%event_room_id, "PDU in room state belongs to a different room (dropping)");
return None;
}
// value.insert("event_id".to_owned(), event_id.to_string().into());
if self.services.pdu_metadata.is_event_rejected(&event_id).await {
// Rejection will be re-evaluated later
trace!(%event_id, "Un-rejecting event");
self.services.pdu_metadata.unmark_event_rejected(&event_id);
}
Some((state_key_from_json(&value)?, value))
})
.collect::<HashMap<(StateEventType, StateKey), CanonicalJsonObject>>()
.await;
let create_event_json = untrusted_state_before
.get(&StateEventType::RoomCreate.with_state_key(""))
.ok_or_else(|| {
err!("Room state returned from send_join did not contain a room create event")
})?;
let create_event_id = gen_event_id(create_event_json, room_version_rules)
.map_err(|e| err!("Failed to generate event ID for room create event: {e:?}"))?;
let create_event = PduEvent::from_id_val(&create_event_id, create_event_json.clone())?;
Ok((create_event, untrusted_state_before))
}
async fn process_send_join_auth_chain(
&self,
response: &create_join_event::v2::Response,
room_id: &RoomId,
room_version_rules: &RoomVersionRules,
create_event: &PduEvent,
) -> Result<BTreeMap<OwnedEventId, PduEvent>> {
let auth_chain_timing_start = Instant::now();
let unauthed_auth_chain = response
.room_state
.auth_chain
.iter()
.stream()
.broad_filter_map(|value| async move {
self
.services
.event_handler
.parse_incoming_pdu(
&to_raw_value(&value)
.expect("CanonicalJsonObject just convert to RawJsonValue"),
Some(room_version_rules),
)
.await
.inspect_err(|e| warn!("Invalid PDU in send_join auth chain (dropping): {e:?}"))
.ok()
})
.broad_filter_map(|(auth_room_id, event_id, value)| async move {
if self.services.pdu_metadata.is_event_rejected(&event_id).await {
// Rejection will be re-evaluated later
trace!(%event_id, "Un-rejecting event");
self.services.pdu_metadata.unmark_event_rejected(&event_id);
}
trace!(%event_id, "Validating event");
if auth_room_id != room_id {
warn!(%event_id, %room_id, %auth_room_id, "PDU in send_join auth chain belongs to a different room (dropping)");
return None;
}
crate::rooms::event_handler::Service::pdu_format_check_1(&value, room_version_rules, create_event.event_id()).inspect_err(|e| {
warn!(%event_id, "PDU in send_join auth chain failed format check (dropping): {e:?}");
}).ok()?;
let mut value = self.services.event_handler.signature_hash_check_2_3(value, room_version_rules).await.inspect_err(|e| {
warn!(%event_id, "PDU in send_join auth chain failed signature check (dropping): {e:?}");
}).ok()?;
value.insert("event_id".to_owned(), event_id.as_str().into());
Some((event_id, value))
})
.collect::<HashMap<_, _>>()
.await;
debug!(
elapsed=?auth_chain_timing_start.elapsed(),
"Finished validating auth chain ({}/{} events passed validation)",
unauthed_auth_chain.len(),
response.room_state.auth_chain.len()
);
let auth_chain = build_local_dag(&unauthed_auth_chain, DagBuilderTree::AuthEvents)
.await?
.into_iter()
.stream()
.wide_filter_map(async |event_id| {
// Perform PDU check 4 to make sure the event doesn't need
// rejecting
let pdu =
PduEvent::from_id_val(&event_id, unauthed_auth_chain[&event_id].clone())
.expect("We already validated auth chian PDUs");
let auth_state = pdu
.auth_events()
.filter_map(|event_id| {
PduEvent::from_id_val(
event_id,
unauthed_auth_chain.get(event_id)?.clone(),
)
.map(|pdu| {
Some((
pdu.kind().with_state_key(
pdu.state_key()
.expect("Auth chain events must have a state key"),
),
pdu,
))
})
.ok()?
})
.collect::<HashMap<_, _>>();
self.services
.event_handler
.auth_state_check_4(
&pdu,
room_version_rules,
create_event.as_pdu(),
&auth_state,
)
.await
.inspect(|result| {
if !*result {
// NOTE: usually we don't *drop* events that fail PDU check 4, but
// send_join handling is special. Instead of rejecting this event and
// consequently every other event that depends on it, we simply drop
// it from the chain
warn!(%event_id, "Auth chain event failed self-authorization (dropping)");
}
})
.inspect_err(
|e| error!(%event_id, ?e, "Failed to run auth check on auth chain event"),
)
.unwrap_or_default()
.then_some((event_id, pdu))
})
.collect::<BTreeMap<_, _>>()
.await;
debug!(
elapsed=?auth_chain_timing_start.elapsed(),
"Finished authentication auth chain ({}/{} events passed authentication)",
auth_chain.len(),
unauthed_auth_chain.len()
);
// Now we need to persist them
auth_chain
.keys()
.stream()
.for_each_concurrent(automatic_width(), async |event_id| {
if self.services.timeline.get_pdu(event_id).await.is_err() {
// Only add events that we don't already have
self.services
.outlier
.add_pdu_outlier(event_id, &unauthed_auth_chain[event_id]);
}
})
.await;
info!(elapsed=?auth_chain_timing_start.elapsed(), "Finished processing authentication events");
Ok(auth_chain)
}
async fn process_send_join_state(
&self,
untrusted_state_before: HashMap<OwnedEventId, &CanonicalJsonObject>,
auth_chain: &BTreeMap<OwnedEventId, PduEvent>,
room_version_rules: &RoomVersionRules,
create_event: &PduEvent,
) -> Result<HashMap<StateTypeKey, PduEvent>> {
let state_timing_start = Instant::now();
let state_size = untrusted_state_before.len();
// NOTE: unlike the auth chain, we need to run PDU checks 1 through 4.
let state_before = untrusted_state_before
.into_iter()
.stream()
.broad_filter_map(|(event_id, value)| async move {
crate::rooms::event_handler::Service::pdu_format_check_1(
value,
room_version_rules,
create_event.event_id(),
)
.inspect_err(|e| {
warn!(%event_id, "PDU in send_join state failed format check (dropping): {e:?}");
})
.ok()?;
let mut value = self
.services
.event_handler
.signature_hash_check_2_3(value.clone(), room_version_rules)
.await
.inspect_err(|e| {
warn!(%event_id, "PDU in send_join state failed signature check (dropping): {e:?}");
})
.ok()?;
value.insert("event_id".to_owned(), event_id.as_str().into());
let pdu = PduEvent::from_id_val(&event_id, value.clone())
.expect("We already validated this state event");
let auth_state = pdu
.auth_events()
.filter_map(|event_id| {
auth_chain.get(event_id).map(|p| {
(
p.kind().with_state_key(
p.state_key()
.expect("auth chain events must have a state key"),
),
p.clone(),
)
})
})
.collect::<HashMap<_, _>>();
self.services
.event_handler
.auth_state_check_4(
&pdu,
room_version_rules,
create_event.as_pdu(),
&auth_state,
)
.await
.inspect(|result| {
if !*result {
warn!(%event_id, "State event failed self-authorization (dropping)");
}
})
.inspect_err(
|e| error!(%event_id, ?e, "Failed to run auth check on state event"),
)
.unwrap_or_default()
.then_some((pdu, value))
})
.collect::<Vec<_>>()
.await;
debug!(
elapsed=?state_timing_start.elapsed(),
"Finished validation and authentication of room state ({}/{} events retained)",
state_before.len(),
state_size,
);
state_before
.iter()
.stream()
.for_each_concurrent(automatic_width(), async |(pdu, pdu_json)| {
if self
.services
.timeline
.get_pdu(pdu.event_id())
.await
.is_err()
{
// Only add events that we don't already have
self.services
.outlier
.add_pdu_outlier(pdu.event_id(), pdu_json);
}
})
.await;
info!(
elapsed=?state_timing_start.elapsed(),
"Finished processing send join state ({}/{} events)",
state_before.len(),
state_size,
);
let state_before_by_key = state_before
.into_iter()
.map(|(pdu, _)| {
(
pdu.kind().with_state_key(
pdu.state_key().expect("state event must have a state key"),
),
pdu,
)
})
.collect::<HashMap<_, _>>();
Ok(state_before_by_key)
}
async fn process_send_join_membership_event(
&self,
membership_event: &serde_json::value::RawValue,
event_id: &EventId,
state_before: &HashMap<StateTypeKey, PduEvent>,
state_before_by_id: &HashMap<OwnedEventId, &PduEvent>,
room_version_rules: &RoomVersionRules,
) -> Result<(PduEvent, CanonicalJsonObject)> {
let (_, calculated_event_id, value) = self
.services
.event_handler
.parse_incoming_pdu(membership_event, Some(room_version_rules))
.await?;
if calculated_event_id != event_id {
return Err!(Request(InvalidParam(debug_warn!(
expected=%event_id,
actual=%calculated_event_id,
"Remote server returned a different membership event to the one we sent it"
))));
}
if self.services.pdu_metadata.is_event_rejected(event_id).await {
// Rejection will be re-evaluated later
trace!(%event_id, "Un-rejecting event");
self.services.pdu_metadata.unmark_event_rejected(event_id);
}
let create_event = state_before
.get(&StateEventType::RoomCreate.with_state_key(""))
.expect("create event must be in state before");
crate::rooms::event_handler::Service::pdu_format_check_1(
&value,
room_version_rules,
create_event.event_id(),
)?;
let mut value = self
.services
.event_handler
.signature_hash_check_2_3(value, room_version_rules)
.await?;
value.insert("event_id".to_owned(), event_id.as_str().into());
let pdu = PduEvent::from_id_val(event_id, value.clone())
.expect("We already validated this state event");
// Perform check 4 to make sure the PDU is self-authorised
let auth_state = pdu
.auth_events()
.filter_map(|event_id| {
state_before_by_id
.get(event_id)
.copied()
.map(|p| (p.kind().with_state_key(p.state_key().unwrap()), p.clone()))
})
.collect::<HashMap<_, _>>();
if !self
.services
.event_handler
.auth_state_check_4(&pdu, room_version_rules, create_event.as_pdu(), &auth_state)
.await?
{
return Err!(Request(Forbidden("Membership event was not self-authorised")));
}
// We can also perform check 5 (state before), since the state returned in the
// response is the state just before our join. However, state_before_check_5
// (part of the event_handler service) won't understand this, so we have to do
// the check manually.
let state_fetch =
|k: StateEventType, s: StateKey| ready(state_before.get(&(k, s)).cloned());
// PDU check: 5
let auth_check = state_res::event_auth::auth_check(
room_version_rules,
&pdu,
None, // TODO: third party invite
|ty, sk| state_fetch(ty.clone(), sk.into()),
create_event.as_pdu(),
)
.await
.map_err(|e| err!(Request(Forbidden("Auth check failed: {e:?}"))))?;
if !auth_check {
return Err!(Request(Forbidden(
"Membership event was not authorised based on the state before it"
)));
}
Ok((pdu, value))
}
}
+461 -99
View File
@@ -1,17 +1,26 @@
mod event;
mod join;
use std::{collections::VecDeque, sync::Arc};
use std::{collections::HashMap, sync::Arc};
use conduwuit::{
Err, Result, Server, debug, debug_info, debug_warn, info, is_true,
matrix::event::gen_event_id, pdu::PartialPdu, warn,
Err, Event, Pdu, Result, Server, debug, debug_info, debug_warn, err, error, info, is_true,
matrix::{
StateKey,
event::{gen_event_id, gen_event_id_canonical_json},
},
pdu::PartialPdu,
state_res, trace,
utils::{self, IterStream, ReadyExt, to_canonical_object},
warn,
};
use database::Database;
use futures::{FutureExt, StreamExt, join};
use futures::{FutureExt, StreamExt, TryFutureExt, join};
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId,
RoomVersionId, UserId,
api::{
error::{ErrorKind, IncompatibleRoomVersionErrorData},
federation,
},
canonical_json::to_canonical_value,
events::{
StateEventType, StaticEventContent,
room::{
@@ -25,13 +34,11 @@
use crate::{
Dep, antispam, globals,
rooms::{
event_handler,
membership::join::MakeJoinResult,
metadata, outlier, pdu_metadata, short,
event_handler, metadata, outlier, pdu_metadata, short,
state::{self, RoomMutexGuard},
state_accessor, state_cache,
state_compressor::{self},
timeline,
state_compressor::{self, CompressedState, HashSetCompressStateEvent},
timeline::{self, pdu_fits},
},
sending, server_keys, sync, users,
};
@@ -92,19 +99,18 @@ fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
}
impl Service {
/// Join a local user to a room. If the room is not local, this will attempt
/// to join via federation. If the room cannot be joined locally, a
/// federation join may be attempted. Users trying to join a room they're
/// already joined to will short-circuit.
/// Join a local user to a room.
pub async fn join_room(
&self,
sender_user: &UserId,
room_id: &RoomId,
reason: Option<String>,
servers: Vec<OwnedServerName>,
servers: &[OwnedServerName],
) -> Result<OwnedRoomId> {
assert!(self.services.globals.user_is_local(sender_user), "user should be local");
let state_lock = self.services.state.mutex.lock(room_id).await;
if self
.services
.state_cache
@@ -181,12 +187,12 @@ pub async fn join_room(
}
if server_in_room {
self.join_local_room(sender_user, room_id, reason, servers)
self.join_local_room(sender_user, room_id, reason, servers, state_lock)
.boxed()
.await?;
} else {
// Ask a remote server if we are not participating in this room
self.join_remote_room(sender_user, room_id, reason, servers)
self.join_remote_room(sender_user, room_id, reason, servers, state_lock)
.boxed()
.await?;
}
@@ -200,11 +206,11 @@ async fn join_local_room(
sender_user: &UserId,
room_id: &RoomId,
reason: Option<String>,
servers: Vec<OwnedServerName>,
servers: &[OwnedServerName],
state_lock: RoomMutexGuard,
) -> Result {
info!("Joining room locally");
let state_lock = self.services.state.mutex.lock(room_id).await;
let (room_version, join_rules, is_invited) = join!(
self.services.state.get_room_version(room_id),
self.services.state_accessor.get_join_rules(room_id),
@@ -258,7 +264,6 @@ async fn join_local_room(
info!("Joined room locally");
return Ok(());
};
drop(state_lock);
if servers.is_empty()
|| servers.len() == 1 && self.services.globals.server_is_ours(&servers[0])
@@ -281,7 +286,7 @@ async fn join_local_room(
remote_servers = %servers.len(),
"Could not join room locally, attempting remote join",
);
Box::pin(self.join_remote_room(sender_user, room_id, reason, servers)).await
Box::pin(self.join_remote_room(sender_user, room_id, reason, servers, state_lock)).await
}
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_remote_room", level = "info")]
@@ -290,83 +295,104 @@ pub async fn join_remote_room(
sender_user: &UserId,
room_id: &RoomId,
reason: Option<String>,
vias: Vec<OwnedServerName>,
servers: &[OwnedServerName],
state_lock: RoomMutexGuard,
) -> Result {
// public so the admin command force-join-room-remotely works
info!("Joining {room_id} over federation.");
// Treat the via list as a "priority queue", and each time a make_join fails,
// move the server to the back of the queue. This way, if a server is down, we
// will eventually try all servers in the list.
let mut priority_queue = VecDeque::from(vias.clone());
let mut template: Option<CanonicalJsonObject> = None;
let mut room_version: Option<RoomVersionId> = None;
let via_count = vias.len();
for via in vias {
let response = self.make_join(room_id, sender_user, &via).await;
match response {
| MakeJoinResult::Success((t, r)) => {
template = Some(t);
room_version = Some(r);
break;
},
| MakeJoinResult::Fatal(e) => {
return Err(e);
},
| MakeJoinResult::Retry => {
// Pop the front to remove this server from the queue, and
// then push it to the back.
// We need to do this vec iter + dequeue to avoid infinite
// looping when we reprioritise.
let _ = priority_queue.pop_front();
priority_queue.push_back(via);
},
}
}
if template.is_none() {
info!("All {} servers were unable to assist in joining {room_id} :(", via_count);
return Err!(BadServerResponse("No server available to assist in joining."));
}
let room_version =
room_version.expect("room_version cannot be None while template is Some");
let (make_join_response, remote_server) = self
.make_join_request(sender_user, room_id, servers)
.await?;
info!("make_join finished");
let room_version = make_join_response.room_version.unwrap_or(RoomVersionId::V1);
let room_version_rules = room_version
.rules()
.expect("room version should have defined rules");
if !self.services.server.supported_room_version(&room_version) {
// How did we get here?
return Err!(BadServerResponse(
"Remote room version {room_version} is not supported"
));
}
let room_version_rules = room_version
.rules()
.expect("room version should have defined rules");
let mut template = self
.seed_local_membership_pdu(
room_id,
sender_user,
MembershipState::Join,
reason,
template.unwrap(),
&room_version_rules,
)
.await?;
let mut join_event_stub: CanonicalJsonObject =
serde_json::from_str(make_join_response.event.get()).map_err(|e| {
err!(BadServerResponse(warn!(
"Invalid make_join event json received from server: {e:?}"
)))
})?;
let join_authorized_via_users_server = {
if room_version_rules
.signatures
.check_join_authorised_via_users_server
{
join_event_stub
.get("content")
.map(|s| {
s.as_object()?
.get("join_authorised_via_users_server")?
.as_str()
})
.and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok())
} else {
None
}
};
join_event_stub.insert(
"origin_server_ts".to_owned(),
CanonicalJsonValue::Integer(
utils::millis_since_unix_epoch()
.try_into()
.expect("Timestamp is valid js_int value"),
),
);
let mut join_content = RoomMemberEventContent::new(MembershipState::Join);
join_content.displayname = self.services.users.displayname(sender_user).await.ok();
join_content.avatar_url = self.services.users.avatar_url(sender_user).await.ok();
join_content.reason = reason;
join_content
.join_authorized_via_users_server
.clone_from(&join_authorized_via_users_server);
join_event_stub.insert(
"content".to_owned(),
to_canonical_value(join_content).expect("event is valid, we just created it"),
);
// Remove event id if it exists
join_event_stub.remove("event_id");
// In order to create a compatible ref hash (EventID) the `hashes` field needs
// to be present
self.services
.server_keys
.hash_and_sign_event(&mut template, &room_version_rules)?;
.hash_and_sign_event(&mut join_event_stub, &room_version_rules)?;
// Generate event id
let event_id = gen_event_id(&template, &room_version_rules)?;
let event_id = gen_event_id(&join_event_stub, &room_version_rules)?;
// Add event_id back
template
join_event_stub
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
// It has enough fields to be called a proper event now
let mut join_event = join_event_stub;
let send_join_request = federation::membership::create_join_event::v2::Request::new(
room_id.to_owned(),
event_id.clone(),
self.services
.sending
.convert_to_outgoing_federation_event(join_event.clone())
.await,
);
// NOTE: send_join can take a long time to respond, but from the point of view
// of other servers, we may already have finished joining. This means they
// sometimes end up sending PDUs to us that we aren't yet ready to accept, and
@@ -378,32 +404,368 @@ pub async fn join_remote_room(
.mutex_federation
.lock(room_id.as_str())
.await;
let state_lock = self.services.state.mutex.lock(room_id).await;
while let Some(via) = priority_queue.pop_front() {
let cork = self.services.db.cork_and_sync();
let result = self
.send_join(
room_id.to_owned(),
event_id.clone(),
&template,
&via,
&room_version_rules,
&state_lock,
)
.await?;
if result.is_none() {
continue;
info!("Asking {remote_server} for send_join in room {room_id}");
let send_join_response = match self
.services
.sending
.send_slow_federation_request(&remote_server, send_join_request)
.await
{
| Ok(response) => response,
| Err(e) => {
error!("send_join failed: {e}");
return Err(e);
},
};
info!("send_join finished");
if join_authorized_via_users_server.is_some() {
if let Some(signed_raw) = &send_join_response.room_state.event {
debug_info!(
"There is a signed event with join_authorized_via_users_server. This room \
is probably using restricted joins. Adding signature to our event"
);
let (signed_event_id, signed_value) =
gen_event_id_canonical_json(signed_raw, &room_version_rules).map_err(
|e| {
err!(Request(BadJson(warn!(
"Could not convert event to canonical JSON: {e}"
))))
},
)?;
if signed_event_id != event_id {
return Err!(Request(BadJson(warn!(
%signed_event_id, %event_id,
"Server {remote_server} sent event with wrong event ID"
))));
}
match signed_value["signatures"]
.as_object()
.ok_or_else(|| {
err!(BadServerResponse(warn!(
"Server {remote_server} sent invalid signatures type"
)))
})
.and_then(|e| {
e.get(remote_server.as_str()).ok_or_else(|| {
err!(BadServerResponse(warn!(
"Server {remote_server} did not send its signature for a \
restricted room"
)))
})
}) {
| Ok(signature) => {
join_event
.get_mut("signatures")
.expect("we created a valid pdu")
.as_object_mut()
.expect("we created a valid pdu")
.insert(remote_server.to_string(), signature.clone());
},
| Err(e) => {
warn!(
"Server {remote_server} sent invalid signature in send_join \
signatures for event {signed_value:?}: {e:?}",
);
},
}
}
info!("send_join finished");
drop(cork);
self.services.sync.wake_all_joined(room_id).await;
return Ok(());
}
info!(
"Despite producing a membership template, no server was capable of actually \
completing the join for us."
);
self.services.short.get_or_create_shortroomid(room_id).await;
info!("Parsing join event");
let parsed_join_pdu = Pdu::from_id_val(&event_id, join_event.clone())
.map_err(|e| err!(BadServerResponse("Invalid join event PDU: {e:?}")))?;
info!("Acquiring server signing keys for response events");
let resp_events = &send_join_response.room_state;
let resp_state = &resp_events.state;
let resp_auth = &resp_events.auth_chain;
self.services
.server_keys
.acquire_events_pubkeys(resp_auth.iter().chain(resp_state.iter()))
.await;
info!("Going through send_join response room_state");
let cork = self.services.db.cork_and_flush();
let state = send_join_response
.room_state
.state
.iter()
.stream()
.then(|pdu| {
self.services
.server_keys
.validate_and_add_event_id_no_fetch(pdu, &room_version_rules)
.inspect_err(|e| {
debug_warn!(
"Could not validate send_join response room_state event: {e:?}"
);
})
.inspect(|_| {
debug!("Completed validating send_join response room_state event");
})
})
.ready_filter_map(Result::ok)
.fold(HashMap::new(), |mut state, (event_id, value)| async move {
let pdu = match Pdu::from_id_val(&event_id, value.clone()) {
| Ok(pdu) => pdu,
| Err(e) => {
debug_warn!("Invalid PDU in send_join response: {e:?}: {value:#?}");
return state;
},
};
if !pdu_fits(&value.clone()) {
warn!(
"dropping incoming PDU {event_id} in room {room_id} from room join \
because it exceeds 65535 bytes or is otherwise too large."
);
return state;
}
self.services.outlier.add_pdu_outlier(&event_id, &value);
self.services.pdu_metadata.clear_pdu_markers(&event_id);
if let Some(state_key) = &pdu.state_key {
let shortstatekey = self
.services
.short
.get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)
.await;
state.insert(shortstatekey, pdu.event_id.clone());
}
state
})
.await;
drop(cork);
info!("Going through send_join response auth_chain");
let cork = self.services.db.cork_and_flush();
send_join_response
.room_state
.auth_chain
.iter()
.stream()
.then(|pdu| {
self.services
.server_keys
.validate_and_add_event_id_no_fetch(pdu, &room_version_rules)
})
.ready_filter_map(Result::ok)
.ready_for_each(|(event_id, value)| {
trace!(%event_id, "Adding PDU as an outlier from send_join auth_chain");
self.services.outlier.add_pdu_outlier(&event_id, &value);
self.services.pdu_metadata.clear_pdu_markers(&event_id);
})
.await;
drop(cork);
debug!("Running send_join auth check");
let fetch_state = &state;
let state_fetch = |k: StateEventType, s: StateKey| async move {
let shortstatekey = self.services.short.get_shortstatekey(&k, &s).await.ok()?;
let event_id = fetch_state.get(&shortstatekey)?;
self.services.timeline.get_pdu(event_id).await.ok()
};
let auth_check = state_res::event_auth::auth_check(
&room_version.rules().unwrap(),
&parsed_join_pdu,
None, // TODO: third party invite
|k, s| state_fetch(k.clone(), s.into()),
&state_fetch(StateEventType::RoomCreate, "".into())
.await
.expect("create event is missing from send_join auth"),
)
.await
.map_err(|e| err!(Request(Forbidden(warn!("Auth check failed: {e:?}")))))?;
if !auth_check {
return Err!(Request(Forbidden("Auth check failed")));
}
let resident_before = self
.services
.state_cache
.server_in_room(self.services.globals.server_name(), room_id)
.await;
let cork = self.services.db.cork_and_flush();
info!("Compressing state from send_join");
let compressed: CompressedState = self
.services
.state_compressor
.compress_state_events(state.iter().map(|(ssk, eid)| (ssk, eid.as_ref())))
.collect()
.await;
debug!("Saving compressed state");
let HashSetCompressStateEvent {
shortstatehash: statehash_before_join,
added,
removed,
} = self
.services
.state_compressor
.save_state(room_id, Arc::new(compressed))
.await?;
debug!("Forcing state for new room");
self.services
.state
.force_state(room_id, statehash_before_join, added, removed, &state_lock)
.await?;
debug!("Updating joined counts for new room");
self.services.state_cache.update_joined_count(room_id).await;
// We append to state before appending the pdu, so we don't have a moment in
// time with the pdu without it's state. This is okay because append_pdu can't
// fail.
let statehash_after_join = self
.services
.state
.append_to_state(&parsed_join_pdu, room_id)
.await?;
info!("Appending new room join event");
self.services
.timeline
.append_pdu(
&parsed_join_pdu,
join_event,
std::iter::once(parsed_join_pdu.event_id.as_ref()),
&state_lock,
room_id,
)
.await?;
self.services
.metadata
.maybe_set_mindepth(room_id, parsed_join_pdu.depth.into())
.await;
info!("Setting final room state for new room");
// We set the room state after inserting the pdu, so that we never have a moment
// in time where events in the current room state do not exist
self.services
.state
.set_room_state(room_id, statehash_after_join, &state_lock);
if !resident_before {
// NOTE: We replace local extremities for this room if we were not a resident
// before. We might be doing a remote join to satisfy restricted join rules,
// so we don't want to do this if we're already a resident. Otherwise, we
// want to replace our forward extremities whole-sale in case we were
// desynced.
info!("Replacing local forward extremities");
self.services
.state
.set_forward_extremities(
room_id,
std::iter::once(parsed_join_pdu.event_id()),
&state_lock,
)
.await;
}
drop(cork);
self.services.sync.wake_all_joined(room_id).await;
Ok(())
}
async fn make_join_request(
&self,
sender_user: &UserId,
room_id: &RoomId,
servers: &[OwnedServerName],
) -> Result<(federation::membership::prepare_join_event::v1::Response, OwnedServerName)> {
let mut make_join_counter: usize = 1;
for remote_server in servers {
if self.services.globals.server_is_ours(remote_server) {
continue;
}
info!(
"Asking {remote_server} for make_join (attempt {make_join_counter}/{})",
servers.len()
);
let mut request = federation::membership::prepare_join_event::v1::Request::new(
room_id.to_owned(),
sender_user.to_owned(),
);
request.ver = self.services.server.supported_room_versions().collect();
let make_join_response = self
.services
.sending
.send_federation_request(remote_server, request)
.await;
trace!("make_join response: {:?}", make_join_response);
make_join_counter = make_join_counter.saturating_add(1);
match make_join_response {
| Ok(response) => {
info!("Received make_join response from {remote_server}");
if let Err(e) = validate_remote_member_event_stub(
&MembershipState::Join,
sender_user,
room_id,
&to_canonical_object(&response.event)?,
) {
warn!("make_join response from {remote_server} failed validation: {e}");
continue;
}
return Ok((response, remote_server.clone()));
},
| Err(e) => match e.kind() {
| ErrorKind::UnableToAuthorizeJoin => {
info!(
"{remote_server} was unable to verify the joining user satisfied \
restricted join requirements: {e}. Will continue trying."
);
},
| ErrorKind::UnableToGrantJoin => {
info!(
"{remote_server} believes the joining user satisfies restricted \
join rules, but is unable to authorise a join for us. Will \
continue trying."
);
},
| ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData {
room_version,
..
}) => {
warn!(
"{remote_server} reports the room we are trying to join is \
v{room_version}, which we do not support."
);
return Err(e);
},
| ErrorKind::Forbidden => {
warn!("{remote_server} refuses to let us join: {e}.");
return Err(e);
},
| ErrorKind::NotFound => {
info!(
"{remote_server} does not know about {room_id}: {e}. Will continue \
trying."
);
},
| _ => {
info!("{remote_server} failed to make_join: {e}. Will continue trying.");
},
},
}
}
info!("All {} servers were unable to assist in joining {room_id} :(", servers.len());
Err!(BadServerResponse("No server available to assist in joining."))
}
-4
View File
@@ -45,10 +45,6 @@ pub async fn get_pdu_outlier(&self, event_id: &EventId) -> Result<PduEvent> {
/// Append the PDU as an outlier.
pub fn add_pdu_outlier(&self, event_id: &EventId, pdu: &CanonicalJsonObject) {
debug_assert!(
pdu.get("event_id").is_some(),
"event_id must be present in database events"
);
self.db.eventid_outlierpdu.raw_put(event_id, Json(pdu));
}
}
+2 -18
View File
@@ -1,7 +1,7 @@
use std::sync::Arc;
use conduwuit::{
Err, Event, PduCount, PduEvent, Result, at, err,
Err, PduCount, PduEvent, Result, at, err,
result::NotFound,
utils::{self, stream::TryReadyExt},
};
@@ -176,15 +176,6 @@ pub(super) async fn append_pdu(
count: PduCount,
) {
debug_assert!(matches!(count, PduCount::Normal(_)), "PduCount not Normal");
debug_assert!(
json.get("event_id").is_some(),
"event_id must be present in database events"
);
debug_assert_eq!(
json.get("event_id").unwrap().as_str().unwrap(),
pdu.event_id().as_str(),
"event ID mismatch between PDU JSON and PDU object"
);
self.pduid_pdu.raw_put(pdu_id, Json(json));
self.eventid_pduid.insert(pdu.event_id.as_bytes(), pdu_id);
@@ -197,10 +188,6 @@ pub(super) fn prepend_backfill_pdu(
event_id: &EventId,
json: &CanonicalJsonObject,
) {
debug_assert!(
json.get("event_id").is_some(),
"event_id must be present in database events"
);
self.pduid_pdu.raw_put(pdu_id, Json(json));
self.eventid_pduid.insert(event_id, pdu_id);
self.eventid_outlierpdu.remove(event_id);
@@ -215,10 +202,7 @@ pub(super) async fn replace_pdu(
if self.pduid_pdu.get(pdu_id).await.is_not_found() {
return Err!(Request(NotFound("PDU does not exist.")));
}
debug_assert!(
pdu_json.get("event_id").is_some(),
"event_id must be present in database events"
);
self.pduid_pdu.raw_put(pdu_id, Json(pdu_json));
Ok(())
-6
View File
@@ -18,12 +18,6 @@ pub fn hash_and_sign_event(
object: &mut CanonicalJsonObject,
room_version_rules: &RoomVersionRules,
) -> Result {
#[cfg(debug_assertions)]
{
if object.contains_key("event_id") {
conduwuit::warn!("Signing a PDU that has an `event_id` present");
}
}
hash_and_sign_event(
self.services.globals.server_name().as_str(),
self.keypair(),
+1 -1
View File
@@ -244,7 +244,7 @@ pub async fn create_local_account(
user_id,
&room_id,
Some("Automatically joining this room upon registration".to_owned()),
vec![
&[
self.services.globals.server_name().to_owned(),
room_server_name.to_owned(),
],
-1
View File
@@ -41,7 +41,6 @@ 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,
+38 -29
View File
@@ -11,20 +11,44 @@
use futures::{Stream, StreamExt};
use ruma::{
DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, UserId,
api::client::device::Device, events::AnyToDeviceEvent, serde::Raw, uint,
api::client::device::Device, assign, 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: &str,
token_max_age: Option<Duration>,
token: Option<DeviceToken>,
initial_device_display_name: Option<String>,
client_ip: Option<String>,
) -> Result<()> {
@@ -38,8 +62,12 @@ pub async fn create_device(
increment(&self.db.userid_devicelistversion, user_id.as_bytes());
self.db.userdeviceid_metadata.put(key, Json(device));
self.set_token(user_id, device_id, token, token_max_age)
.await
if let Some(token) = token {
self.set_token(user_id, device_id, token).await?;
}
Ok(())
}
/// Removes a device from a user.
@@ -97,31 +125,12 @@ 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,
token: &str,
token_max_age: Option<Duration>,
DeviceToken { token, max_age }: DeviceToken,
) -> Result<()> {
let key = (user_id, device_id);
if self.db.userdeviceid_metadata.qry(&key).await.is_err() {
@@ -136,7 +145,7 @@ pub async fn set_token(
if self
.services
.appservice
.find_from_token(token)
.find_from_token(&token)
.await
.is_ok()
{
@@ -153,10 +162,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) = token_max_age {
if let Some(max_age) = max_age {
let expires = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("system time should not be before the epoch")
+1
View File
@@ -14,6 +14,7 @@
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};
+1 -1
View File
@@ -155,7 +155,7 @@ async fn get_account_deeplink(
));
};
format!("device/{device_id}/delete")
format!("device/{device_id}/remove")
},
| AccountManagementAction::DeviceView => {
let Some(device_id) = query.device_id else {