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
Ginger 9a94b93ddd fix: Fix new database migrations running on every server startup 2026-07-12 12:31:12 -04:00
Gingerandtimedout 0ac0bd93ae fix: Default deserialize for compatibility_mode 2026-07-12 16:28:27 +01:00
Gingerandtimedout fe9f718c6e fix: Stop using the federation DNS resolver for non-federation requests 2026-07-12 16:11:00 +01:00
nexandEllis Git e333f5a8e5 chore: announce 2026-07-12 04:14:23 +00:00
Ginger b8c64a68d4 chore: Release 2026-07-12 00:09:26 -04:00
Ginger 163f049757 chore: Update changelog 2026-07-12 00:07:20 -04:00
Ginger f48125c37a fix: Remove dependency on aws-lc-rs 2026-07-11 22:48:30 -04:00
timedoutandEllis Git 4001b99261 style: Make send_join's docstring more useful 2026-07-12 02:11:50 +00:00
timedoutandEllis Git b6e5a106ff fix: Don't unhelpfully panic when encountering an unrecognised room version 2026-07-12 02:11:50 +00:00
timedoutandEllis Git e1eea45fd6 style: Fix EOL 2026-07-12 02:11:50 +00:00
timedoutandEllis Git 7cb53f41d6 chore: Add newsfrag 2026-07-12 02:11:50 +00:00
timedoutandEllis Git fbf81fcdeb fix: Opportunistically re-use room format rules when parsing incoming PDUs 2026-07-12 02:11:50 +00:00
timedoutandEllis Git 87030a3a22 fix: Ensure PDU returned by create_hash_and_sign_event is itself signed 2026-07-12 02:11:50 +00:00
timedoutandEllis Git 4f509fa113 fix: Convert room summary to federation format when sending invites 2026-07-12 02:11:50 +00:00
timedoutandEllis Git aa7ed885c0 fix: Check correct field name when determining event type 2026-07-12 02:11:50 +00:00
timedoutandEllis Git d5e6e617d1 fix: Remove redundant banned room server checks
These don't make sense here anyway
2026-07-12 02:11:50 +00:00
timedoutandEllis Git 1f7680ad5f feat: Give send_join the invite treatment 2026-07-12 02:11:50 +00:00
timedoutandEllis Git 0c37a542d4 feat: Give send_leave the invite treatment 2026-07-12 02:11:50 +00:00
timedoutandEllis Git efd07da0d7 feat: Give send_knock the invite treatment 2026-07-12 02:11:50 +00:00
timedoutandEllis Git f0590e882a fix: Only run ACL checks when we know we have live state
Prevents a potential bug where we might inadvertently reject valid invites because we have a stale state cache that blocks the sender or even ourselves

Also fixes the banned remote server room check by using the create event instead of room ID
2026-07-12 02:11:50 +00:00
timedoutandEllis Git 7225bf25ae feat: Persist create events received during the invite process 2026-07-12 02:11:50 +00:00
timedoutandEllis Git e52bb5db69 style: Move invite validation into own functions 2026-07-12 02:11:50 +00:00
timedoutandEllis Git 995726923b feat: Enforce new federation invite checks
These were introduced in spec v1.16 however we didn't implement them until now for compatibility.
2026-07-12 02:11:50 +00:00
GingerandEllis Git 556141b404 fix: Trim whitespace from OIDC client secret file 2026-07-12 02:06:48 +00:00
62 changed files with 1007 additions and 938 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ runs:
- name: Install timelord-cli and git-warp-time - name: Install timelord-cli and git-warp-time
if: steps.check-binaries.outputs.need-install == 'true' 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: with:
tool: git-warp-time,timelord-cli@3.0.1 tool: git-warp-time,timelord-cli@3.0.1
+1 -1
View File
@@ -65,7 +65,7 @@ jobs:
path: binaries path: binaries
merge-multiple: true merge-multiple: true
- name: Create Release and Upload - 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: with:
draft: true draft: true
files: binaries/* files: binaries/*
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
- name: Setup Node.js - name: Setup Node.js
if: steps.runner-env.outputs.node_major == '' || steps.runner-env.outputs.node_major < '20' 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: with:
node-version: 22 node-version: 22
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
steps: steps:
- name: 📦 Setup Node.js - name: 📦 Setup Node.js
uses: https://github.com/actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 uses: https://github.com/actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with: with:
node-version: "22" node-version: "22"
+1 -1
View File
@@ -218,7 +218,7 @@ jobs:
path: binaries path: binaries
merge-multiple: true merge-multiple: true
- name: Create Release and Upload - 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: with:
draft: true draft: true
files: binaries/* files: binaries/*
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
name: Renovate name: Renovate
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: 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 options: --tmpfs /tmp:exec
steps: steps:
- name: Checkout - name: Checkout
+24
View File
@@ -1,3 +1,27 @@
# Continuwuity 26.6.2 (2026-07-12)
## Bugfixes
- Fixed the server returning 500 errors if `admin_console_automatic` is enabled and no TTY is available. Contributed by @s1lv3r. (#1975)
- Fixed `global.oauth.compatibility_mode` being required, despite being ignored, when the `[global.oauth.oidc]` config section is provided.
- Fixed an issue with a migration that could cause user accounts imported from an identity provider to be marked as deactivated when the server started. If you have accounts affected by this issue, use `!admin users reset-password --convert-to-local-account` to reactivate them.
# Continuwuity 26.6.1 (2026-07-12)
## Features
- Added enforcement for new federated invite checks and corrected a bunch of related spec compliance issues along the way. Contributed by @nex. (#1952)
## Bugfixes
- Fixed existing accounts failing to link when logging in with OIDC if `prompt_for_localpart` was `false`. (#1942)
- Authentication is no longer required on the `/_matrix/client/v3/account/3pid/email/requestToken` endpoint. (#1953)
- Fixed newly created rooms failing to sync properly in clients using legacy sync.
- Stopped appservice users from being erroneously marked as deactivated during a 26.6 database migration.
- Whitespace will now automatically be trimmed from the start and end of the `global.oauth.oidc.client_secret_file`.
# Continuwuity 26.6.0 (2026-07-10) # Continuwuity 26.6.0 (2026-07-10)
## Features ## Features
+2 -2
View File
@@ -26,7 +26,7 @@ ### Pre-commit Checks
```bash ```bash
# Install prek using cargo-binstall # Install prek using cargo-binstall https://github.com/cargo-bins/cargo-binstall
cargo binstall prek cargo binstall prek
# Install git hooks to run checks automatically # Install git hooks to run checks automatically
@@ -155,7 +155,7 @@ ### Creating pull requests
Before submitting a pull request, please ensure: Before submitting a pull request, please ensure:
1. Your code passes all CI checks (formatting, linting, typo detection, etc.) 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 3. Your commit messages follow the conventional commits format
4. Tests are added for new functionality 4. Tests are added for new functionality
5. Documentation is updated if needed 5. Documentation is updated if needed
Generated
+137 -234
View File
@@ -259,9 +259,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]] [[package]]
name = "aws-lc-rs" name = "aws-lc-rs"
version = "1.17.0" version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad"
dependencies = [ dependencies = [
"aws-lc-sys", "aws-lc-sys",
"zeroize", "zeroize",
@@ -269,14 +269,15 @@ dependencies = [
[[package]] [[package]]
name = "aws-lc-sys" name = "aws-lc-sys"
version = "0.41.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444"
dependencies = [ dependencies = [
"cc", "cc",
"cmake", "cmake",
"dunce", "dunce",
"fs_extra", "fs_extra",
"pkg-config",
] ]
[[package]] [[package]]
@@ -557,9 +558,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.25.0" version = "1.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@@ -575,9 +576,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.12.0" version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]] [[package]]
name = "bytesize" name = "bytesize"
@@ -641,9 +642,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.65" version = "1.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@@ -826,7 +827,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit" name = "conduwuit"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"clap", "clap",
@@ -864,7 +865,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit_admin" name = "conduwuit_admin"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"assign", "assign",
"clap", "clap",
@@ -890,7 +891,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit_api" name = "conduwuit_api"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"assign", "assign",
"async-trait", "async-trait",
@@ -928,7 +929,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit_build_metadata" name = "conduwuit_build_metadata"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"built", "built",
"cargo_metadata", "cargo_metadata",
@@ -936,7 +937,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit_core" name = "conduwuit_core"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"argon2", "argon2",
"arrayvec", "arrayvec",
@@ -1004,7 +1005,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit_database" name = "conduwuit_database"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"async-channel", "async-channel",
"conduwuit_core", "conduwuit_core",
@@ -1025,7 +1026,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit_macros" name = "conduwuit_macros"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"cargo_toml", "cargo_toml",
"itertools 0.15.0", "itertools 0.15.0",
@@ -1036,7 +1037,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit_router" name = "conduwuit_router"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"assign", "assign",
"axum", "axum",
@@ -1074,7 +1075,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit_service" name = "conduwuit_service"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"askama", "askama",
"assign", "assign",
@@ -1126,7 +1127,7 @@ dependencies = [
[[package]] [[package]]
name = "conduwuit_web" name = "conduwuit_web"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"askama", "askama",
"assign", "assign",
@@ -1361,18 +1362,18 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.15" version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.6" version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
dependencies = [ dependencies = [
"crossbeam-epoch", "crossbeam-epoch",
"crossbeam-utils", "crossbeam-utils",
@@ -1380,27 +1381,27 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.9.18" version = "0.9.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-queue" name = "crossbeam-queue"
version = "0.3.12" version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.21" version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
[[package]] [[package]]
name = "crossterm" name = "crossterm"
@@ -1463,9 +1464,9 @@ dependencies = [
[[package]] [[package]]
name = "ctor" name = "ctor"
version = "1.0.7" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" checksum = "fb22e947478ccf9dc44d8922042c677a63fbb88f2cb468521d1145816e5087cb"
dependencies = [ dependencies = [
"link-section", "link-section",
"linktime-proc-macro", "linktime-proc-macro",
@@ -1513,38 +1514,14 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "817fa642fb0ee7fe42e95783e00e0969927b96091bdd4b9b1af082acd943913b" 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]] [[package]]
name = "darling" name = "darling"
version = "0.23.0" version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
dependencies = [ dependencies = [
"darling_core 0.23.0", "darling_core",
"darling_macro 0.23.0", "darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
] ]
[[package]] [[package]]
@@ -1560,24 +1537,13 @@ dependencies = [
"syn", "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]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.23.0" version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [ dependencies = [
"darling_core 0.23.0", "darling_core",
"quote", "quote",
"syn", "syn",
] ]
@@ -2122,9 +2088,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"r-efi 6.0.0", "r-efi 6.0.0",
"rand_core 0.10.1", "rand_core 0.10.1",
"wasm-bindgen",
] ]
[[package]] [[package]]
@@ -2290,7 +2258,7 @@ dependencies = [
"http", "http",
"httpdate", "httpdate",
"mime", "mime",
"sha1 0.10.6", "sha1 0.10.7",
] ]
[[package]] [[package]]
@@ -2475,9 +2443,9 @@ dependencies = [
[[package]] [[package]]
name = "http-body-util" name = "http-body-util"
version = "0.1.3" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
@@ -2906,11 +2874,11 @@ dependencies = [
[[package]] [[package]]
name = "jobserver" name = "jobserver"
version = "0.1.34" version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [ dependencies = [
"getrandom 0.3.4", "getrandom 0.4.3",
"libc", "libc",
] ]
@@ -3060,9 +3028,9 @@ dependencies = [
[[package]] [[package]]
name = "link-section" name = "link-section"
version = "0.18.3" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24670b639492630905459a6c7d47f063d33c2d4fcd5362f6e5827c5613976c9f" checksum = "e333fe507b738576d6da5bb3f1a7d7a1c80307ed9ef31624c057d844c19c93e9"
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
@@ -3199,19 +3167,20 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.8.2" version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]] [[package]]
name = "memory-serve" name = "memory-serve"
version = "2.1.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81b5bbad2035f57b1e95f66da606832edd935b47d82312e38e1ccffbcfb8a427" checksum = "18718e8f7eefb3194d346474ac95f50ada065ff043e0074e5b8e85b85148122f"
dependencies = [ dependencies = [
"axum", "axum",
"brotli", "brotli",
"flate2", "flate2",
"fs-err",
"mime_guess", "mime_guess",
"sha256", "sha256",
"tracing", "tracing",
@@ -3395,9 +3364,9 @@ dependencies = [
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c863e9ab5e7bf9c99ba75e1050f1e4d624ae87ed3532d6238ffbdc7b585dbbe6" checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [ dependencies = [
"num-integer", "num-integer",
"num-traits", "num-traits",
@@ -3414,7 +3383,7 @@ dependencies = [
"num-integer", "num-integer",
"num-iter", "num-iter",
"num-traits", "num-traits",
"rand 0.8.6", "rand 0.8.7",
"smallvec", "smallvec",
"zeroize", "zeroize",
] ]
@@ -3445,11 +3414,10 @@ dependencies = [
[[package]] [[package]]
name = "num-iter" name = "num-iter"
version = "0.1.45" version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
dependencies = [ dependencies = [
"autocfg",
"num-integer", "num-integer",
"num-traits", "num-traits",
] ]
@@ -3495,7 +3463,7 @@ dependencies = [
"chrono", "chrono",
"getrandom 0.2.17", "getrandom 0.2.17",
"http", "http",
"rand 0.8.6", "rand 0.8.7",
"reqwest 0.12.28", "reqwest 0.12.28",
"serde", "serde",
"serde_json", "serde_json",
@@ -3700,7 +3668,7 @@ dependencies = [
"oauth2", "oauth2",
"p256", "p256",
"p384", "p384",
"rand 0.8.6", "rand 0.8.7",
"rsa", "rsa",
"serde", "serde",
"serde-value", "serde-value",
@@ -3791,7 +3759,7 @@ dependencies = [
"opentelemetry", "opentelemetry",
"percent-encoding", "percent-encoding",
"portable-atomic", "portable-atomic",
"rand 0.9.4", "rand 0.9.5",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
@@ -3968,7 +3936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [ dependencies = [
"phf_shared", "phf_shared",
"rand 0.8.6", "rand 0.8.7",
] ]
[[package]] [[package]]
@@ -4112,22 +4080,22 @@ dependencies = [
] ]
[[package]] [[package]]
name = "proc-macro-error-attr2" name = "proc-macro-error-attr3"
version = "2.0.0" version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" checksum = "34e4dd828515431dd6c4a030d26f7eaed7dd4778226e9d2bb968d65ca4ec3d4d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
] ]
[[package]] [[package]]
name = "proc-macro-error2" name = "proc-macro-error3"
version = "2.0.1" version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" checksum = "5ee475e440453418ff1335189eddf7101ba502cd818ab7ae04209bc83aa925aa"
dependencies = [ dependencies = [
"proc-macro-error-attr2", "proc-macro-error-attr3",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@@ -4207,9 +4175,9 @@ checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
[[package]] [[package]]
name = "pxfm" name = "pxfm"
version = "0.1.29" version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
[[package]] [[package]]
name = "quick-error" name = "quick-error"
@@ -4240,15 +4208,16 @@ dependencies = [
[[package]] [[package]]
name = "quinn-proto" name = "quinn-proto"
version = "0.11.15" version = "0.11.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"bytes", "bytes",
"getrandom 0.3.4", "getrandom 0.4.3",
"lru-slab", "lru-slab",
"rand 0.9.4", "rand 0.10.2",
"rand_pcg",
"ring", "ring",
"rustc-hash", "rustc-hash",
"rustls", "rustls",
@@ -4262,16 +4231,16 @@ dependencies = [
[[package]] [[package]]
name = "quinn-udp" name = "quinn-udp"
version = "0.5.14" version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
dependencies = [ dependencies = [
"cfg_aliases", "cfg_aliases",
"libc", "libc",
"once_cell", "once_cell",
"socket2", "socket2",
"tracing", "tracing",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -4303,9 +4272,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.6" version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
dependencies = [ dependencies = [
"libc", "libc",
"rand_chacha 0.3.1", "rand_chacha 0.3.1",
@@ -4314,9 +4283,9 @@ dependencies = [
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.9.4" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
dependencies = [ dependencies = [
"rand_chacha 0.9.0", "rand_chacha 0.9.0",
"rand_core 0.9.5", "rand_core 0.9.5",
@@ -4377,6 +4346,15 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" 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]] [[package]]
name = "recaptcha-verify" name = "recaptcha-verify"
version = "0.2.0" version = "0.2.0"
@@ -4419,9 +4397,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.12.4" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -4431,9 +4409,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.14" version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -4539,9 +4517,9 @@ checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7"
[[package]] [[package]]
name = "resolvematrix" name = "resolvematrix"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a574e37f06e219dcfdb14a4f0abf56e806c4551e030aae53802d2c42b29aea33" checksum = "a68b9735d8c1096d8152d4dfb5a705c0135dd14d181542fd87301f8d3cb4ed03"
dependencies = [ dependencies = [
"futures", "futures",
"hickory-resolver", "hickory-resolver",
@@ -4799,7 +4777,7 @@ dependencies = [
[[package]] [[package]]
name = "ruminuwuity" name = "ruminuwuity"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"assign", "assign",
"ruma", "ruma",
@@ -4837,9 +4815,9 @@ dependencies = [
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.27" version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb"
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
@@ -4871,9 +4849,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.41" version = "0.23.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"log", "log",
@@ -4948,9 +4926,9 @@ dependencies = [
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.22" version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]] [[package]]
name = "rustyline-async" name = "rustyline-async"
@@ -5136,7 +5114,7 @@ version = "0.48.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8c44ee52fa3d30581f951b57aec754ef9cd70186df5fb788367804360c48097" checksum = "b8c44ee52fa3d30581f951b57aec754ef9cd70186df5fb788367804360c48097"
dependencies = [ dependencies = [
"rand 0.9.4", "rand 0.9.5",
"sentry-types", "sentry-types",
"serde", "serde",
"serde_json", "serde_json",
@@ -5209,7 +5187,7 @@ checksum = "1be93ef0435a5ab91f88178fb7681249437e875d004f996ecb35be94dc99a0e0"
dependencies = [ dependencies = [
"debugid", "debugid",
"hex", "hex",
"rand 0.9.4", "rand 0.9.5",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.18", "thiserror 2.0.18",
@@ -5389,7 +5367,7 @@ version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
dependencies = [ dependencies = [
"darling 0.23.0", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@@ -5397,9 +5375,9 @@ dependencies = [
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.10.6" version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.2.17", "cpufeatures 0.2.17",
@@ -5722,9 +5700,9 @@ dependencies = [
[[package]] [[package]]
name = "termimad" name = "termimad"
version = "0.35.0" version = "0.35.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "903906afabf58273a7a90b47e0e60701cbd01731ca72ba88508b38b5539647f4" checksum = "d53d4b1294b87e81925b7ae7f8f4d000376e3a5b3349d978429665428a793fcb"
dependencies = [ dependencies = [
"coolor", "coolor",
"crokey", "crokey",
@@ -5788,9 +5766,9 @@ dependencies = [
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.1.9" version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@@ -5865,9 +5843,9 @@ dependencies = [
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.11.0" version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f"
dependencies = [ dependencies = [
"tinyvec_macros", "tinyvec_macros",
] ]
@@ -6230,7 +6208,7 @@ dependencies = [
"futures", "futures",
"http", "http",
"parking_lot", "parking_lot",
"rand 0.9.4", "rand 0.9.5",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.18", "thiserror 2.0.18",
@@ -6444,9 +6422,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.23.4" version = "1.23.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" checksum = "ea5fab0d6c3c01ae70085a09cb03d4c7a1d6314e2b3e075392783396d724ca0a"
dependencies = [ dependencies = [
"getrandom 0.4.3", "getrandom 0.4.3",
"js-sys", "js-sys",
@@ -6472,13 +6450,12 @@ dependencies = [
[[package]] [[package]]
name = "validator_derive" name = "validator_derive"
version = "0.20.0" version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca" checksum = "240e4b81c20a1d6d50d1d7265c658dfbd204e8b9ac4d80f3c931f39462196335"
dependencies = [ dependencies = [
"darling 0.20.11", "darling",
"once_cell", "proc-macro-error3",
"proc-macro-error2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@@ -6779,16 +6756,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets 0.52.6", "windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
] ]
[[package]] [[package]]
@@ -6806,31 +6774,14 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.52.6", "windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.52.6", "windows_aarch64_msvc",
"windows_i686_gnu 0.52.6", "windows_i686_gnu",
"windows_i686_gnullvm 0.52.6", "windows_i686_gnullvm",
"windows_i686_msvc 0.52.6", "windows_i686_msvc",
"windows_x86_64_gnu 0.52.6", "windows_x86_64_gnu",
"windows_x86_64_gnullvm 0.52.6", "windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.52.6", "windows_x86_64_msvc",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
] ]
[[package]] [[package]]
@@ -6839,96 +6790,48 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 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]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 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]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 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]] [[package]]
name = "winnow" name = "winnow"
version = "0.7.15" version = "0.7.15"
@@ -6972,7 +6875,7 @@ dependencies = [
[[package]] [[package]]
name = "xtask" name = "xtask"
version = "26.6.0" version = "26.6.2"
dependencies = [ dependencies = [
"askama", "askama",
"cargo_metadata", "cargo_metadata",
@@ -7012,18 +6915,18 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.52" version = "0.8.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.52" version = "0.8.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -7092,9 +6995,9 @@ dependencies = [
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.21" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" checksum = "bd2f034a4bebf216c9e4b7083603e024cf930873fd67830cfb083c9fa33129d9"
[[package]] [[package]]
name = "zstd" name = "zstd"
+2 -8
View File
@@ -12,7 +12,7 @@ license = "Apache-2.0"
# See also `rust-toolchain.toml` # See also `rust-toolchain.toml`
readme = "README.md" readme = "README.md"
repository = "https://forgejo.ellis.link/continuwuation/continuwuity" repository = "https://forgejo.ellis.link/continuwuation/continuwuity"
version = "26.6.0" version = "26.6.2"
[workspace.metadata.crane] [workspace.metadata.crane]
name = "conduwuit" name = "conduwuit"
@@ -141,12 +141,6 @@ features = [
version = "0.23.25" version = "0.23.25"
default-features = false default-features = false
[workspace.dependencies.aws-lc-sys]
version = "0.41.0"
[workspace.dependencies.aws-lc-rs]
version = "1.17.0"
[workspace.dependencies.reqwest] [workspace.dependencies.reqwest]
version = "0.13.2" version = "0.13.2"
default-features = false default-features = false
@@ -570,7 +564,7 @@ features = ["std"]
version = "0.3.0" version = "0.3.0"
[workspace.dependencies.resolvematrix] [workspace.dependencies.resolvematrix]
version = "1.1.0" version = "1.2.0"
[workspace.dependencies.serde_urlencoded] [workspace.dependencies.serde_urlencoded]
version = "0.7.1" version = "0.7.1"
+1
View File
@@ -0,0 +1 @@
Appservices are now properly able to create devices for E2EE.
-1
View File
@@ -1 +0,0 @@
Stopped appservice users from being erroneously marked as deactivated during a 26.6 database migration.
+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
@@ -1 +0,0 @@
Fixed existing accounts failing to link when logging in with OIDC if `prompt_for_localpart` was `false`.
-1
View File
@@ -1 +0,0 @@
Authentication is no longer required on the `/_matrix/client/v3/account/3pid/email/requestToken` endpoint.
+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." "message": "Welcome to Continuwuity! Important announcements about the project will appear here."
}, },
{ {
"id": 15, "id": 16,
"mention_room": true, "mention_room": true,
"date": "2026-07-10", "date": "2026-07-13",
"message": "[Continuwuity 26.6.0](https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.6.0) is finally out! This is by far our largest release yet, and it wouldn't have been possible without our community. Thank you so much for all of your patience, support, and/or development time over the last three months <3" "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": { "advisory-db": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1781566179, "lastModified": 1783840254,
"narHash": "sha256-Tqv8I586fYzWpEW/Smq/JqESFa3DVVzVWsnAMtvhy/I=", "narHash": "sha256-XjyvZk0f3YiinVHmkGOotmLBAzvK+LwEJYj2QqJ5pn8=",
"owner": "rustsec", "owner": "rustsec",
"repo": "advisory-db", "repo": "advisory-db",
"rev": "74e084413d979d52d2f93b1d93b1ab7b9ee648f5", "rev": "6e3286f4efa8c142fb33e5ea4342c8db6693cf34",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -18,11 +18,11 @@
}, },
"crane": { "crane": {
"locked": { "locked": {
"lastModified": 1780532242, "lastModified": 1783203018,
"narHash": "sha256-D+BsdpxmtUwtqGoY0IXPhHgTlmqgcZKCEo1oMyn7ep0=", "narHash": "sha256-G6R9IT/xwFuu+CYBWDUAok6AdC4ERC4ZfPPFtEpxnZE=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "59a82a1222dd3b2080b5cc52a1a2e8d5f1b77f37", "rev": "80db5bdc391be8a1794f6d8a2d56e3a84ebcede2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -39,11 +39,11 @@
"rust-analyzer-src": "rust-analyzer-src" "rust-analyzer-src": "rust-analyzer-src"
}, },
"locked": { "locked": {
"lastModified": 1781527054, "lastModified": 1783844668,
"narHash": "sha256-1fX9ev2Fh5QoKQ41G9dYutjo5j/jywu6tZse5Eb1Ck4=", "narHash": "sha256-3MOpw4y3reoErRLvFBDz0t9aG6FWXUj7leKvUns5eQA=",
"owner": "nix-community", "owner": "nix-community",
"repo": "fenix", "repo": "fenix",
"rev": "8c2e51dffefc040a21975da7abf6f252c8c9b783", "rev": "4e49ef62eedaa5c149d62b63b3f53844e1cc45d7",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -74,11 +74,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1778716662, "lastModified": 1782949081,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", "narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", "rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -89,11 +89,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1781074563, "lastModified": 1783776592,
"narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=", "narHash": "sha256-UgCQzxeWI75XM8G+hPrPh+MKzEPjG3SpAj7dtqSbksA=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca", "rev": "e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -105,11 +105,11 @@
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1777168982, "lastModified": 1782614948,
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=", "narHash": "sha256-ePjCwr1sNm9NYUqywL7QfK3JnlS015msC+eBu2zKlp8=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixpkgs.lib", "repo": "nixpkgs.lib",
"rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14", "rev": "db3f255737b94216eb71cce308e2912cf6bc2d7c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -132,11 +132,11 @@
"rust-analyzer-src": { "rust-analyzer-src": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1781453968, "lastModified": 1783779020,
"narHash": "sha256-+V3nK4pCngbmgyVGXY6Kkrlevp4ocPkJJLf2aqwkDNA=", "narHash": "sha256-vpm418WZa9l1KUl6HRs+Ga+SIfE+D2/sV4olwTlilYc=",
"owner": "rust-lang", "owner": "rust-lang",
"repo": "rust-analyzer", "repo": "rust-analyzer",
"rev": "cc272809a173c2c11d0e479d639c811c1eacf049", "rev": "5be5e89cf0145d73a85c805726821b4adfc3af48",
"type": "github" "type": "github"
}, },
"original": { "original": {
+48 -48
View File
@@ -510,14 +510,14 @@
} }
}, },
"node_modules/@shikijs/core": { "node_modules/@shikijs/core": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.3.1.tgz",
"integrity": "sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ==", "integrity": "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/primitive": "4.2.0", "@shikijs/primitive": "4.3.1",
"@shikijs/types": "4.2.0", "@shikijs/types": "4.3.1",
"@shikijs/vscode-textmate": "^10.0.2", "@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4", "@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.5" "hast-util-to-html": "^9.0.5"
@@ -527,13 +527,13 @@
} }
}, },
"node_modules/@shikijs/engine-javascript": { "node_modules/@shikijs/engine-javascript": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.3.1.tgz",
"integrity": "sha512-fjETeq1k5ffyXqRgS6+3hpvqseLalp1kjNfRbXpUgWR8FpZ1CmQfiNHovc5lncYjt/Vg5JK/WJEmLahjwMa0og==", "integrity": "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "4.2.0", "@shikijs/types": "4.3.1",
"@shikijs/vscode-textmate": "^10.0.2", "@shikijs/vscode-textmate": "^10.0.2",
"oniguruma-to-es": "^4.3.6" "oniguruma-to-es": "^4.3.6"
}, },
@@ -542,13 +542,13 @@
} }
}, },
"node_modules/@shikijs/engine-oniguruma": { "node_modules/@shikijs/engine-oniguruma": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.3.1.tgz",
"integrity": "sha512-hTorK1dffPkpbMUk6Z+828PgRo7d07HbnizoP0hNPFjhxMHctj0Px/qoHeGMYafc6ju+u9iMldN4JbVzNQM++g==", "integrity": "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "4.2.0", "@shikijs/types": "4.3.1",
"@shikijs/vscode-textmate": "^10.0.2" "@shikijs/vscode-textmate": "^10.0.2"
}, },
"engines": { "engines": {
@@ -556,26 +556,26 @@
} }
}, },
"node_modules/@shikijs/langs": { "node_modules/@shikijs/langs": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.3.1.tgz",
"integrity": "sha512-bwrVRlJ0wUhZxAbVdvBbv2TTC9yLsh4C/IO5Ofz0T8MQntgDvyVnkbjw9vi50r1kx7RCIJdnJnjZAwmAsXFLZQ==", "integrity": "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "4.2.0" "@shikijs/types": "4.3.1"
}, },
"engines": { "engines": {
"node": ">=20" "node": ">=20"
} }
}, },
"node_modules/@shikijs/primitive": { "node_modules/@shikijs/primitive": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.3.1.tgz",
"integrity": "sha512-NOq+DtUkVBJtZMVXL5A0vI0Xk8nvDYaXetFHSJFlOqjDZIVhIPRYFdGkSoElDqNuegikcc3A76SNUa8dTqtAYA==", "integrity": "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "4.2.0", "@shikijs/types": "4.3.1",
"@shikijs/vscode-textmate": "^10.0.2", "@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4" "@types/hast": "^3.0.4"
}, },
@@ -584,16 +584,16 @@
} }
}, },
"node_modules/@shikijs/rehype": { "node_modules/@shikijs/rehype": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@shikijs/rehype/-/rehype-4.3.1.tgz",
"integrity": "sha512-ST3EWye/dwF1gWskczJNBnwFtDzEQ9ceytXZtyc/GfwR5V0qJrkoSGZO55O3SAKDDsXkTDcsfwd9pVe7ROlAHg==", "integrity": "sha512-oshrlfUF3VPUJfnp5K1lLwsS/SRBKrIxONpdWebSKZXdBE3UsZnxgqpvRUA8UsofS7vmjFOCAHIT71ECbmOxTw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "4.2.0", "@shikijs/types": "4.3.1",
"@types/hast": "^3.0.4", "@types/hast": "^3.0.4",
"hast-util-to-string": "^3.0.1", "hast-util-to-string": "^3.0.1",
"shiki": "4.2.0", "shiki": "4.3.1",
"unified": "^11.0.5", "unified": "^11.0.5",
"unist-util-visit": "^5.1.0" "unist-util-visit": "^5.1.0"
}, },
@@ -602,22 +602,22 @@
} }
}, },
"node_modules/@shikijs/themes": { "node_modules/@shikijs/themes": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.3.1.tgz",
"integrity": "sha512-RX8IHYeLv8Cu2W6ruc3RxUqWn0IYCqSrMBzi/uRGAmfyDNOnNO5BF/Px7o97n4XTpmFTo5GbRaazuOWj+2ak2w==", "integrity": "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/types": "4.2.0" "@shikijs/types": "4.3.1"
}, },
"engines": { "engines": {
"node": ">=20" "node": ">=20"
} }
}, },
"node_modules/@shikijs/types": { "node_modules/@shikijs/types": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.3.1.tgz",
"integrity": "sha512-VT/MKtlpOhEPZloSH3Pb9WCZEBDoQVMa9jedp5UAwmJOar1DVc9DRODAxmYPW9M93IK4ryuqRejFfmlvlVDemw==", "integrity": "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -684,9 +684,9 @@
} }
}, },
"node_modules/@types/hast": { "node_modules/@types/hast": {
"version": "3.0.4", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz",
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1076,9 +1076,9 @@
} }
}, },
"node_modules/@ungap/structured-clone": { "node_modules/@ungap/structured-clone": {
"version": "1.3.1", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz",
"integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
@@ -3589,18 +3589,18 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/shiki": { "node_modules/shiki": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-4.2.0.tgz", "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.3.1.tgz",
"integrity": "sha512-hjNax6o/ylDy9lefQEaSDtzaT3iVNtZ3WmpQnbuQNoG4xvnSKf2kSKbihZVO4JRG1TTMejs7CmNRYlWgAL66pQ==", "integrity": "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@shikijs/core": "4.2.0", "@shikijs/core": "4.3.1",
"@shikijs/engine-javascript": "4.2.0", "@shikijs/engine-javascript": "4.3.1",
"@shikijs/engine-oniguruma": "4.2.0", "@shikijs/engine-oniguruma": "4.3.1",
"@shikijs/langs": "4.2.0", "@shikijs/langs": "4.3.1",
"@shikijs/themes": "4.2.0", "@shikijs/themes": "4.3.1",
"@shikijs/types": "4.2.0", "@shikijs/types": "4.3.1",
"@shikijs/vscode-textmate": "^10.0.2", "@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4" "@types/hast": "^3.0.4"
}, },
+3 -3
View File
@@ -486,7 +486,7 @@ pub(super) async fn get_remote_pdu(
.services .services
.rooms .rooms
.event_handler .event_handler
.parse_incoming_pdu(&response.pdu) .parse_incoming_pdu(&response.pdu, None)
.boxed() .boxed()
.await; .await;
@@ -832,7 +832,7 @@ pub(super) async fn force_set_room_state_from_server(
.services .services
.rooms .rooms
.event_handler .event_handler
.parse_incoming_pdu(&pdu) .parse_incoming_pdu(&pdu, Some(&room_version_rules))
.await .await
{ {
| Ok(t) => t, | Ok(t) => t,
@@ -1014,7 +1014,7 @@ pub(super) async fn resolve_true_destination(
let resolver: &MatrixResolver = if no_cache { let resolver: &MatrixResolver = if no_cache {
&MatrixResolverBuilder::new() &MatrixResolverBuilder::new()
.dangerous_tls_accept_invalid_certs(self.services.server.config.allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure) .dangerous_tls_accept_invalid_certs(self.services.server.config.allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure)
.http_client(self.services.client.default.clone()) .http_client(self.services.client.dns.clone())
.build()? .build()?
} else { } else {
&self.services.client.matrix_resolver &self.services.client.matrix_resolver
+1 -1
View File
@@ -54,7 +54,7 @@ pub(super) async fn fetch_support_well_known(&self, server_name: OwnedServerName
let response = self let response = self
.services .services
.client .client
.default .external_resource
.get(format!("https://{server_name}/.well-known/matrix/support")) .get(format!("https://{server_name}/.well-known/matrix/support"))
.send() .send()
.await?; .await?;
+1 -1
View File
@@ -26,7 +26,7 @@
}; };
use service::{mailer::messages, uiaa::UiaaInitiator, users::HashedPassword}; 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}; use crate::{Ruma, router::ClientIdentity};
pub(crate) mod register; pub(crate) mod register;
+8 -6
View File
@@ -20,9 +20,12 @@
assign, assign,
}; };
use serde_json::value::RawValue; 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; use crate::Ruma;
/// # `POST /_matrix/client/v3/register` /// # `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()); .unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
// Generate new token for the device // 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 // Create device for this account
services services
@@ -127,8 +130,7 @@ pub(crate) async fn register_route(
.create_device( .create_device(
&user_id, &user_id,
&device_id, &device_id,
&new_token, Some(new_token.clone()),
None,
body.initial_device_display_name.clone(), body.initial_device_display_name.clone(),
Some(client.to_string()), 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"); debug_info!(%user_id, ?device, "New account created via legacy registration");
Ok(assign!(register::v3::Response::new(user_id), { Ok(assign!(register::v3::Response::new(user_id), {
access_token: token, access_token: token.map(DeviceToken::into_token),
device_id: device, device_id: device,
refresh_token: None, refresh_token: None,
expires_in: None, expires_in: None,
+1 -8
View File
@@ -89,14 +89,7 @@ pub(crate) async fn update_device_route(
services services
.users .users
.create_device( .create_device(sender_user, &device_id, None, None, Some(client.to_string()))
sender_user,
&device_id,
&appservice.registration.as_token,
None,
None,
Some(client.to_string()),
)
.await?; .await?;
return Ok(update_device::v3::Response::new()); return Ok(update_device::v3::Response::new());
+3 -2
View File
@@ -3,7 +3,7 @@
use conduwuit::{ use conduwuit::{
Err, Result, debug_error, err, info, Err, Result, debug_error, err, info,
matrix::{event::gen_event_id_canonical_json, pdu::PartialPdu}, matrix::{event::gen_event_id_canonical_json, pdu::PartialPdu},
warn, trace, warn,
}; };
use futures::FutureExt; use futures::FutureExt;
use ruma::{ use ruma::{
@@ -166,7 +166,7 @@ pub(crate) async fn invite_helper(
let invite_room_state = services let invite_room_state = services
.rooms .rooms
.state .state
.summary_stripped(&pdu, room_id, recipient_user) .summary_stripped(&pdu, room_id, recipient_user, true)
.await; .await;
drop(state_lock); drop(state_lock);
@@ -193,6 +193,7 @@ pub(crate) async fn invite_helper(
.await .await
.ok(); .ok();
trace!(?request, "Sending invite");
let response = services let response = services
.sending .sending
.send_federation_request(recipient_user.server_name(), request) .send_federation_request(recipient_user.server_name(), request)
+1 -1
View File
@@ -24,5 +24,5 @@ pub(crate) async fn register_client_route(
.await .await
.map_err(|err| (StatusCode::BAD_REQUEST, Json(err)).into_response())?; .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, assign,
}; };
use service::users::DeviceToken;
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH}; use super::DEVICE_ID_LENGTH;
use crate::Ruma; use crate::Ruma;
/// # `GET /_matrix/client/v3/login` /// # `GET /_matrix/client/v3/login`
@@ -196,8 +197,8 @@ pub(crate) async fn login_route(
.clone() .clone()
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into()); .unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
// Generate a new token for the device (ensuring no collisions) // Generate a new token for the device
let token = services.users.generate_unique_token().await; let token = DeviceToken::new_random();
// Determine if device_id was provided and exists in the db for this user // Determine if device_id was provided and exists in the db for this user
let device_exists = if body.device_id.is_some() { let device_exists = if body.device_id.is_some() {
@@ -213,7 +214,7 @@ pub(crate) async fn login_route(
if device_exists { if device_exists {
services services
.users .users
.set_token(&user_id, &device_id, &token, None) .set_token(&user_id, &device_id, token.clone())
.await?; .await?;
} else { } else {
services services
@@ -221,8 +222,7 @@ pub(crate) async fn login_route(
.create_device( .create_device(
&user_id, &user_id,
&device_id, &device_id,
&token, Some(token.clone()),
None,
body.initial_device_display_name.clone(), body.initial_device_display_name.clone(),
Some(client.to_string()), Some(client.to_string()),
) )
@@ -241,7 +241,7 @@ pub(crate) async fn login_route(
info!("{user_id} logged in"); info!("{user_id} logged in");
#[allow(deprecated)] #[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, well_known: client_discovery_info,
expires_in: None, expires_in: None,
home_server: Some(services.config.server_name.clone()), 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) .authenticate_password(&body.auth, sender_user, body.identity.sender_device(), None)
.await?; .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); let expires_in = services.users.create_login_token(sender_user, &login_token);
Ok(get_login_token::v1::Response::new( Ok(get_login_token::v1::Response::new(
+2 -3
View File
@@ -15,10 +15,9 @@
#[derive(Deserialize)] #[derive(Deserialize)]
pub(crate) struct AuthQueryParams { pub(crate) struct AuthQueryParams {
pub(super) user_id: Option<String>, 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>, 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 /// 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. // MSC3202/MSC4190: Handle device_id masquerading for appservices.
// The device_id can be provided via `device_id` or // The device_id can be provided via `device_id` or
// `org.matrix.msc3202.device_id` query parameter. // `org.matrix.msc3202.device_id` query parameter.
let sender_device = let sender_device = if let Some(device_id) = query
if let Some(device_id) = query.device_id.as_deref().map(Into::into) { .device_id
// Verify the device exists for this user .or(query.legacy_device_id)
if services .as_deref()
.users .map(Into::into)
.get_device_metadata(&sender_user, device_id) {
.await // Verify the device exists for this user
.is_err() if services
{ .users
return Err!(Request(Forbidden( .get_device_metadata(&sender_user, device_id)
"Device does not exist for user or appservice cannot masquerade as \ .await
this device." .is_err()
))); {
} return Err!(Request(Forbidden(
"Device does not exist for user or appservice cannot masquerade as this \
device."
)));
}
Some(device_id.to_owned()) Some(device_id.to_owned())
} else { } else {
None None
}; };
Ok(ClientIdentity::Appservice { Ok(ClientIdentity::Appservice {
sender_user, sender_user,
+294 -110
View File
@@ -1,23 +1,29 @@
use std::collections::{HashMap, hash_map::Entry};
use axum::extract::State; use axum::extract::State;
use axum_client_ip::ClientIp; use axum_client_ip::ClientIp;
use base64::{Engine as _, engine::general_purpose}; use base64::{Engine as _, engine::general_purpose};
use conduwuit::{ use conduwuit::{
Err, Error, PduEvent, Result, err, error, Err, Error, EventTypeExt, PduEvent, Result, debug, err, error,
matrix::{Event, event::gen_event_id}, matrix::{Event, StateKey},
utils::{self, hash::sha256}, result::FlatOk,
state_res, trace,
utils::hash::sha256,
warn, warn,
}; };
use ruma::{ use ruma::{
CanonicalJsonValue, UserId, CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedUserId, ServerName,
UserId,
api::{ api::{
error::{ErrorKind, IncompatibleRoomVersionErrorData}, error::{ErrorKind, IncompatibleRoomVersionErrorData},
federation::membership::{RawStrippedState, create_invite}, federation::membership::{RawStrippedState, create_invite},
}, },
events::room::member::{MembershipState, RoomMemberEventContent}, events::{StateEventType, room::member::MembershipState},
serde::JsonObject, room_version_rules::RoomVersionRules,
}; };
use serde::Deserialize;
use crate::Ruma; use crate::{Ruma, server::utils::validate_any_membership_event};
/// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}` /// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
/// ///
@@ -28,30 +34,16 @@ pub(crate) async fn create_invite_route(
ClientIp(client): ClientIp, ClientIp(client): ClientIp,
body: Ruma<create_invite::v2::Request>, body: Ruma<create_invite::v2::Request>,
) -> Result<create_invite::v2::Response> { ) -> Result<create_invite::v2::Response> {
// ACL check origin
services
.rooms
.event_handler
.acl_check(&body.identity, &body.room_id)
.await?;
if !services.server.supported_room_version(&body.room_version) { if !services.server.supported_room_version(&body.room_version) {
return Err(Error::BadRequest( return Err(Error::BadRequest(
ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData::new( ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData::new(
body.room_version.clone(), body.room_version.clone(),
)), )),
"Server does not support this room version.", "This server does not support that room version",
)); ));
} }
let room_version_rules = body.room_version.rules().unwrap(); let room_version_rules = body.room_version.rules().unwrap();
if let Some(server) = body.room_id.server_name() {
if services.moderation.is_remote_server_forbidden(server) {
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
}
if services if services
.moderation .moderation
.is_remote_server_forbidden(&body.identity) .is_remote_server_forbidden(&body.identity)
@@ -61,90 +53,61 @@ pub(crate) async fn create_invite_route(
body.identity, body.room_id body.identity, body.room_id
); );
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Federation denied with {}", body.identity)));
} }
let mut signed_event = utils::to_canonical_object(&body.event) // First, validate the invite room state, so we can compare with the create
.map_err(|_| err!(Request(InvalidParam("Invite event is invalid."))))?; // event.
debug!(
// Ensure this is a membership event event_id=%body.event_id,
if signed_event room_id=%body.room_id,
.get("type") room_version=?body.room_version,
.expect("event must have a type") via=?body.via,
.as_str() "Validating invite room state for invite request"
.expect("type must be a string") );
!= "m.room.member" let (create_event_id, state) = validate_invite_state(
{ &services,
return Err!(Request(BadJson( &body.invite_room_state,
"Not allowed to send non-membership event to invite endpoint." &room_version_rules,
))); body.room_id.clone(),
}
let content: RoomMemberEventContent = serde_json::from_value(
signed_event
.get("content")
.ok_or_else(|| err!(Request(BadJson("Event missing content property"))))?
.clone()
.into(),
) )
.map_err(|e| err!(Request(BadJson(warn!("Event content is empty or invalid: {e}")))))?; .await?;
let create_event_json = state
.get(&StateEventType::RoomCreate.with_state_key(""))
.expect("must have create event in invite state by this point");
// Ensure this is an invite membership event // We can now perform the banned remote server check with the create event.
if content.membership != MembershipState::Invite { // N.B. this checks the sender field, which is technically incorrect for rooms
return Err!(Request(BadJson( // v10 and below. This usually isn't the case though so sue me
"Not allowed to send a non-invite membership event to invite endpoint." let creator = create_event_json
)));
}
// Ensure the sending user isn't a lying bozo
let sender_user = signed_event
.get("sender") .get("sender")
.and_then(|v| v.as_str()) .and_then(|v| v.as_str())
.map(UserId::parse) .map(UserId::parse)
.and_then(Result::ok) .flat_ok()
.ok_or_else(|| err!(Request(InvalidParam("Invalid sender property"))))?; .expect("must have valid sender in create event");
if services
if sender_user.server_name() != body.identity { .moderation
return Err!(Request(Forbidden("Sender's server does not match the origin server.",))); .is_remote_server_forbidden(creator.server_name())
}
// Ensure the target user belongs to this server
let recipient_user = signed_event
.get("state_key")
.and_then(|v| v.as_str())
.map(UserId::parse)
.and_then(Result::ok)
.ok_or_else(|| err!(Request(InvalidParam("Invalid state_key property"))))?;
if !services
.globals
.server_is_ours(recipient_user.server_name())
{ {
return Err!(Request(InvalidParam("User does not belong to this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }
// Make sure we're not ACL'ed from their room. // And then we can validate the member event itself
services let (mut signed_event, sender_user, recipient_user) = validate_invite_membership_event(
.rooms &services,
.event_handler &body.event,
.acl_check(recipient_user.server_name(), &body.room_id) &room_version_rules,
.await?; &body.identity,
create_event_id.clone(),
services body.room_id.clone(),
.server_keys body.event_id.clone(),
.hash_and_sign_event(&mut signed_event, &room_version_rules) )
.map_err(|e| err!(Request(InvalidParam("Failed to sign event: {e}"))))?; .await?;
// Generate event id
let event_id = gen_event_id(&signed_event, &room_version_rules)?;
// Add event_id back
signed_event.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.to_string()));
if services.rooms.metadata.is_banned(&body.room_id).await if services.rooms.metadata.is_banned(&body.room_id).await
&& !services.users.is_admin(&recipient_user).await && !services.users.is_admin(&recipient_user).await
{ {
return Err!(Request(Forbidden("This room is banned on this homeserver."))); return Err!(Request(Forbidden("That room is banned on this homeserver.")));
} }
if services.config.block_non_admin_invites && !services.users.is_admin(&recipient_user).await if services.config.block_non_admin_invites && !services.users.is_admin(&recipient_user).await
@@ -161,30 +124,57 @@ pub(crate) async fn create_invite_route(
return Err!(Request(Forbidden("Invite rejected by antispam service."))); return Err!(Request(Forbidden("Invite rejected by antispam service.")));
} }
// If we're already in the room, ensure that neither the origin nor ourselves
// are ACL'd.
let resident = services
.rooms
.state_cache
.server_in_room(services.globals.server_name(), &body.room_id)
.await;
if resident {
services
.rooms
.event_handler
.acl_check(&body.identity, &body.room_id)
.await?;
services
.rooms
.event_handler
.acl_check(recipient_user.server_name(), &body.room_id)
.await
.map_err(|_| err!(Request(Forbidden("This server is ACL'd from that room"))))?;
}
services
.server_keys
.hash_and_sign_event(&mut signed_event, &room_version_rules)
.map_err(|e| err!(Request(InvalidParam("Failed to sign event: {e}"))))?;
// Add event_id back
signed_event
.insert("event_id".to_owned(), CanonicalJsonValue::String(body.event_id.to_string()));
let mut invite_state = body.invite_room_state.clone(); let mut invite_state = body.invite_room_state.clone();
let pdu = PduEvent::from_id_val(&body.event_id, signed_event.clone())
let mut event: JsonObject = serde_json::from_str(body.event.get()) .expect("must be able to create PDU object");
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?; invite_state.push(RawStrippedState::Pdu(serde_json::value::to_raw_value(&signed_event)?));
event.insert("event_id".to_owned(), "$placeholder".into());
let pdu: PduEvent = serde_json::from_value(event.into())
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?;
invite_state.push(RawStrippedState::Pdu(
serde_json::value::to_raw_value(&pdu).expect("PDU was just created, it must be valid"),
));
// If we are active in the room, the remote server will notify us about the // If we are active in the room, the remote server will notify us about the
// join/invite through /send. If we are not in the room, we need to manually // join/invite through /send. If we are not in the room, we need to manually
// record the invited state for client /sync through update_membership(), and // record the invited state for client /sync through update_membership(), and
// send the invite PDU to the relevant appservices. // send the invite PDU to the relevant appservices.
if !services if !resident {
.rooms // We will start by recording the room's create event as an outlier.
.state_cache // This will allow us to recognise it later in case the sender revokes the
.server_in_room(services.globals.server_name(), &body.room_id) // invite over federation later. We could store more state from the invite
.await // request, but we will get that during send_join anyway.
{ // This is safe to just add directly as an outlier as we already auth checked it
// during validation.
services
.rooms
.outlier
.add_pdu_outlier(&create_event_id, create_event_json);
services services
.rooms .rooms
.state_cache .state_cache
@@ -240,3 +230,197 @@ pub(crate) async fn create_invite_route(
.await, .await,
)) ))
} }
/// Validates the *membership event* in the invite request, per the steps listed
/// under the invite endpoint's [spec].
///
/// Returns the validated JSON body, sender user ID, and recipient user ID.
///
/// Since this function performs a PDU format check, the create event must be
/// known ahead of time. This implies validating the invite state before the
/// invite event itself.
///
/// [spec]: https://spec.matrix.org/v1.19/server-server-api/#put_matrixfederationv2inviteroomideventid
async fn validate_invite_membership_event(
services: &crate::State,
body: &serde_json::value::RawValue,
room_version_rules: &RoomVersionRules,
origin: &ServerName,
create_event_id: OwnedEventId,
room_id: OwnedRoomId,
event_id: OwnedEventId,
) -> Result<(CanonicalJsonObject, OwnedUserId, OwnedUserId)> {
trace!(?body, "Invite membership event");
let (pdu, target_membership, sender_user, recipient_user) = validate_any_membership_event(
services,
body,
room_version_rules,
create_event_id,
room_id,
event_id,
)
.await?;
// Ensure the sender belongs to the remote that is sending the invite
if sender_user.server_name() != origin {
return Err!(Request(Forbidden("Sender belongs to a different server")));
}
// Ensure the target user belongs to this server
if !services
.globals
.server_is_ours(recipient_user.server_name())
{
return Err!(Request(InvalidParam("Recipient does not belong to this homeserver")));
}
if target_membership != MembershipState::Invite {
return Err!(Request(BadJson("Invalid membership (expected `invite`)")));
}
Ok((pdu, sender_user, recipient_user))
}
/// Validates the *invite state* of an invite request, per the steps listed
/// under the endpoint's [spec].
///
/// Returns the create event's event ID, and the partial state map.
///
/// [spec]: https://spec.matrix.org/v1.19/server-server-api/#put_matrixfederationv2inviteroomideventid
async fn validate_invite_state(
services: &crate::State,
invite_state: &[RawStrippedState],
room_version_rules: &RoomVersionRules,
room_id: OwnedRoomId,
) -> Result<(OwnedEventId, HashMap<(StateEventType, StateKey), CanonicalJsonObject>)> {
trace!(?invite_state, "Raw invite state");
let mut invite_state_map: HashMap<(StateEventType, StateKey), _> =
HashMap::with_capacity(invite_state.len());
let mut create_event_id: Option<OwnedEventId> = None;
for (idx, invite_state_event) in invite_state.iter().cloned().enumerate() {
trace!(%idx, ?invite_state_event, "Invite state event");
// Stripped state hasn't been sent over federation since v1.16.
let RawStrippedState::Pdu(raw_pdu) = invite_state_event else {
debug!(%idx, "Invite state event is not a PDU");
return Err!(Request(InvalidParam(
"PDU in invite state (index {idx}) violates the room event format"
)));
};
let (state_event_room_id, state_event_id, state_event_json) = services
.rooms
.event_handler
.parse_incoming_pdu(&raw_pdu, Some(room_version_rules))
.await
.map_err(|e| {
err!(Request(InvalidParam(debug_warn!("Invalid PDU in invite state: {e}"))))
})?;
if state_event_room_id != room_id {
return Err!(Request(InvalidParam(debug_warn!(
%state_event_room_id,
%room_id,
"PDU in invite state ({state_event_id}) belongs to the wrong room"
))));
}
services
.server_keys
.verify_event(&state_event_json, room_version_rules)
.await
.map_err(|e| {
err!(Request(InvalidParam("Signature verification failed on invite event: {e}")))
})?;
let Some(state_key) = state_event_json.get("state_key").and_then(|k| k.as_str()) else {
return Err!(Request(InvalidParam(debug_info!(
"PDU in invite state ({state_event_id}) is not a state event"
))));
};
let Some(event_type) = state_event_json.get("type").and_then(|k| k.as_str()) else {
return Err!(Request(InvalidParam(debug_warn!(
"PDU in invite state ({state_event_id}) is not an event?"
))));
};
let key = StateEventType::from(event_type).with_state_key(state_key);
match invite_state_map.entry(key) {
| Entry::Occupied(entry) =>
return Err!(Request(InvalidParam(
"Duplicate state events in invite state for state key: {:?}",
entry.key(),
))),
| Entry::Vacant(entry) => {
if entry.key().0 == StateEventType::RoomCreate {
// Ensure this is a legal create event.
let pdu_event =
PduEvent::from_id_val(&state_event_id, state_event_json.clone())
.expect("must be able to create pdu event from event json");
debug!("Validating discovered create event in invite room state");
validate_invite_create_event(&pdu_event, room_version_rules).await?;
create_event_id = Some(state_event_id);
}
entry.insert(state_event_json);
},
}
}
let Some(create_event_id) = create_event_id else {
return Err!(Request(InvalidParam(debug_warn!(
parsed_state=?invite_state_map,
"Invite state does not contain the m.room.create event"
))));
};
invite_state_map.iter().try_for_each(|(key, event_json)| {
service::rooms::event_handler::Service::pdu_format_check_1(
event_json,
room_version_rules,
&create_event_id,
)
.map_err(|e| {
err!(Request(InvalidParam(
"PDU in invite state for {key:?} violates the room event format: {e}"
)))
})
})?;
Ok((create_event_id, invite_state_map))
}
#[derive(Deserialize)]
struct MFederate {
#[serde(rename = "m.federate")]
mfederate: Option<bool>,
}
/// Validates that a create event is suitable for the invite, namely:
///
/// 1. It passes auth checks (aka is valid)
/// 2. The room is federated (there's no point persisting unfederated rooms)
async fn validate_invite_create_event(
pdu: &PduEvent,
room_version_rules: &RoomVersionRules,
) -> Result {
if !state_res::auth_check(
room_version_rules,
pdu,
None,
|_, _| async {
unreachable!("No state should be fetched when processing a lone create event");
},
pdu,
)
.await
.unwrap_or_default()
{
return Err!(Request(InvalidParam("m.room.create event fails auth check")));
}
let can_federate = pdu.get_content::<MFederate>()?.mfederate;
if !can_federate.unwrap_or(true) {
return Err!(Request(InvalidParam(
"Cannot receive invites to a room with m.federate=false"
)));
}
Ok(())
}
-8
View File
@@ -75,14 +75,6 @@ pub(crate) async fn create_join_event_template_route(
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }
if let Some(server) = body.room_id.server_name() {
if services.moderation.is_remote_server_forbidden(server) {
return Err!(Request(Forbidden(warn!(
"Room ID server name {server} is banned on this homeserver."
))));
}
}
let room_version = services.rooms.state.get_room_version(&body.room_id).await?; let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
let room_version_rules = room_version.rules().unwrap(); let room_version_rules = room_version.rules().unwrap();
if !body.ver.contains(&room_version) { if !body.ver.contains(&room_version) {
-6
View File
@@ -58,12 +58,6 @@ pub(crate) async fn create_knock_event_template_route(
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }
if let Some(server) = body.room_id.server_name() {
if services.moderation.is_remote_server_forbidden(server) {
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
}
let room_version = services.rooms.state.get_room_version(&body.room_id).await?; let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
let room_version_rules = room_version.rules().unwrap(); let room_version_rules = room_version.rules().unwrap();
+1
View File
@@ -27,6 +27,7 @@ pub(crate) async fn create_leave_event_template_route(
{ {
info!( info!(
origin = body.identity.as_str(), origin = body.identity.as_str(),
room_id = %body.room_id,
"Refusing to serve make_leave for room we aren't participating in" "Refusing to serve make_leave for room we aren't participating in"
); );
return Err!(Request(NotFound("This server is not participating in that room."))); return Err!(Request(NotFound("This server is not participating in that room.")));
+1 -1
View File
@@ -150,7 +150,7 @@ async fn process_inbound_transaction(
.pdus .pdus
.iter() .iter()
.stream() .stream()
.broad_then(|pdu| services.rooms.event_handler.parse_incoming_pdu(pdu)) .broad_then(|pdu| services.rooms.event_handler.parse_incoming_pdu(pdu, None))
.inspect_err(|e| warn!("Could not parse incoming PDU: {e}")) .inspect_err(|e| warn!("Could not parse incoming PDU: {e}"))
.ready_filter_map(Result::ok); .ready_filter_map(Result::ok);
+67 -115
View File
@@ -2,34 +2,36 @@
use axum::extract::State; use axum::extract::State;
use conduwuit::{ use conduwuit::{
Err, Event, Result, at, debug, err, info, Err, Event, Result, at, debug, err, info, trace,
matrix::event::gen_event_id_canonical_json,
trace,
utils::stream::{BroadbandExt, IterStream, TryBroadbandExt}, utils::stream::{BroadbandExt, IterStream, TryBroadbandExt},
warn, warn,
}; };
use conduwuit_service::Services; use conduwuit_service::Services;
use futures::{FutureExt, StreamExt, TryStreamExt}; use futures::{FutureExt, StreamExt, TryStreamExt};
use ruma::{ use ruma::{
CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, ServerName, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, RoomId, ServerName, UserId,
api::federation::membership::create_join_event, api::federation::membership::create_join_event,
assign, assign,
events::{ events::{
StateEventType, TimelineEventType, TimelineEventType,
room::member::{MembershipState, RoomMemberEventContent}, room::member::{MembershipState, RoomMemberEventContent},
}, },
room_version_rules::RoomVersionRules,
}; };
use serde_json::value::{RawValue as RawJsonValue, to_raw_value}; use serde_json::value::to_raw_value;
use crate::Ruma; use crate::{Ruma, server::utils::validate_any_membership_event};
/// helper method for /send_join v1 and v2 /// Creates a join membership event for the target, returning a computed
/// response with room state, auth chains, etc.
#[tracing::instrument(skip(services, pdu, omit_members), fields(room_id = room_id.as_str(), origin = origin.as_str()), level = "info")] #[tracing::instrument(skip(services, pdu, omit_members), fields(room_id = room_id.as_str(), origin = origin.as_str()), level = "info")]
async fn create_join_event( async fn create_join_event(
services: &Services, services: &Services,
origin: &ServerName, origin: &ServerName,
room_id: &RoomId, room_id: &RoomId,
pdu: &RawJsonValue, event_id: &EventId,
room_version_rules: &RoomVersionRules,
mut pdu: CanonicalJsonObject,
omit_members: bool, omit_members: bool,
) -> Result<create_join_event::v2::RoomState> { ) -> Result<create_join_event::v2::RoomState> {
if !services.rooms.metadata.exists(room_id).await { if !services.rooms.metadata.exists(room_id).await {
@@ -43,6 +45,7 @@ async fn create_join_event(
{ {
info!( info!(
origin = origin.as_str(), origin = origin.as_str(),
room_id = %room_id,
"Refusing to serve send_join for room we aren't participating in" "Refusing to serve send_join for room we aren't participating in"
); );
return Err!(Request(NotFound("This server is not participating in that room."))); return Err!(Request(NotFound("This server is not participating in that room.")));
@@ -64,94 +67,25 @@ async fn create_join_event(
.await .await
.map_err(|e| err!(Request(NotFound(error!("Room has no state: {e}")))))?; .map_err(|e| err!(Request(NotFound(error!("Room has no state: {e}")))))?;
// We do not add the event_id field to the pdu here because of signature and let sender = pdu
// hashes checks .get("sender")
trace!("Getting room version"); .and_then(|v| v.as_str())
let room_version = services.rooms.state.get_room_version(room_id).await?; .map(UserId::parse)
let room_version_rules = room_version.rules().unwrap(); .and_then(Result::ok)
.expect("sender was already validated");
trace!("Generating event ID and converting to canonical json");
let Ok((event_id, mut value)) = gen_event_id_canonical_json(pdu, &room_version_rules) else {
// Event could not be converted to canonical json
return Err!(Request(BadJson("Could not convert event to canonical json.")));
};
let event_room_id: OwnedRoomId = serde_json::from_value(
value
.get("room_id")
.ok_or_else(|| err!(Request(BadJson("Event missing room_id property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("room_id field is not a valid room ID: {e}")))))?;
if event_room_id != room_id {
return Err!(Request(BadJson("Event room_id does not match request path room ID.")));
}
let event_type: StateEventType = serde_json::from_value(
value
.get("type")
.ok_or_else(|| err!(Request(BadJson("Event missing type property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("Event has invalid state event type: {e}")))))?;
if event_type != StateEventType::RoomMember {
return Err!(Request(BadJson(
"Not allowed to send non-membership state event to join endpoint."
)));
}
let content: RoomMemberEventContent = serde_json::from_value( let content: RoomMemberEventContent = serde_json::from_value(
value pdu.get("content")
.get("content")
.ok_or_else(|| err!(Request(BadJson("Event missing content property"))))? .ok_or_else(|| err!(Request(BadJson("Event missing content property"))))?
.clone() .clone()
.into(), .into(),
) )
.map_err(|e| err!(Request(BadJson(warn!("Event content is empty or invalid: {e}")))))?; .map_err(|e| err!(Request(BadJson(warn!("Event content is empty or invalid: {e}")))))?;
if content.membership != MembershipState::Join {
return Err!(Request(BadJson(
"Not allowed to send a non-join membership event to join endpoint."
)));
}
let sender: OwnedUserId = serde_json::from_value(
value
.get("sender")
.ok_or_else(|| err!(Request(BadJson("Event missing sender property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("sender property is not a valid user ID: {e}")))))?;
// check if origin server is trying to send for another server
if sender.server_name() != origin {
return Err!(Request(Forbidden("Not allowed to join on behalf of another server.")));
}
let state_key: OwnedUserId = serde_json::from_value(
value
.get("state_key")
.ok_or_else(|| err!(Request(BadJson("Event missing state_key property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("State key is not a valid user ID: {e}")))))?;
if state_key != sender {
return Err!(Request(BadJson("State key does not match sender user.")));
}
if let Some(authorising_user) = content.join_authorized_via_users_server { if let Some(authorising_user) = content.join_authorized_via_users_server {
use ruma::RoomVersionId::*; if !room_version_rules.authorization.restricted_join_rule {
if matches!(room_version, V1 | V2 | V3 | V4 | V5 | V6 | V7) {
return Err!(Request(InvalidParam( return Err!(Request(InvalidParam(
"Room version {room_version} does not support restricted rooms but \ "Room version does not support restricted rooms but \
join_authorised_via_users_server ({authorising_user}) was found in the event." join_authorised_via_users_server ({authorising_user}) was found in the event."
))); )));
} }
@@ -174,8 +108,7 @@ async fn create_join_event(
they cannot authorise your join." they cannot authorise your join."
))); )));
} }
if !super::user_can_perform_restricted_join(services, &sender, room_id).await? {
if !super::user_can_perform_restricted_join(services, &state_key, room_id).await? {
return Err!(Request(UnableToAuthorizeJoin( return Err!(Request(UnableToAuthorizeJoin(
"Joining user did not pass restricted room's rules." "Joining user did not pass restricted room's rules."
))); )));
@@ -183,7 +116,7 @@ async fn create_join_event(
services services
.server_keys .server_keys
.hash_and_sign_event(&mut value, &room_version_rules) .hash_and_sign_event(&mut pdu, room_version_rules)
.map_err(|e| { .map_err(|e| {
err!(Request(InvalidParam(warn!("Failed to sign send_join event: {e}")))) err!(Request(InvalidParam(warn!("Failed to sign send_join event: {e}"))))
})?; })?;
@@ -200,7 +133,7 @@ async fn create_join_event(
let pdu_id = services let pdu_id = services
.rooms .rooms
.event_handler .event_handler
.handle_incoming_pdu(sender.server_name(), room_id, &event_id, value.clone(), false) .handle_incoming_pdu(sender.server_name(), room_id, event_id, pdu.clone(), false)
.boxed() .boxed()
.await? .await?
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?; .ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
@@ -220,14 +153,12 @@ async fn create_join_event(
.iter() .iter()
.try_stream() .try_stream()
.broad_filter_map(|event_id| async move { .broad_filter_map(|event_id| async move {
if omit_members { if omit_members && let Ok(e) = event_id.as_ref() {
if let Ok(e) = event_id.as_ref() { let pdu = services.rooms.timeline.get_pdu(e).await;
let pdu = services.rooms.timeline.get_pdu(e).await; if pdu.is_ok_and(|p| *p.kind() == TimelineEventType::RoomMember) {
if pdu.is_ok_and(|p| *p.kind() == TimelineEventType::RoomMember) { trace!("omitting member event {e:?} from returned state");
trace!("omitting member event {e:?} from returned state"); // skip members
// skip members return None;
return None;
}
} }
} }
Some(event_id) Some(event_id)
@@ -289,7 +220,7 @@ async fn create_join_event(
Ok(assign!(create_join_event::v2::RoomState::new(), { Ok(assign!(create_join_event::v2::RoomState::new(), {
auth_chain, auth_chain,
state, state,
event: to_raw_value(&CanonicalJsonValue::Object(value)).ok(), event: to_raw_value(&CanonicalJsonValue::Object(pdu)).ok(),
members_omitted: omit_members, members_omitted: omit_members,
servers_in_room, servers_in_room,
})) }))
@@ -309,24 +240,45 @@ pub(crate) async fn create_join_event_v2_route(
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }
if let Some(server) = body.room_id.server_name() { let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
if services.moderation.is_remote_server_forbidden(server) { let create_event = services
warn!( .rooms
"Server {} tried joining room ID {} through us which has a server name that is \ .state_accessor
globally forbidden. Rejecting.", .get_room_create_event(&body.room_id)
body.identity, &body.room_id, .await;
); let room_version_rules = room_version.rules().unwrap();
return Err!(Request(Forbidden(warn!(
"Room ID server name {server} is banned on this homeserver." let (value, membership, sender, target) = validate_any_membership_event(
)))); &services,
} &body.pdu,
&room_version_rules,
create_event.event_id.clone(),
body.room_id.clone(),
body.event_id.clone(),
)
.await?;
if membership != MembershipState::Join {
return Err!(Request(InvalidParam("Invalid membership (expected `join`)")));
}
if sender.server_name() != body.identity {
return Err!(Request(InvalidParam("Sender belongs to a different server")));
}
if sender != target {
return Err!(Request(InvalidParam("Sender does not match state key")));
} }
let now = Instant::now(); let now = Instant::now();
let room_state = let room_state = create_join_event(
create_join_event(&services, &body.identity, &body.room_id, &body.pdu, body.omit_members) &services,
.boxed() &body.identity,
.await?; &body.room_id,
&body.event_id,
&room_version_rules,
value,
body.omit_members,
)
.boxed()
.await?;
info!( info!(
"Finished sending a join for {} in {} in {:?}", "Finished sending a join for {} in {} in {:?}",
body.identity, body.identity,
+29 -97
View File
@@ -1,21 +1,11 @@
use axum::extract::State; use axum::extract::State;
use conduwuit::{ use conduwuit::{Err, Event, Result, debug_info, err, matrix::pdu::PduEvent, warn};
Err, Result, err, info,
matrix::{event::gen_event_id_canonical_json, pdu::PduEvent},
warn,
};
use futures::FutureExt; use futures::FutureExt;
use ruma::{ use ruma::{
OwnedUserId, api::federation::membership::create_knock_event, events::room::member::MembershipState,
api::federation::membership::create_knock_event,
events::{
StateEventType,
room::member::{MembershipState, RoomMemberEventContent},
},
serde::JsonObject,
}; };
use crate::Ruma; use crate::{Ruma, server::utils::validate_any_membership_event};
/// # `PUT /_matrix/federation/v1/send_knock/{roomId}/{eventId}` /// # `PUT /_matrix/federation/v1/send_knock/{roomId}/{eventId}`
/// ///
@@ -33,18 +23,7 @@ pub(crate) async fn create_knock_event_v1_route(
forbidden. Rejecting.", forbidden. Rejecting.",
body.identity, &body.room_id, body.identity, &body.room_id,
); );
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Federation denied with {}", body.identity)));
}
if let Some(server) = body.room_id.server_name() {
if services.moderation.is_remote_server_forbidden(server) {
warn!(
"Server {} tried knocking room ID {} which has a server name that is globally \
forbidden. Rejecting.",
body.identity, &body.room_id,
);
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
}
} }
if !services.rooms.metadata.exists(&body.room_id).await { if !services.rooms.metadata.exists(&body.room_id).await {
@@ -57,8 +36,9 @@ pub(crate) async fn create_knock_event_v1_route(
.server_in_room(services.globals.server_name(), &body.room_id) .server_in_room(services.globals.server_name(), &body.room_id)
.await .await
{ {
info!( debug_info!(
origin = body.identity.as_str(), origin = body.identity.as_str(),
room_id = %body.room_id,
"Refusing to serve send_knock for room we aren't participating in" "Refusing to serve send_knock for room we aren't participating in"
); );
return Err!(Request(NotFound("This server is not participating in that room."))); return Err!(Request(NotFound("This server is not participating in that room.")));
@@ -72,88 +52,40 @@ pub(crate) async fn create_knock_event_v1_route(
.await?; .await?;
let room_version = services.rooms.state.get_room_version(&body.room_id).await?; let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
let create_event = services
.rooms
.state_accessor
.get_room_create_event(&body.room_id)
.await;
let room_version_rules = room_version.rules().unwrap(); let room_version_rules = room_version.rules().unwrap();
if !room_version_rules.authorization.knocking { if !room_version_rules.authorization.knocking {
return Err!(Request(Forbidden("Room version does not support knocking."))); return Err!(Request(Forbidden("Room version does not support knocking.")));
} }
let Ok((event_id, value)) = gen_event_id_canonical_json(&body.pdu, &room_version_rules) let (mut event, target_membership, sender, target) = validate_any_membership_event(
else { &services,
// Event could not be converted to canonical json &body.pdu,
return Err!(Request(InvalidParam("Could not convert event to canonical json."))); &room_version_rules,
}; create_event.event_id().to_owned(),
body.room_id.clone(),
let event_type: StateEventType = serde_json::from_value( body.event_id.clone(),
value
.get("type")
.ok_or_else(|| err!(Request(InvalidParam("Event has no event type."))))?
.clone()
.into(),
) )
.map_err(|e| err!(Request(InvalidParam("Event has invalid event type: {e}"))))?; .await?;
if event_type != StateEventType::RoomMember { if target_membership != MembershipState::Knock {
return Err!(Request(InvalidParam( return Err!(Request(InvalidParam("Invalid membership (expected `knock`)")));
"Not allowed to send non-membership state event to knock endpoint.",
)));
} }
let content: RoomMemberEventContent = serde_json::from_value(
value
.get("content")
.ok_or_else(|| err!(Request(InvalidParam("Membership event has no content"))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(InvalidParam("Event has invalid membership content: {e}"))))?;
if content.membership != MembershipState::Knock {
return Err!(Request(InvalidParam(
"Not allowed to send a non-knock membership event to knock endpoint."
)));
}
// ACL check sender server name
let sender: OwnedUserId = serde_json::from_value(
value
.get("sender")
.ok_or_else(|| err!(Request(InvalidParam("Event has no sender user ID."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson("Event sender is not a valid user ID: {e}"))))?;
services
.rooms
.event_handler
.acl_check(sender.server_name(), &body.room_id)
.await?;
// check if origin server is trying to send for another server
if sender.server_name() != body.identity { if sender.server_name() != body.identity {
return Err!(Request(BadJson("Not allowed to knock on behalf of another server/user."))); return Err!(Request(InvalidParam("Sender belongs to a different server")));
}
if sender != target {
return Err!(Request(InvalidParam("Sender does not match state key")));
} }
let state_key: OwnedUserId = serde_json::from_value( event.insert("event_id".to_owned(), body.event_id.as_str().into());
value
.get("state_key")
.ok_or_else(|| err!(Request(InvalidParam("Event does not have a state_key"))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson("Event does not have a valid state_key: {e}"))))?;
if state_key != sender { let pdu = PduEvent::from_id_val(&body.event_id, event.clone())
return Err!(Request(InvalidParam("state_key does not match sender user of event.")));
}
let mut event: JsonObject = serde_json::from_str(body.pdu.get())
.map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?;
event.insert("event_id".to_owned(), "$placeholder".into());
let pdu: PduEvent = serde_json::from_value(event.into())
.map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?; .map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?;
let mutex_lock = services let mutex_lock = services
@@ -166,7 +98,7 @@ pub(crate) async fn create_knock_event_v1_route(
let pdu_id = services let pdu_id = services
.rooms .rooms
.event_handler .event_handler
.handle_incoming_pdu(sender.server_name(), &body.room_id, &event_id, value.clone(), false) .handle_incoming_pdu(sender.server_name(), &body.room_id, &body.event_id, event, false)
.boxed() .boxed()
.await? .await?
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?; .ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
@@ -181,7 +113,7 @@ pub(crate) async fn create_knock_event_v1_route(
let knock_room_state = services let knock_room_state = services
.rooms .rooms
.state .state
.summary_stripped(&pdu, &body.room_id, &sender) .summary_stripped(&pdu, &body.room_id, &sender, true)
.await; .await;
Ok(create_knock_event::v1::Response::new(knock_room_state)) Ok(create_knock_event::v1::Response::new(knock_room_state))
+31 -104
View File
@@ -1,18 +1,11 @@
use axum::extract::State; use axum::extract::State;
use conduwuit::{Err, Result, err, info, matrix::event::gen_event_id_canonical_json}; use conduwuit::{Err, Result, debug_info, err};
use conduwuit_service::Services;
use futures::FutureExt; use futures::FutureExt;
use ruma::{ use ruma::{
OwnedRoomId, OwnedUserId, RoomId, ServerName, api::federation::membership::create_leave_event, events::room::member::MembershipState,
api::federation::membership::create_leave_event,
events::{
StateEventType,
room::member::{MembershipState, RoomMemberEventContent},
},
}; };
use serde_json::value::RawValue as RawJsonValue;
use crate::Ruma; use crate::{Ruma, server::utils::validate_any_membership_event};
/// # `PUT /_matrix/federation/v2/send_leave/{roomId}/{eventId}` /// # `PUT /_matrix/federation/v2/send_leave/{roomId}/{eventId}`
/// ///
@@ -21,17 +14,8 @@ pub(crate) async fn create_leave_event_v2_route(
State(services): State<crate::State>, State(services): State<crate::State>,
body: Ruma<create_leave_event::v2::Request>, body: Ruma<create_leave_event::v2::Request>,
) -> Result<create_leave_event::v2::Response> { ) -> Result<create_leave_event::v2::Response> {
create_leave_event(&services, &body.identity, &body.room_id, &body.pdu).await?; let room_id = body.room_id.as_ref();
let origin = &body.identity;
Ok(create_leave_event::v2::Response::new())
}
async fn create_leave_event(
services: &Services,
origin: &ServerName,
room_id: &RoomId,
pdu: &RawJsonValue,
) -> Result {
if !services.rooms.metadata.exists(room_id).await { if !services.rooms.metadata.exists(room_id).await {
return Err!(Request(NotFound("Room is unknown to this server."))); return Err!(Request(NotFound("Room is unknown to this server.")));
} }
@@ -42,9 +26,10 @@ async fn create_leave_event(
.server_in_room(services.globals.server_name(), room_id) .server_in_room(services.globals.server_name(), room_id)
.await .await
{ {
info!( debug_info!(
origin = origin.as_str(), origin = origin.as_str(),
"Refusing to serve backfill for room we aren't participating in" room_id = %room_id,
"Refusing to send_leave for room we aren't participating in"
); );
return Err!(Request(NotFound("This server is not participating in that room."))); return Err!(Request(NotFound("This server is not participating in that room.")));
} }
@@ -56,91 +41,31 @@ async fn create_leave_event(
.acl_check(origin, room_id) .acl_check(origin, room_id)
.await?; .await?;
// We do not add the event_id field to the pdu here because of signature and
// hashes checks
let room_version = services.rooms.state.get_room_version(room_id).await?; let room_version = services.rooms.state.get_room_version(room_id).await?;
let create_event = services
.rooms
.state_accessor
.get_room_create_event(room_id)
.await;
let room_version_rules = room_version.rules().unwrap(); let room_version_rules = room_version.rules().unwrap();
let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_rules) else { let (value, membership, sender, target) = validate_any_membership_event(
// Event could not be converted to canonical json &services,
return Err!(Request(BadJson("Could not convert event to canonical json."))); &body.pdu,
}; &room_version_rules,
create_event.event_id.clone(),
let event_room_id: OwnedRoomId = serde_json::from_value( body.room_id.clone(),
serde_json::to_value( body.event_id.clone(),
value
.get("room_id")
.ok_or_else(|| err!(Request(BadJson("Event missing room_id property."))))?,
)
.expect("CanonicalJson is valid json value"),
) )
.map_err(|e| err!(Request(BadJson(warn!("room_id field is not a valid room ID: {e}")))))?; .await?;
if membership != MembershipState::Leave {
if event_room_id != room_id { return Err!(Request(InvalidParam("Invalid membership (expected `leave`)")));
return Err!(Request(BadJson("Event room_id does not match request path room ID.")));
} }
if sender.server_name() != body.identity {
let content: RoomMemberEventContent = serde_json::from_value( return Err!(Request(InvalidParam("Sender belongs to a different server")));
value
.get("content")
.ok_or_else(|| err!(Request(BadJson("Event missing content property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("Event content is empty or invalid: {e}")))))?;
if content.membership != MembershipState::Leave {
return Err!(Request(BadJson(
"Not allowed to send a non-leave membership event to leave endpoint."
)));
} }
if sender != target {
let event_type: StateEventType = serde_json::from_value( return Err!(Request(InvalidParam("Sender does not match state key")));
value
.get("type")
.ok_or_else(|| err!(Request(BadJson("Event missing type property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("Event has invalid state event type: {e}")))))?;
if event_type != StateEventType::RoomMember {
return Err!(Request(BadJson(
"Not allowed to send non-membership state event to leave endpoint."
)));
}
// ACL check sender server name
let sender: OwnedUserId = serde_json::from_value(
value
.get("sender")
.ok_or_else(|| err!(Request(BadJson("Event missing sender property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("sender property is not a valid user ID: {e}")))))?;
services
.rooms
.event_handler
.acl_check(sender.server_name(), room_id)
.await?;
if sender.server_name() != origin {
return Err!(Request(BadJson("Not allowed to leave on behalf of another server/user.")));
}
let state_key: OwnedUserId = serde_json::from_value(
value
.get("state_key")
.ok_or_else(|| err!(Request(BadJson("Event missing state_key property."))))?
.clone()
.into(),
)
.map_err(|e| err!(Request(BadJson(warn!("State key is not a valid user ID: {e}")))))?;
if state_key != sender {
return Err!(Request(BadJson("State key does not match sender user.")));
} }
let mutex_lock = services let mutex_lock = services
@@ -153,7 +78,7 @@ async fn create_leave_event(
let pdu_id = services let pdu_id = services
.rooms .rooms
.event_handler .event_handler
.handle_incoming_pdu(origin, room_id, &event_id, value, false) .handle_incoming_pdu(origin, room_id, &body.event_id, value, false)
.boxed() .boxed()
.await? .await?
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?; .ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
@@ -164,5 +89,7 @@ async fn create_leave_event(
.sending .sending
.send_pdu_room(room_id, &pdu_id) .send_pdu_room(room_id, &pdu_id)
.boxed() .boxed()
.await .await?;
Ok(create_leave_event::v2::Response::new())
} }
+105 -2
View File
@@ -1,7 +1,10 @@
use conduwuit::{Err, Result, is_false}; use conduwuit::{Err, Result, err, is_false};
use conduwuit_service::Services; use conduwuit_service::Services;
use futures::{FutureExt, future::OptionFuture, join}; use futures::{FutureExt, future::OptionFuture, join};
use ruma::{EventId, RoomId, ServerName}; use ruma::{
CanonicalJsonObject, EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, ServerName,
UserId, events::room::member::MembershipState, room_version_rules::RoomVersionRules,
};
pub(super) struct AccessCheck<'a> { pub(super) struct AccessCheck<'a> {
pub(super) services: &'a Services, pub(super) services: &'a Services,
@@ -63,3 +66,103 @@ pub(super) async fn assert(&self) -> Result {
Ok(()) Ok(())
} }
} }
/// Performs validation on a membership event that should be run on any event a
/// remote is trying to send via us.
///
/// ## Checks performed
///
/// 1. PDU room ID matches request path room ID
/// 2. PDU event ID matches request path event ID
/// 3. Signature check
/// 4. Event type check
/// 5. `sender` field presence (and parsing)
/// 6. `state_key` field presence (and parsing)
/// 7. PDU room format check (PDU check 1)
///
/// ## Returns
///
/// A resulting tuple of (PDU JSON, target membership state, sender, recipient).
pub(crate) async fn validate_any_membership_event(
services: &crate::State,
body: &serde_json::value::RawValue,
room_version_rules: &RoomVersionRules,
create_event_id: OwnedEventId,
expected_room_id: OwnedRoomId,
expected_event_id: OwnedEventId,
) -> Result<(CanonicalJsonObject, MembershipState, OwnedUserId, OwnedUserId)> {
let (template_room_id, template_event_id, pdu) = services
.rooms
.event_handler
.parse_incoming_pdu(body, Some(room_version_rules))
.await
.map_err(|e| err!(Request(BadJson("Invalid membership PDU: {e}"))))?;
if template_room_id != expected_room_id {
return Err!(Request(InvalidParam("Membership event does not belong to requested room")));
}
if template_event_id != expected_event_id {
return Err!(Request(InvalidParam(debug_warn!(
%template_event_id,
%expected_event_id,
"Membership event ID does not match provided event ID"
))));
}
services
.server_keys
.verify_event(&pdu, room_version_rules)
.await
.map_err(|e| {
err!(Request(InvalidParam("Signature verification failed on membership event: {e}")))
})?;
// Ensure this is a membership event
if pdu
.get("type")
.expect("event must have a type")
.as_str()
.expect("type must be a string")
!= "m.room.member"
{
return Err!(Request(BadJson(
"Not allowed to send non-membership event to this endpoint"
)));
}
let membership = pdu
.get("content")
.ok_or_else(|| err!(Request(BadJson("Event missing content property"))))?
.as_object()
.ok_or_else(|| err!(Request(BadJson("Event content is not an object"))))?
.get("membership")
.ok_or_else(|| err!(Request(BadJson("Event missing membership property"))))?
.as_str()
.ok_or_else(|| err!(Request(BadJson("Event is not a string"))))?
.to_owned();
let sender_user = pdu
.get("sender")
.and_then(|v| v.as_str())
.map(UserId::parse)
.and_then(Result::ok)
.ok_or_else(|| err!(Request(InvalidParam("Invalid sender property"))))?;
let recipient_user = pdu
.get("state_key")
.and_then(|v| v.as_str())
.map(UserId::parse)
.and_then(Result::ok)
.ok_or_else(|| err!(Request(InvalidParam("Invalid state_key property"))))?;
// Do a quick format check. The spec doesn't suggest this, but it's probably
// a good idea nonetheless.
service::rooms::event_handler::Service::pdu_format_check_1(
&pdu,
room_version_rules,
&create_event_id,
)
.map_err(|e| {
err!(Request(InvalidParam("Membership event violates the room event format: {e}")))
})?;
Ok((pdu, membership.into(), sender_user, recipient_user))
}
+1
View File
@@ -2442,6 +2442,7 @@ pub struct OauthConfig {
/// legacy authentication will be unable to log in. /// legacy authentication will be unable to log in.
/// ///
/// default: "hybrid" /// default: "hybrid"
#[serde(default)]
compatibility_mode: OAuthMode, compatibility_mode: OAuthMode,
/// display: hidden /// display: hidden
+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 ruma::events::room::message::RoomMessageEventContent;
use tokio::time::{Duration, sleep}; use tokio::time::{Duration, sleep};
@@ -7,10 +9,16 @@
pub(super) const SIGNAL: &str = "SIGUSR2"; pub(super) const SIGNAL: &str = "SIGUSR2";
impl super::Service { 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) { pub(super) async fn console_auto_start(&self) {
#[cfg(feature = "console")] #[cfg(feature = "console")]
if self.services.server.config.admin_console_automatic { 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 // Allow more of the startup sequence to execute before spawning
tokio::task::yield_now().await; tokio::task::yield_now().await;
self.console.start().await; self.console.start().await;
+1 -1
View File
@@ -134,7 +134,7 @@ async fn check(&self) -> Result<()> {
let response = self let response = self
.services .services
.client .client
.default .external_resource
.get(CHECK_FOR_ANNOUNCEMENTS_URL) .get(CHECK_FOR_ANNOUNCEMENTS_URL)
.send() .send()
.await? .await?
+14 -19
View File
@@ -16,9 +16,9 @@ pub struct Service {
pub matrix_resolver: Arc<MatrixResolver>, pub matrix_resolver: Arc<MatrixResolver>,
pub dns_resolver: Arc<TokioResolver>, pub dns_resolver: Arc<TokioResolver>,
pub default: reqwest::Client, pub dns: reqwest::Client,
pub url_preview: reqwest::Client, pub url_preview: reqwest::Client,
pub extern_media: reqwest::Client, pub external_resource: reqwest::Client,
pub federation: reqwest::Client, pub federation: reqwest::Client,
pub federation_slow: reqwest::Client, pub federation_slow: reqwest::Client,
pub sender: reqwest::Client, pub sender: reqwest::Client,
@@ -35,17 +35,17 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
let dns_resolver = get_dns_resolver(args.server)?; let dns_resolver = get_dns_resolver(args.server)?;
let dns_client = base(&args.server.config)?
.connect_timeout(Duration::from_secs(args.server.config.well_known_conn_timeout))
.read_timeout(Duration::from_secs(args.server.config.well_known_timeout))
.timeout(Duration::from_secs(args.server.config.well_known_timeout))
.pool_max_idle_per_host(0)
.redirect(redirect::Policy::limited(4))
.build()?;
let matrix_resolver = Arc::new(MatrixResolverBuilder::new() let matrix_resolver = Arc::new(MatrixResolverBuilder::new()
.dangerous_tls_accept_invalid_certs(args.server.config.allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure) .dangerous_tls_accept_invalid_certs(args.server.config.allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure)
.http_client( .http_client(dns_client.clone())
base(&args.server.config)?
.connect_timeout(Duration::from_secs(args.server.config.well_known_conn_timeout))
.read_timeout(Duration::from_secs(args.server.config.well_known_timeout))
.timeout(Duration::from_secs(args.server.config.well_known_timeout))
.pool_max_idle_per_host(0)
.redirect(redirect::Policy::limited(4))
.build()?
)
.dns_resolver(dns_resolver.clone()) .dns_resolver(dns_resolver.clone())
.build()?); .build()?);
let matrix_dns_resolver = matrix_resolver.create_dns_resolver(); let matrix_dns_resolver = matrix_resolver.create_dns_resolver();
@@ -69,23 +69,19 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
matrix_resolver, matrix_resolver,
dns_resolver, dns_resolver,
default: base(config)? dns: dns_client,
.dns_resolver(matrix_dns_resolver.clone())
.build()?,
url_preview: base(config) url_preview: base(config)
.and_then(|builder| { .and_then(|builder| {
builder_interface(builder, url_preview_bind_iface.as_deref()) builder_interface(builder, url_preview_bind_iface.as_deref())
})? })?
.local_address(url_preview_bind_addr) .local_address(url_preview_bind_addr)
.dns_resolver(matrix_dns_resolver.clone())
.timeout(Duration::from_secs(config.url_preview_timeout)) .timeout(Duration::from_secs(config.url_preview_timeout))
.redirect(redirect::Policy::limited(3)) .redirect(redirect::Policy::limited(3))
.user_agent(url_preview_user_agent) .user_agent(url_preview_user_agent)
.build()?, .build()?,
extern_media: base(config)? external_resource: base(config)?
.dns_resolver(matrix_dns_resolver.clone())
.redirect(redirect::Policy::limited(3)) .redirect(redirect::Policy::limited(3))
.build()?, .build()?,
@@ -128,7 +124,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
.build()?, .build()?,
appservice: base(config)? appservice: base(config)?
.dns_resolver(matrix_dns_resolver.clone()) .dns_resolver(matrix_dns_resolver)
.connect_timeout(Duration::from_secs(5)) .connect_timeout(Duration::from_secs(5))
.read_timeout(Duration::from_secs(config.appservice_timeout)) .read_timeout(Duration::from_secs(config.appservice_timeout))
.timeout(Duration::from_secs(config.appservice_timeout)) .timeout(Duration::from_secs(config.appservice_timeout))
@@ -138,7 +134,6 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
.build()?, .build()?,
pusher: base(config)? pusher: base(config)?
.dns_resolver(matrix_dns_resolver)
.connect_timeout(Duration::from_secs(config.pusher_conn_timeout)) .connect_timeout(Duration::from_secs(config.pusher_conn_timeout))
.timeout(Duration::from_secs(config.pusher_timeout)) .timeout(Duration::from_secs(config.pusher_timeout))
.pool_max_idle_per_host(1) .pool_max_idle_per_host(1)
+1 -1
View File
@@ -260,7 +260,7 @@ async fn location_request(&self, location: &str) -> Result<FileMeta> {
let response = self let response = self
.services .services
.client .client
.extern_media .external_resource
.get(location) .get(location)
.send() .send()
.await?; .await?;
+4 -2
View File
@@ -829,7 +829,7 @@ async fn fix_local_invite_state(services: &Services) -> Result {
&& services.globals.user_is_local(&membership_event.sender) { && services.globals.user_is_local(&membership_event.sender) {
// build and save stripped state for their invite in the database // build and save stripped state for their invite in the database
let stripped_state = services.rooms.state.summary_stripped(&membership_event, &room_id, &user_id).await; let stripped_state = services.rooms.state.summary_stripped(&membership_event, &room_id, &user_id, false).await;
userroomid_invitestate.put((&user_id, &room_id), Json(stripped_state)); userroomid_invitestate.put((&user_id, &room_id), Json(stripped_state));
fixed = fixed.saturating_add(1); fixed = fixed.saturating_add(1);
} }
@@ -882,7 +882,7 @@ async fn split_userid_password(services: &Services) -> Result {
drop(cork); drop(cork);
info!(?remote_users, "Split userid_password."); info!(?remote_users, "Split userid_password.");
db["global"].insert(FIXED_LOCAL_INVITE_STATE_MARKER, []); db["global"].insert(SPLIT_USERID_PASSWORD, []);
db.db.sort()?; db.db.sort()?;
Ok(()) Ok(())
} }
@@ -895,5 +895,7 @@ async fn obliterate_roomsynctoken_shortstatehash_with_extreme_prejudice(
info!("Cleared roomsynctoken_shortstatehash."); info!("Cleared roomsynctoken_shortstatehash.");
services.db["global"].insert(DROP_ROOMSYNCTOKEN_SHORTSTATEHASH, []);
Ok(()) Ok(())
} }
+7 -9
View File
@@ -25,7 +25,7 @@
OAuthError, ResponseMode, Scope, TokenRequest, TokenResponse, TokenType, OAuthError, ResponseMode, Scope, TokenRequest, TokenResponse, TokenType,
}, },
}, },
users, users::{self, DeviceToken},
}; };
pub mod client_metadata; pub mod client_metadata;
@@ -343,7 +343,7 @@ async fn create_session(
client_name: Option<String>, client_name: Option<String>,
client_id: String, client_id: String,
) -> Result<TokenResponse, OAuthError> { ) -> 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 refresh_token = Self::generate_token();
let device_id = requested_scopes let device_id = requested_scopes
@@ -376,8 +376,7 @@ async fn create_session(
.create_device( .create_device(
&authorizing_user, &authorizing_user,
device_id, device_id,
&access_token, Some(access_token.clone()),
Some(Self::ACCESS_TOKEN_MAX_AGE),
client_name, client_name,
None, None,
) )
@@ -413,7 +412,7 @@ async fn create_session(
); );
Ok(TokenResponse { Ok(TokenResponse {
access_token, access_token: access_token.into_token(),
token_type: TokenType::Bearer, token_type: TokenType::Bearer,
expires_in: Self::ACCESS_TOKEN_MAX_AGE.as_secs(), expires_in: Self::ACCESS_TOKEN_MAX_AGE.as_secs(),
scope: requested_scopes.iter().join(" "), 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"); 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 new_refresh_token = Self::generate_token();
let scope = session_info.scopes.iter().join(" "); let scope = session_info.scopes.iter().join(" ");
session_info session_info
@@ -461,8 +460,7 @@ async fn refresh_session(
.set_token( .set_token(
&refresh_token_info.user_id, &refresh_token_info.user_id,
&refresh_token_info.device_id, &refresh_token_info.device_id,
&new_access_token, new_access_token.clone(),
Some(Self::ACCESS_TOKEN_MAX_AGE),
) )
.await .await
.expect("should be able to set token"); .expect("should be able to set token");
@@ -479,7 +477,7 @@ async fn refresh_session(
.raw_put(&new_refresh_token, Json(refresh_token_info)); .raw_put(&new_refresh_token, Json(refresh_token_info));
Ok(TokenResponse { Ok(TokenResponse {
access_token: new_access_token, access_token: new_access_token.into_token(),
token_type: TokenType::Bearer, token_type: TokenType::Bearer,
expires_in: Self::ACCESS_TOKEN_MAX_AGE.as_secs(), expires_in: Self::ACCESS_TOKEN_MAX_AGE.as_secs(),
scope, scope,
+1
View File
@@ -151,6 +151,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
config: config.clone(), config: config.clone(),
client_secret: if let Some(client_secret_file) = &config.client_secret_file { client_secret: if let Some(client_secret_file) = &config.client_secret_file {
std::fs::read_to_string(client_secret_file) std::fs::read_to_string(client_secret_file)
.map(|client_secret| client_secret.trim().to_owned())
.map(ClientSecret::new) .map(ClientSecret::new)
.map_err(|err| err!("Failed to read OIDC client secret file: {err}"))? .map_err(|err| err!("Failed to read OIDC client secret file: {err}"))?
} else if let Some(client_secret) = &config.client_secret { } else if let Some(client_secret) = &config.client_secret {
@@ -241,7 +241,7 @@ pub async fn get_missing_events(
latest_events.clear(); latest_events.clear();
for raw_event in response.events { for raw_event in response.events {
let (_, event_id, pdu_json) = self.parse_incoming_pdu(&raw_event).await?; let (_, event_id, pdu_json) = self.parse_incoming_pdu(&raw_event, None).await?;
let pdu = PduEvent::from_id_val(&event_id, pdu_json).map_err(|e| { let pdu = PduEvent::from_id_val(&event_id, pdu_json).map_err(|e| {
err!(Request(BadJson("Failed to parse gapfilled event {event_id}: {e}"))) err!(Request(BadJson("Failed to parse gapfilled event {event_id}: {e}")))
})?; })?;
@@ -384,8 +384,9 @@ async fn fetch_event_via(
.send_federation_request(&remote, get_event::v1::Request::new(event_id.clone())) .send_federation_request(&remote, get_event::v1::Request::new(event_id.clone()))
.await?; .await?;
let (calculated_event_id, value) = let (_, calculated_event_id, value) = self
Self::parse_incoming_pdu_with_known_room(&res.pdu, room_version_rules)?; .parse_incoming_pdu(&res.pdu, Some(room_version_rules))
.await?;
if calculated_event_id != event_id { if calculated_event_id != event_id {
Err!(Request(BadJson(warn!( Err!(Request(BadJson(warn!(
+10 -8
View File
@@ -48,14 +48,16 @@ pub(super) async fn fetch_and_persist_event_auth<Pdu>(
let mut auth_chain_map = HashMap::with_capacity(event_auth.auth_chain.len()); let mut auth_chain_map = HashMap::with_capacity(event_auth.auth_chain.len());
for auth_pdu_json in event_auth.auth_chain { for auth_pdu_json in event_auth.auth_chain {
let (auth_event_room_id, auth_event_id, auth_pdu_json) = let (auth_event_room_id, auth_event_id, auth_pdu_json) = match self
match self.parse_incoming_pdu(&auth_pdu_json).await { .parse_incoming_pdu(&auth_pdu_json, Some(room_version_rules))
| Ok(parsed) => parsed, .await
| Err(e) => { {
warn!(error=?e, "Dropping auth chain event as it could not be parsed"); | Ok(parsed) => parsed,
continue; | Err(e) => {
}, warn!(error=?e, "Dropping auth chain event as it could not be parsed");
}; continue;
},
};
if let Err(e) = Self::pdu_format_check_1( if let Err(e) = Self::pdu_format_check_1(
&auth_pdu_json, &auth_pdu_json,
room_version_rules, room_version_rules,
@@ -314,7 +314,7 @@ pub(super) async fn fetch_full_state(
.iter() .iter()
.stream() .stream()
.broad_filter_map(|raw_event_json| async { .broad_filter_map(|raw_event_json| async {
if let Some(parsed) = self.parse_incoming_pdu(raw_event_json).await.ok() if let Some(parsed) = self.parse_incoming_pdu(raw_event_json, None).await.ok()
&& parsed.0 == room_id && parsed.0 == room_id
{ {
Some(parsed) Some(parsed)
@@ -351,7 +351,7 @@ pub(super) async fn fetch_full_state(
.iter() .iter()
.stream() .stream()
.broad_filter_map(|raw_event_json| async { .broad_filter_map(|raw_event_json| async {
if let Some(parsed) = self.parse_incoming_pdu(raw_event_json).await.ok() if let Some(parsed) = self.parse_incoming_pdu(raw_event_json, None).await.ok()
&& parsed.0 == room_id && parsed.0 == room_id
{ {
Some(parsed) Some(parsed)
@@ -14,10 +14,12 @@ impl super::Service {
/// This performs steps 1 through 4 of [S-S section 5.1][spec], returning /// This performs steps 1 through 4 of [S-S section 5.1][spec], returning
/// the parsed PDU and modified JSON object. /// the parsed PDU and modified JSON object.
/// ///
/// **External callers likely want `handle_incoming_pdu` instead.**
///
/// [spec]: https://spec.matrix.org/v1.19/server-server-api/#checks-performed-on-receipt-of-a-pdu /// [spec]: https://spec.matrix.org/v1.19/server-server-api/#checks-performed-on-receipt-of-a-pdu
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[tracing::instrument(name = "handle_outlier", skip_all)] #[tracing::instrument(name = "handle_outlier", skip_all)]
pub(super) async fn handle_outlier_pdu<'a, Pdu>( pub async fn handle_outlier_pdu<'a, Pdu>(
&self, &self,
origin: &'a ServerName, origin: &'a ServerName,
create_event: &'a Pdu, create_event: &'a Pdu,
@@ -54,7 +56,6 @@ pub(super) async fn handle_outlier_pdu<'a, Pdu>(
})?; })?;
value.remove("unsigned"); value.remove("unsigned");
let room_version_rules = get_room_version_rules(create_event)?;
// 2. Check signatures, otherwise drop. // 2. Check signatures, otherwise drop.
// 3. Check content hash, redacting the event if it fails. // 3. Check content hash, redacting the event if it fails.
@@ -82,25 +82,14 @@ pub(super) fn expect_event_id_array(
} }
impl super::Service { impl super::Service {
/// Parses an incoming PDU JSON object, generating an event ID for it. Does
/// not insert the event ID into the returned object. Does not discover the
/// room ID.
pub(super) fn parse_incoming_pdu_with_known_room(
pdu: &RawJsonValue,
room_version_rules: &RoomVersionRules,
) -> Result<(OwnedEventId, CanonicalJsonObject)> {
let (event_id, value) =
gen_event_id_canonical_json(pdu, room_version_rules).map_err(|e| {
err!(Request(InvalidParam("Could not convert event to canonical json: {e}")))
})?;
// NOTE: validation checks are now performed by `pdu_format_check_1`.
Ok((event_id, value))
}
/// Parses an incoming PDU JSON object, generating an event ID for it and /// Parses an incoming PDU JSON object, generating an event ID for it and
/// attempts to discover the associated room ID. Does not insert the event /// attempts to discover the associated room ID. Does not insert the event
/// ID into the returned object. /// ID into the returned object.
pub async fn parse_incoming_pdu(&self, pdu: &RawJsonValue) -> Result<Parsed> { pub async fn parse_incoming_pdu(
&self,
pdu: &RawJsonValue,
room_version_rules: Option<&RoomVersionRules>,
) -> Result<Parsed> {
let value = serde_json::from_str::<CanonicalJsonObject>(pdu.get()).map_err(|e| { let value = serde_json::from_str::<CanonicalJsonObject>(pdu.get()).map_err(|e| {
err!(BadServerResponse(debug_warn!("Error parsing incoming event {e:?}"))) err!(BadServerResponse(debug_warn!("Error parsing incoming event {e:?}")))
})?; })?;
@@ -111,17 +100,20 @@ pub async fn parse_incoming_pdu(&self, pdu: &RawJsonValue) -> Result<Parsed> {
let room_id = extract_room_id(event_type, &value)?; let room_id = extract_room_id(event_type, &value)?;
let room_version_rules = self let room_version_rules = match room_version_rules {
.services | Some(r) => r,
.state | None => &self
.get_room_version(&room_id) .services
.await .state
.unwrap_or(RoomVersionId::V1) .get_room_version(&room_id)
.rules() .await
.unwrap(); .unwrap_or(RoomVersionId::V1)
.rules()
.expect("room version must be supported"),
};
let (event_id, value) = let (event_id, value) =
gen_event_id_canonical_json(pdu, &room_version_rules).map_err(|e| { gen_event_id_canonical_json(pdu, room_version_rules).map_err(|e| {
err!(Request(InvalidParam("Could not convert event to canonical json: {e}"))) err!(Request(InvalidParam("Could not convert event to canonical json: {e}")))
})?; })?;
// NOTE: validation checks are now performed by `pdu_format_check_1`. // NOTE: validation checks are now performed by `pdu_format_check_1`.
+27 -8
View File
@@ -6,8 +6,9 @@
}; };
use futures::future::ready; use futures::future::ready;
use ruma::{ use ruma::{
CanonicalJsonObject, EventId, OwnedEventId, ServerName, api::error::ErrorKind, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, ServerName,
canonical_json::redact, events::StateEventType, room_version_rules::RoomVersionRules, api::error::ErrorKind, canonical_json::redact, events::StateEventType,
room_version_rules::RoomVersionRules,
}; };
use crate::rooms::{ use crate::rooms::{
@@ -18,7 +19,7 @@ impl super::Service {
/// Checks that the PDU conforms to the PDU format (check 1). This is /// Checks that the PDU conforms to the PDU format (check 1). This is
/// already mostly done during deserialisation, so this function just checks /// already mostly done during deserialisation, so this function just checks
/// that the PDU isn't a too large. /// that the PDU isn't a too large.
pub(super) fn pdu_format_check_1( pub fn pdu_format_check_1(
pdu_json: &CanonicalJsonObject, pdu_json: &CanonicalJsonObject,
room_version_rules: &RoomVersionRules, room_version_rules: &RoomVersionRules,
create_event_id: &EventId, create_event_id: &EventId,
@@ -42,9 +43,27 @@ pub(super) fn pdu_format_check_1(
return Err!(Request(BadJson("PDU has too many auth events"))); 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); // The m.room.create event is the genesis event and has empty auth_events
if !event_format.allow_room_create_in_auth_events && create_event_in_auth_events { // by definition, so it is exempt from the checks below requiring or
return Err!(Request(BadJson("PDU references a create event"))); // 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")?; let prev_events = expect_event_id_array(pdu_json, "prev_events")?;
@@ -59,7 +78,7 @@ pub(super) fn pdu_format_check_1(
/// the content hash verification fails (check 3), returning the /// the content hash verification fails (check 3), returning the
/// potentially modified JSON. Returns an error if the PDU cannot be /// potentially modified JSON. Returns an error if the PDU cannot be
/// redacted, or fails signature verification. /// redacted, or fails signature verification.
pub(super) async fn signature_hash_check_2_3( pub async fn signature_hash_check_2_3(
&self, &self,
pdu_json: CanonicalJsonObject, pdu_json: CanonicalJsonObject,
room_version_rules: &RoomVersionRules, room_version_rules: &RoomVersionRules,
@@ -92,7 +111,7 @@ pub(super) async fn signature_hash_check_2_3(
/// If the auth check fails, false is returned, otherwise true. /// If the auth check fails, false is returned, otherwise true.
/// ///
/// [spec]: https://spec.matrix.org/v1.19/server-server-api/#checks-performed-on-receipt-of-a-pdu /// [spec]: https://spec.matrix.org/v1.19/server-server-api/#checks-performed-on-receipt-of-a-pdu
pub(super) async fn auth_state_check_4( pub async fn auth_state_check_4(
&self, &self,
pdu: &PduEvent, pdu: &PduEvent,
room_version_rules: &RoomVersionRules, room_version_rules: &RoomVersionRules,
+18 -3
View File
@@ -1,7 +1,7 @@
use std::{collections::HashMap, fmt::Write, sync::Arc}; use std::{collections::HashMap, fmt::Write, sync::Arc};
use async_trait::async_trait; use async_trait::async_trait;
use conduwuit::debug; use conduwuit::{debug, utils::stream::WidebandExt};
use conduwuit_core::{ use conduwuit_core::{
Event, PduEvent, Result, err, Event, PduEvent, Result, err,
result::FlatOk, result::FlatOk,
@@ -30,7 +30,7 @@
short::{ShortEventId, ShortStateHash}, short::{ShortEventId, ShortStateHash},
state_compressor::{CompressedState, parse_compressed_state_event}, state_compressor::{CompressedState, parse_compressed_state_event},
}, },
sync, sending, sync,
}; };
pub struct Service { pub struct Service {
@@ -45,6 +45,7 @@ struct Services {
state_cache: Dep<rooms::state_cache::Service>, state_cache: Dep<rooms::state_cache::Service>,
state_accessor: Dep<rooms::state_accessor::Service>, state_accessor: Dep<rooms::state_accessor::Service>,
state_compressor: Dep<rooms::state_compressor::Service>, state_compressor: Dep<rooms::state_compressor::Service>,
sending: Dep<sending::Service>,
sync: Dep<sync::Service>, sync: Dep<sync::Service>,
timeline: Dep<rooms::timeline::Service>, timeline: Dep<rooms::timeline::Service>,
} }
@@ -71,6 +72,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"), .depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
state_compressor: args state_compressor: args
.depend::<rooms::state_compressor::Service>("rooms::state_compressor"), .depend::<rooms::state_compressor::Service>("rooms::state_compressor"),
sending: args.depend::<sending::Service>("sending"),
sync: args.depend::<sync::Service>("sync"), sync: args.depend::<sync::Service>("sync"),
timeline: args.depend::<rooms::timeline::Service>("rooms::timeline"), timeline: args.depend::<rooms::timeline::Service>("rooms::timeline"),
}, },
@@ -307,6 +309,7 @@ pub async fn summary_stripped(
event: &PduEvent, event: &PduEvent,
room_id: &RoomId, room_id: &RoomId,
target_user: &UserId, target_user: &UserId,
federation: bool,
) -> Vec<RawStrippedState> { ) -> Vec<RawStrippedState> {
let mut state_events = [ let mut state_events = [
(&StateEventType::RoomCreate, ""), (&StateEventType::RoomCreate, ""),
@@ -343,8 +346,20 @@ pub async fn summary_stripped(
.await .await
.into_iter() .into_iter()
.filter_map(Result::ok) .filter_map(Result::ok)
.map(|pdu| RawStrippedState::Pdu(serde_json::value::to_raw_value(&pdu).unwrap())) .stream()
.wide_then(async |pdu| {
let formatted = if federation {
self.services
.sending
.convert_to_outgoing_federation_event(pdu.to_canonical_object())
.await
} else {
serde_json::value::to_raw_value(&pdu).unwrap()
};
RawStrippedState::Pdu(formatted)
})
.collect() .collect()
.await
} }
/// Set the state hash to a new version, but does not update state_cache. /// Set the state hash to a new version, but does not update state_cache.
+1 -1
View File
@@ -114,7 +114,7 @@ pub async fn update_membership(
let invite_state = if is_local { let invite_state = if is_local {
self.services self.services
.state .state
.summary_stripped(pdu, room_id, user_id) .summary_stripped(pdu, room_id, user_id, false)
.await .await
} else { } else {
vec![] vec![]
+5 -2
View File
@@ -166,8 +166,11 @@ pub async fn get_remote_pdu(&self, room_id: &RoomId, event_id: &EventId) -> Resu
/// Backfills a single PDU. /// Backfills a single PDU.
#[tracing::instrument(skip(self, pdu), level = "debug")] #[tracing::instrument(skip(self, pdu), level = "debug")]
pub async fn backfill_pdu(&self, origin: &ServerName, pdu: Box<RawJsonValue>) -> Result<()> { pub async fn backfill_pdu(&self, origin: &ServerName, pdu: Box<RawJsonValue>) -> Result<()> {
let (room_id, event_id, value) = let (room_id, event_id, value) = self
self.services.event_handler.parse_incoming_pdu(&pdu).await?; .services
.event_handler
.parse_incoming_pdu(&pdu, None)
.await?;
// Lock so we cannot backfill the same pdu twice at the same time // Lock so we cannot backfill the same pdu twice at the same time
let mutex_lock = self let mutex_lock = self
+15
View File
@@ -308,6 +308,21 @@ pub async fn create_hash_and_sign_event(
| _ => Err!(Request(Unknown(warn!("Signing event failed: {e}")))), | _ => Err!(Request(Unknown(warn!("Signing event failed: {e}")))),
}; };
} }
// Evil hack because only the PDU JSON gets signed
pdu.hashes = serde_json::from_value(serde_json::to_value(
pdu_json
.get("hashes")
.expect("must have hashes after signing")
.clone(),
)?)?;
pdu.signatures = serde_json::from_value(serde_json::to_value(
pdu_json
.get("signatures")
.expect("must have signatures after signing")
.clone(),
)?)?;
// Generate event id // Generate event id
pdu.event_id = gen_event_id(&pdu_json, &room_version_rules)?; pdu.event_id = gen_event_id(&pdu_json, &room_version_rules)?;
pdu_json pdu_json
-1
View File
@@ -41,7 +41,6 @@ pub async fn set_dehydrated_device(&self, user_id: &UserId, request: Request) ->
self.create_device( self.create_device(
user_id, user_id,
&request.device_id, &request.device_id,
"",
None, None,
request.initial_device_display_name.clone(), request.initial_device_display_name.clone(),
None, None,
+38 -29
View File
@@ -11,20 +11,44 @@
use futures::{Stream, StreamExt}; use futures::{Stream, StreamExt};
use ruma::{ use ruma::{
DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, UserId, 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 serde_json::json;
use crate::users::increment; 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 { impl super::Service {
/// Adds a new device to a user. /// 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( pub async fn create_device(
&self, &self,
user_id: &UserId, user_id: &UserId,
device_id: &DeviceId, device_id: &DeviceId,
token: &str, token: Option<DeviceToken>,
token_max_age: Option<Duration>,
initial_device_display_name: Option<String>, initial_device_display_name: Option<String>,
client_ip: Option<String>, client_ip: Option<String>,
) -> Result<()> { ) -> Result<()> {
@@ -38,8 +62,12 @@ pub async fn create_device(
increment(&self.db.userid_devicelistversion, user_id.as_bytes()); increment(&self.db.userid_devicelistversion, user_id.as_bytes());
self.db.userdeviceid_metadata.put(key, Json(device)); 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. /// 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() 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. /// Replaces the access token of one device.
pub async fn set_token( pub async fn set_token(
&self, &self,
user_id: &UserId, user_id: &UserId,
device_id: &DeviceId, device_id: &DeviceId,
token: &str, DeviceToken { token, max_age }: DeviceToken,
token_max_age: Option<Duration>,
) -> Result<()> { ) -> Result<()> {
let key = (user_id, device_id); let key = (user_id, device_id);
if self.db.userdeviceid_metadata.qry(&key).await.is_err() { if self.db.userdeviceid_metadata.qry(&key).await.is_err() {
@@ -136,7 +145,7 @@ pub async fn set_token(
if self if self
.services .services
.appservice .appservice
.find_from_token(token) .find_from_token(&token)
.await .await
.is_ok() .is_ok()
{ {
@@ -153,10 +162,10 @@ pub async fn set_token(
} }
// Assign token to user device combination // Assign token to user device combination
self.db.userdeviceid_token.put_raw(key, token); self.db.userdeviceid_token.put_raw(key, &token);
self.db.token_userdeviceid.raw_put(token, key); 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() let expires = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.expect("system time should not be before the epoch") .expect("system time should not be before the epoch")
+1
View File
@@ -14,6 +14,7 @@
utils::{self}, utils::{self},
}; };
use database::Map; use database::Map;
pub use device::DeviceToken;
pub use profile::ProfileFieldChange; pub use profile::ProfileFieldChange;
use ruma::{UserId, api::error::ErrorKind, encryption::CrossSigningKey, serde::Raw}; use ruma::{UserId, api::error::ErrorKind, encryption::CrossSigningKey, serde::Raw};
use serde::{Deserialize, Serialize}; 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 => { | AccountManagementAction::DeviceView => {
let Some(device_id) = query.device_id else { let Some(device_id) = query.device_id else {