Compare commits

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

Fixes a failure in matrix-dart-sdk
2026-07-14 11:00:34 +02:00
N00byKing e52bbeaf94 fix: Correct link for code style guide in CONTRIBUTING.md 2026-07-14 08:59:08 +00:00
Renovate Bot ad1c194fb0 chore(deps): update github-actions-digest 2026-07-14 05:01:52 +00:00
GingerandEllis Git 5e2dcd8e79 chore: News fragment 2026-07-13 20:38:10 +00:00
GingerandEllis Git 24c6474bd1 fix: Allow unstable and stable device id query together 2026-07-13 20:38:10 +00:00
gingerandEllis Git 642d05048b chore: announce 2026-07-13 18:54:04 +00:00
Koen OostveenandEllis Git 446ae93ad7 fix(web): Change incorrect deeplink for deleting a device 2026-07-13 13:45:46 +00:00
Renovate BotandEllis Git 9ca0774b2d chore(deps): lock file maintenance 2026-07-13 13:44:47 +00:00
Ginger ce494eb0c0 fix: Properly sync newly created rooms 2026-07-13 08:50:53 -04:00
Ginger 1f5e178c3f fix: Properly handle appservice device creation 2026-07-13 08:50:48 -04:00
Renovate Bot ddd050d402 chore(deps): update ghcr.io/renovatebot/renovate docker tag to v43.260.2 2026-07-13 05:03:24 +00:00
Ginger 6ad3e679bc chore: Release 2026-07-12 17:34:16 -04:00
Ginger 65812bb246 chore: Update changelog 2026-07-12 17:33:52 -04:00
theS1LV3R 9f3a7994c7 chore: Changelog 2026-07-12 20:22:19 +02:00
theS1LV3R baef3289fe fix(service/admin): Prevent console from being spawned when no TTY is available 2026-07-12 20:22:18 +02:00
47 changed files with 448 additions and 922 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
+9
View File
@@ -1,3 +1,12 @@
# Continuwuity 26.6.2 (2026-07-12)
## Bugfixes
- Fixed the server returning 500 errors if `admin_console_automatic` is enabled and no TTY is available. Contributed by @s1lv3r. (#1975)
- Fixed `global.oauth.compatibility_mode` being required, despite being ignored, when the `[global.oauth.oidc]` config section is provided.
- Fixed an issue with a migration that could cause user accounts imported from an identity provider to be marked as deactivated when the server started. If you have accounts affected by this issue, use `!admin users reset-password --convert-to-local-account` to reactivate them.
# Continuwuity 26.6.1 (2026-07-12) # Continuwuity 26.6.1 (2026-07-12)
## 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
+135 -232
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.1" 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.1" 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.1" 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.1" 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.1" 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.1" 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.1" 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.1" 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.1" 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.1" 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",
@@ -4799,7 +4777,7 @@ dependencies = [
[[package]] [[package]]
name = "ruminuwuity" name = "ruminuwuity"
version = "26.6.1" 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.1" 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"
+1 -1
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.1" version = "26.6.2"
[workspace.metadata.crane] [workspace.metadata.crane]
name = "conduwuit" name = "conduwuit"
-1
View File
@@ -1 +0,0 @@
Added support for the OAuth2 device authorization flow. Contributed by @ginger
+1
View File
@@ -0,0 +1 @@
Appservices are now properly able to create devices for E2EE.
-1
View File
@@ -1 +0,0 @@
Fixed `global.oauth.compatibility_mode` being required, despite being ignored, when the `[global.oauth.oidc]` config section is provided.
-1
View File
@@ -1 +0,0 @@
Fixed an issue with a migration that could cause user accounts imported from an identity provider to be marked as deactivated when the server started. If you have accounts affected by this issue, use `!admin users reset-password --convert-to-local-account` to reactivate them.
+1
View File
@@ -0,0 +1 @@
Fixed newly created rooms failing to sync properly in clients using legacy sync.
+1
View File
@@ -0,0 +1 @@
Appservices may now specify both the unstable and stable `device_id` query parameters in a request. The stable parameter will take priority. Contributed by @ginger.
+1
View File
@@ -0,0 +1 @@
Fixed the deeplink redirect for deleting devices. Contributed by @koen
+1
View File
@@ -0,0 +1 @@
Fix status code for oauth registration. Contributed by @n00byking
+1
View File
@@ -0,0 +1 @@
Exempt m.room.create from auth_events check. Contributed by @eleboucher
@@ -6,10 +6,10 @@
"message": "Welcome to Continuwuity! Important announcements about the project will appear here." "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.1](https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v26.6.1) has just been released! This release contains a bunch of bugfixes for bugs found initially after 26.6.0's release - please read the changelog before updating!" "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"
}, },
+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());
-13
View File
@@ -1,13 +0,0 @@
use axum::{Form, Json, extract::State, response::IntoResponse};
use http::StatusCode;
use service::oauth::grant::DeviceCodeRequest;
pub(crate) async fn device_authorization_route(
State(services): State<crate::State>,
Form(request): Form<DeviceCodeRequest>,
) -> impl IntoResponse {
match services.oauth.request_device_code(request).await {
| Ok(response) => Ok(Json(response)),
| Err(err) => Err((StatusCode::BAD_REQUEST, Json(err))),
}
}
-3
View File
@@ -10,7 +10,6 @@
use serde_json::json; use serde_json::json;
pub(crate) use server_metadata::*; pub(crate) use server_metadata::*;
mod device;
mod register_client; mod register_client;
mod server_metadata; mod server_metadata;
mod token; mod token;
@@ -21,7 +20,6 @@
const CLIENT_REGISTER_PATH: &str = "client/register"; const CLIENT_REGISTER_PATH: &str = "client/register";
const TOKEN_REVOKE_PATH: &str = "client/revoke"; const TOKEN_REVOKE_PATH: &str = "client/revoke";
const TOKEN_PATH: &str = "grant/token"; const TOKEN_PATH: &str = "grant/token";
const DEVICE_AUTHORIZATION_PATH: &str = "device";
const ACCOUNT_MANAGEMENT_PATH: &str = concat!(conduwuit_core::ROUTE_PREFIX, "/account/deeplink"); const ACCOUNT_MANAGEMENT_PATH: &str = concat!(conduwuit_core::ROUTE_PREFIX, "/account/deeplink");
pub(crate) fn router(state: crate::State) -> Router<crate::State> { pub(crate) fn router(state: crate::State) -> Router<crate::State> {
@@ -55,5 +53,4 @@ fn oauth_router() -> Router<crate::State> {
.route(concat!("/", JWKS_URI_PATH), get(async || Json(json!({"keys": []})))) .route(concat!("/", JWKS_URI_PATH), get(async || Json(json!({"keys": []}))))
.route(concat!("/", TOKEN_PATH), post(token::token_route)) .route(concat!("/", TOKEN_PATH), post(token::token_route))
.route(concat!("/", TOKEN_REVOKE_PATH), post(token::revoke_token_route)) .route(concat!("/", TOKEN_REVOKE_PATH), post(token::revoke_token_route))
.route(concat!("/", DEVICE_AUTHORIZATION_PATH), post(device::device_authorization_route))
} }
+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())
} }
+3 -4
View File
@@ -12,8 +12,8 @@
use crate::{ use crate::{
Ruma, Ruma,
client::oauth::{ client::oauth::{
ACCOUNT_MANAGEMENT_PATH, AUTH_CODE_PATH, CLIENT_REGISTER_PATH, DEVICE_AUTHORIZATION_PATH, ACCOUNT_MANAGEMENT_PATH, AUTH_CODE_PATH, CLIENT_REGISTER_PATH, JWKS_URI_PATH, TOKEN_PATH,
JWKS_URI_PATH, TOKEN_PATH, TOKEN_REVOKE_PATH, TOKEN_REVOKE_PATH,
}, },
}; };
@@ -49,8 +49,7 @@ pub(crate) async fn authorization_server_metadata(services: &Services) -> Value
], ],
"authorization_endpoint": endpoint_base.join(AUTH_CODE_PATH).unwrap(), "authorization_endpoint": endpoint_base.join(AUTH_CODE_PATH).unwrap(),
"code_challenge_methods_supported": ["S256"], "code_challenge_methods_supported": ["S256"],
"device_authorization_endpoint": endpoint_base.join(DEVICE_AUTHORIZATION_PATH).unwrap(), "grant_types_supported": ["authorization_code", "refresh_token"],
"grant_types_supported": ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code"],
"issuer": services.config.get_client_domain(), "issuer": services.config.get_client_domain(),
"jwks_uri": endpoint_base.join(JWKS_URI_PATH).unwrap(), "jwks_uri": endpoint_base.join(JWKS_URI_PATH).unwrap(),
"prompt_values_supported": ["create"], "prompt_values_supported": ["create"],
+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,
+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;
-2
View File
@@ -132,8 +132,6 @@ pub enum ApplicationType {
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum GrantType { pub enum GrantType {
AuthorizationCode, AuthorizationCode,
#[serde(rename = "urn:ietf:params:oauth:grant-type:device_code")]
DeviceCode,
RefreshToken, RefreshToken,
} }
+17 -62
View File
@@ -13,7 +13,6 @@
use url::Url; use url::Url;
use super::client_metadata::ResponseType; use super::client_metadata::ResponseType;
use crate::oauth::client_metadata::GrantType;
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AuthorizationCodeQuery { pub struct AuthorizationCodeQuery {
@@ -30,33 +29,6 @@ pub struct AuthorizationCodeQuery {
pub prompt: Option<Prompt>, pub prompt: Option<Prompt>,
} }
#[derive(Deserialize, Serialize)]
pub struct AuthorizationCodeResponse {
pub state: String,
pub code: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeviceCodeRequest {
pub client_id: String,
pub scope: RawScopes,
}
#[derive(Deserialize, Serialize)]
pub struct DeviceCodeResponse {
pub device_code: String,
pub user_code: String,
pub verification_uri: Url,
#[serde(skip_serializing_if = "Option::is_none")]
pub verification_uri_complete: Option<Url>,
pub expires_in: u64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeviceCodeVerifyQuery {
pub user_code: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)] #[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[non_exhaustive] #[non_exhaustive]
@@ -154,29 +126,19 @@ pub struct OAuthError {
impl OAuthError { impl OAuthError {
#[must_use] #[must_use]
pub fn new(error: ErrorCode, error_description: String) -> Self { pub const fn invalid_request(error_description: &'static str) -> Self {
Self { Self {
error, error: ErrorCode::InvalidRequest,
error_description: Cow::Owned(error_description),
}
}
#[must_use]
pub const fn new_static(error: ErrorCode, error_description: &'static str) -> Self {
Self {
error,
error_description: Cow::Borrowed(error_description), error_description: Cow::Borrowed(error_description),
} }
} }
#[must_use]
pub const fn invalid_request(error_description: &'static str) -> Self {
Self::new_static(ErrorCode::InvalidRequest, error_description)
}
#[must_use] #[must_use]
pub const fn invalid_grant(error_description: &'static str) -> Self { pub const fn invalid_grant(error_description: &'static str) -> Self {
Self::new_static(ErrorCode::InvalidGrant, error_description) Self {
error: ErrorCode::InvalidGrant,
error_description: Cow::Borrowed(error_description),
}
} }
} }
@@ -195,43 +157,36 @@ pub enum ErrorCode {
AccessDenied, AccessDenied,
InvalidScope, InvalidScope,
InvalidGrant, InvalidGrant,
InvalidClient,
InvalidClientMetadata, InvalidClientMetadata,
AuthorizationPending,
ExpiredToken,
} }
#[derive(Deserialize)] #[derive(Serialize, Deserialize)]
pub struct TokenRequest { pub struct AuthorizationCodeResponse {
pub client_id: String, pub state: String,
#[serde(flatten)] pub code: String,
pub request: TokenRequestType,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(tag = "grant_type", rename_all = "snake_case")] #[serde(tag = "grant_type", rename_all = "snake_case")]
pub enum TokenRequestType { pub enum TokenRequest {
AuthorizationCode { AuthorizationCode {
code: String, code: String,
redirect_uri: Url, redirect_uri: Url,
client_id: String,
code_verifier: String, code_verifier: String,
}, },
#[serde(rename = "urn:ietf:params:oauth:grant-type:device_code")]
DeviceCode {
device_code: String,
},
RefreshToken { RefreshToken {
client_id: String,
refresh_token: String, refresh_token: String,
}, },
} }
impl TokenRequestType { impl TokenRequest {
#[must_use] #[must_use]
pub fn grant_type(&self) -> GrantType { pub fn client_id(&self) -> &str {
match self { match self {
| Self::AuthorizationCode { .. } => GrantType::AuthorizationCode, | Self::AuthorizationCode { client_id, .. }
| Self::DeviceCode { .. } => GrantType::DeviceCode, | Self::RefreshToken { client_id, .. } => client_id,
| Self::RefreshToken { .. } => GrantType::RefreshToken,
} }
} }
} }
+37 -235
View File
@@ -12,22 +12,20 @@
use database::{Deserialized, Json, Map}; use database::{Deserialized, Json, Map};
use itertools::Itertools; use itertools::Itertools;
use lru_cache::LruCache; use lru_cache::LruCache;
use rand::distr::{Distribution, slice::Choose};
use ruma::{DeviceId, OwnedDeviceId, OwnedUserId, UserId}; use ruma::{DeviceId, OwnedDeviceId, OwnedUserId, UserId};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
use crate::{ use crate::{
Dep, config, Dep,
oauth::{ oauth::{
client_metadata::{ApplicationType, ClientMetadata, ResponseType}, client_metadata::{ApplicationType, ClientMetadata, ResponseType},
grant::{ grant::{
AuthorizationCodeQuery, AuthorizationCodeResponse, CodeChallengeMethod, AuthorizationCodeQuery, AuthorizationCodeResponse, CodeChallengeMethod, ErrorCode,
DeviceCodeRequest, DeviceCodeResponse, ErrorCode, OAuthError, ResponseMode, Scope, OAuthError, ResponseMode, Scope, TokenRequest, TokenResponse, TokenType,
TokenRequest, TokenRequestType, TokenResponse, TokenType,
}, },
}, },
users, users::{self, DeviceToken},
}; };
pub mod client_metadata; pub mod client_metadata;
@@ -37,8 +35,7 @@ pub struct Service {
services: Services, services: Services,
db: Data, db: Data,
tickets: Mutex<HashMap<String, HashMap<OAuthTicket, SystemTime>>>, tickets: Mutex<HashMap<String, HashMap<OAuthTicket, SystemTime>>>,
pending_auth_code_grants: tokio::sync::Mutex<LruCache<String, PendingAuthCodeGrant>>, pending_code_grants: tokio::sync::Mutex<LruCache<String, PendingCodeGrant>>,
pending_device_code_grants: tokio::sync::Mutex<LruCache<String, PendingDeviceCodeGrant>>,
} }
struct Data { struct Data {
@@ -49,7 +46,6 @@ struct Data {
struct Services { struct Services {
users: Dep<users::Service>, users: Dep<users::Service>,
config: Dep<config::Service>,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
@@ -66,7 +62,7 @@ struct RefreshTokenInfo {
device_id: OwnedDeviceId, device_id: OwnedDeviceId,
} }
struct PendingAuthCodeGrant { struct PendingCodeGrant {
authorizing_user: OwnedUserId, authorizing_user: OwnedUserId,
requested_scopes: BTreeSet<Scope>, requested_scopes: BTreeSet<Scope>,
client_name: Option<String>, client_name: Option<String>,
@@ -76,8 +72,12 @@ struct PendingAuthCodeGrant {
requested_at: SystemTime, requested_at: SystemTime,
} }
impl PendingAuthCodeGrant { impl PendingCodeGrant {
const MAX_AGE: Duration = Duration::from_mins(1); const MAX_AGE: Duration = Duration::from_mins(1);
const RANDOM_CODE_LENGTH: usize = 32;
#[must_use]
pub(crate) fn generate_code() -> String { utils::random_string(Self::RANDOM_CODE_LENGTH) }
#[must_use] #[must_use]
pub(crate) fn is_valid_for(&self, client_id: &str) -> bool { pub(crate) fn is_valid_for(&self, client_id: &str) -> bool {
@@ -90,43 +90,6 @@ pub(crate) fn is_valid_for(&self, client_id: &str) -> bool {
} }
} }
struct PendingDeviceCodeGrant {
state: DeviceCodeGrantState,
requested_scopes: BTreeSet<Scope>,
client_name: Option<String>,
client_id: String,
requested_at: SystemTime,
}
enum DeviceCodeGrantState {
Unverified {
user_code: String,
},
Verified {
authorizing_user: OwnedUserId,
},
}
impl PendingDeviceCodeGrant {
const MAX_AGE: Duration = Duration::from_mins(1);
#[must_use]
pub(crate) fn is_valid_for(&self, client_id: &str) -> bool {
let now = SystemTime::now();
self.client_id == client_id
&& now
.duration_since(self.requested_at)
.is_ok_and(|age| age < Self::MAX_AGE)
}
}
pub struct DeviceCodeGrantInfo {
pub device_code: String,
pub client_metadata: ClientMetadata,
pub requested_scopes: BTreeSet<Scope>,
}
/// A time-limited grant for a client to perform some sensitive action. /// A time-limited grant for a client to perform some sensitive action.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum OAuthTicket { pub enum OAuthTicket {
@@ -149,7 +112,6 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
Ok(Arc::new(Self { Ok(Arc::new(Self {
services: Services { services: Services {
users: args.depend::<users::Service>("users"), users: args.depend::<users::Service>("users"),
config: args.depend::<config::Service>("config"),
}, },
db: Data { db: Data {
clientid_clientmetadata: args.db["clientid_clientmetadata"].clone(), clientid_clientmetadata: args.db["clientid_clientmetadata"].clone(),
@@ -157,11 +119,8 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
refreshtoken_refreshtokeninfo: args.db["refreshtoken_refreshtokeninfo"].clone(), refreshtoken_refreshtokeninfo: args.db["refreshtoken_refreshtokeninfo"].clone(),
}, },
tickets: Mutex::default(), tickets: Mutex::default(),
pending_auth_code_grants: tokio::sync::Mutex::new(LruCache::new( pending_code_grants: tokio::sync::Mutex::new(LruCache::new(
Self::MAX_PENDING_GRANTS, Self::MAX_PENDING_CODE_GRANTS,
)),
pending_device_code_grants: tokio::sync::Mutex::new(LruCache::new(
Self::MAX_PENDING_GRANTS,
)), )),
})) }))
} }
@@ -174,21 +133,11 @@ impl Service {
// Maximum number of pending code grants which will be held in memory at once, // Maximum number of pending code grants which will be held in memory at once,
// to prevent unbounded memory use if someone decides to repeatedly reload the // to prevent unbounded memory use if someone decides to repeatedly reload the
// grant page. // grant page.
const MAX_PENDING_GRANTS: usize = 100; const MAX_PENDING_CODE_GRANTS: usize = 100;
const RANDOM_TOKEN_LENGTH: usize = 32; const RANDOM_TOKEN_LENGTH: usize = 32;
const USER_CODE_CHARACTERS: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const USER_CODE_LENGTH: usize = 6;
fn generate_token() -> String { utils::random_string(Self::RANDOM_TOKEN_LENGTH) } fn generate_token() -> String { utils::random_string(Self::RANDOM_TOKEN_LENGTH) }
fn generate_user_code() -> String {
Choose::new(Self::USER_CODE_CHARACTERS)
.unwrap()
.sample_iter(&mut rand::rng())
.take(Self::USER_CODE_LENGTH)
.collect()
}
pub async fn register_client(&self, metadata: &ClientMetadata) -> Result<String, OAuthError> { pub async fn register_client(&self, metadata: &ClientMetadata) -> Result<String, OAuthError> {
metadata.validate().map_err(|error| OAuthError { metadata.validate().map_err(|error| OAuthError {
error: ErrorCode::InvalidClientMetadata, error: ErrorCode::InvalidClientMetadata,
@@ -282,14 +231,14 @@ pub async fn request_authorization_code(
| ResponseMode::Query => '?', | ResponseMode::Query => '?',
}; };
let code = Self::generate_token(); let code = PendingCodeGrant::generate_code();
info!( info!(
client_id = &query.client_id, client_id = &query.client_id,
client_name = &client_metadata.client_name, client_name = &client_metadata.client_name,
?requested_scopes, ?requested_scopes,
?authorizing_user, ?authorizing_user,
"Issuing OAuth authorization code" "Issuing oauth authorization code"
); );
let redirect_uri = format!( let redirect_uri = format!(
@@ -303,7 +252,7 @@ pub async fn request_authorization_code(
.unwrap(), .unwrap(),
); );
let pending_grant = PendingAuthCodeGrant { let pending_grant = PendingCodeGrant {
authorizing_user, authorizing_user,
requested_scopes, requested_scopes,
client_name: client_metadata.client_name, client_name: client_metadata.client_name,
@@ -313,7 +262,7 @@ pub async fn request_authorization_code(
requested_at: SystemTime::now(), requested_at: SystemTime::now(),
}; };
self.pending_auth_code_grants self.pending_code_grants
.lock() .lock()
.await .await
.insert(code, pending_grant); .insert(code, pending_grant);
@@ -321,129 +270,15 @@ pub async fn request_authorization_code(
Ok(redirect_uri) Ok(redirect_uri)
} }
pub async fn request_device_code(
&self,
query: DeviceCodeRequest,
) -> Result<DeviceCodeResponse, OAuthError> {
let Some(client_metadata) = self.get_client_metadata(&query.client_id).await else {
return Err(OAuthError::new_static(ErrorCode::InvalidClient, "Invalid client ID"));
};
let requested_scopes = query
.scope
.to_scopes()
.map_err(|err| OAuthError::new(ErrorCode::InvalidGrant, err))?;
let device_code = Self::generate_token();
let user_code = Self::generate_user_code();
let verification_uri = self
.services
.config
.get_client_domain()
.join(&format!("{}/oauth2/grant/device_code", conduwuit::ROUTE_PREFIX))
.unwrap();
let mut verification_uri_complete = verification_uri.clone();
verification_uri_complete
.query_pairs_mut()
.append_pair("user_code", &user_code);
info!(
client_id = &query.client_id,
client_name = &client_metadata.client_name,
?requested_scopes,
"Issuing OAuth device code"
);
let pending_grant = PendingDeviceCodeGrant {
state: DeviceCodeGrantState::Unverified { user_code: user_code.clone() },
requested_scopes,
client_name: client_metadata.client_name,
client_id: query.client_id,
requested_at: SystemTime::now(),
};
self.pending_device_code_grants
.lock()
.await
.insert(device_code.clone(), pending_grant);
Ok(DeviceCodeResponse {
device_code,
user_code,
verification_uri,
verification_uri_complete: Some(verification_uri_complete),
expires_in: PendingDeviceCodeGrant::MAX_AGE.as_secs(),
})
}
pub async fn grant_info_for_user_code(
&self,
supplied_user_code: &str,
) -> Option<DeviceCodeGrantInfo> {
let pending_grants = self.pending_device_code_grants.lock().await;
let (device_code, grant) = pending_grants
.iter()
.find(|(_, grant)| {
matches!(&grant.state, DeviceCodeGrantState::Unverified { user_code } if user_code == supplied_user_code)
})?;
let client_metadata = self
.get_client_metadata(&grant.client_id)
.await
.expect("client should exist");
Some(DeviceCodeGrantInfo {
device_code: device_code.clone(),
client_metadata,
requested_scopes: grant.requested_scopes.clone(),
})
}
pub async fn validate_device_code(
&self,
authorizing_user: OwnedUserId,
device_code: &str,
) -> Result<(), String> {
let mut pending_grants = self.pending_device_code_grants.lock().await;
let Some(pending_grant) = pending_grants.get_mut(device_code) else {
return Err("Invalid device code".to_owned());
};
match &mut pending_grant.state {
| state @ DeviceCodeGrantState::Unverified { .. } => {
*state = DeviceCodeGrantState::Verified { authorizing_user };
Ok(())
},
| DeviceCodeGrantState::Verified {
authorizing_user: previous_authorizing_user,
} =>
if *previous_authorizing_user == authorizing_user {
Ok(())
} else {
Err("Device code is already verified".to_owned())
},
}
}
pub async fn issue_token(&self, request: TokenRequest) -> Result<TokenResponse, OAuthError> { pub async fn issue_token(&self, request: TokenRequest) -> Result<TokenResponse, OAuthError> {
let TokenRequest { client_id, request } = request;
let Some(client_metadata) = self.get_client_metadata(&client_id).await else {
return Err(OAuthError::new_static(ErrorCode::InvalidClient, "Invalid client ID"));
};
if !client_metadata.grant_types.contains(&request.grant_type()) {
return Err(OAuthError::invalid_grant("Client cannot request this grant type"));
}
match request { match request {
| TokenRequestType::AuthorizationCode { code, redirect_uri, code_verifier } => { | TokenRequest::AuthorizationCode {
let mut pending_grants = self.pending_auth_code_grants.lock().await; code,
redirect_uri,
client_id,
code_verifier,
} => {
let mut pending_grants = self.pending_code_grants.lock().await;
let Some(pending_grant) = pending_grants let Some(pending_grant) = pending_grants
.remove(&code) .remove(&code)
@@ -470,39 +305,7 @@ pub async fn issue_token(&self, request: TokenRequest) -> Result<TokenResponse,
) )
.await .await
}, },
| TokenRequestType::DeviceCode { device_code } => { | TokenRequest::RefreshToken { client_id, refresh_token } =>
let mut pending_grants = self.pending_device_code_grants.lock().await;
let Some(pending_grant) = pending_grants
.remove(&device_code)
.filter(|grant| grant.is_valid_for(&client_id))
else {
return Err(OAuthError::new_static(
ErrorCode::ExpiredToken,
"Invalid device code",
));
};
match &pending_grant.state {
| DeviceCodeGrantState::Unverified { .. } => {
pending_grants.insert(device_code, pending_grant);
Err(OAuthError::new_static(
ErrorCode::AuthorizationPending,
"Authorization is pending",
))
},
| DeviceCodeGrantState::Verified { authorizing_user } =>
self.create_session(
authorizing_user.to_owned(),
pending_grant.requested_scopes,
pending_grant.client_name,
client_id,
)
.await,
}
},
| TokenRequestType::RefreshToken { refresh_token } =>
self.refresh_session(client_id, refresh_token).await, self.refresh_session(client_id, refresh_token).await,
} }
} }
@@ -540,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
@@ -561,10 +364,11 @@ async fn create_session(
.await .await
.is_ok() .is_ok()
{ {
return Err(OAuthError::new_static( return Err(OAuthError {
ErrorCode::InvalidScope, error: ErrorCode::InvalidScope,
"A device with the supplied ID already exists for this user", error_description: "A device with the supplied ID already exists for this user"
)); .into(),
});
} }
self.services self.services
@@ -572,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,
) )
@@ -609,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(" "),
@@ -645,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
@@ -657,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");
@@ -675,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,
+24 -7
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::{
@@ -42,11 +43,27 @@ pub 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).
} else if event_format.allow_room_create_in_auth_events && !create_event_in_auth_events { let Some(event_type) = pdu_json.get("type").and_then(CanonicalJsonValue::as_str) else {
return Err!(Request(BadJson("PDU does not reference the room create event"))); 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")?;
-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 {
+3 -17
View File
@@ -65,17 +65,17 @@ pub(super) async fn for_local_user(services: &Services, user_id: &UserId) -> Sel
} }
pub(super) fn for_device( pub(super) fn for_device(
client_metadata: Option<&ClientMetadata>, oauth_metadata: Option<&ClientMetadata>,
display_name: Option<&str>, display_name: Option<&str>,
) -> Self { ) -> Self {
let avatar_src = client_metadata let avatar_src = oauth_metadata
.and_then(|metadata| metadata.logo_uri.as_ref()) .and_then(|metadata| metadata.logo_uri.as_ref())
.map(|uri| uri.as_str().to_owned()); .map(|uri| uri.as_str().to_owned());
let avatar_type = if let Some(avatar_src) = avatar_src { let avatar_type = if let Some(avatar_src) = avatar_src {
AvatarType::Image(avatar_src) AvatarType::Image(avatar_src)
} else if let Some(initial) = display_name.and_then(|name| name.chars().next()) { } else if let Some(initial) = display_name.and_then(|name| name.chars().next()) {
if client_metadata.is_some() { if oauth_metadata.is_some() {
AvatarType::Initial(initial) AvatarType::Initial(initial)
} else { } else {
AvatarType::Initial('❖') AvatarType::Initial('❖')
@@ -86,20 +86,6 @@ pub(super) fn for_device(
Self { avatar_type } Self { avatar_type }
} }
pub(super) fn for_client(client_metadata: &ClientMetadata) -> Self {
let avatar_type = if let Some(logo) = &client_metadata.logo_uri {
AvatarType::Image(logo.to_string())
} else if let Some(name) = &client_metadata.client_name
&& let Some(char) = name.chars().next()
{
AvatarType::Initial(char)
} else {
AvatarType::Initial('?')
};
Self { avatar_type }
}
} }
#[derive(Debug, Template)] #[derive(Debug, Template)]
+36 -108
View File
@@ -4,12 +4,9 @@
response::Redirect, response::Redirect,
routing::on, routing::on,
}; };
use conduwuit_service::oauth::{ use conduwuit_service::oauth::grant::{AuthorizationCodeQuery, Prompt};
client_metadata::ClientMetadata,
grant::{AuthorizationCodeQuery, DeviceCodeVerifyQuery, Prompt},
};
use ruma::OwnedUserId; use ruma::OwnedUserId;
use serde::Deserialize; use url::Url;
use crate::{ use crate::{
ROUTE_PREFIX, WebError, ROUTE_PREFIX, WebError,
@@ -17,7 +14,7 @@
pages::{ pages::{
GET_POST, Result, TemplateContext, GET_POST, Result, TemplateContext,
account::register::{RegisterQuery, RequestedRegistrationFlow}, account::register::{RegisterQuery, RequestedRegistrationFlow},
components::{Avatar, ClientScopes, UserCard}, components::{Avatar, AvatarType, ClientScopes},
}, },
response, response,
session::{LoginIntent, LoginQuery, LoginTarget, User}, session::{LoginIntent, LoginQuery, LoginTarget, User},
@@ -25,9 +22,7 @@
}; };
pub(crate) fn build() -> Router<crate::State> { pub(crate) fn build() -> Router<crate::State> {
Router::new() Router::new().route("/authorization_code", on(GET_POST, route_authorization_code))
.route("/authorization_code", on(GET_POST, route_authorization_code))
.route("/device_code", on(GET_POST, route_device_code))
} }
template! { template! {
@@ -35,9 +30,12 @@ struct Grant use "grant.html.j2" {
logout_query: String, logout_query: String,
user_id: OwnedUserId, user_id: OwnedUserId,
user_avatar: Avatar, user_avatar: Avatar,
client_metadata: ClientMetadata, client_uri: Url,
scopes: ClientScopes, client_name: String,
device_code: Option<String> client_avatar: Avatar,
policy_uri: Option<Url>,
tos_uri: Option<Url>,
scopes: ClientScopes
} }
} }
@@ -104,6 +102,27 @@ async fn route_authorization_code(
let scopes = query.scope.to_scopes().map_err(WebError::BadRequest)?; let scopes = query.scope.to_scopes().map_err(WebError::BadRequest)?;
let client_name = if let Some(name) = &client.client_name {
name
} else {
"Unknown application"
}
.to_owned();
let client_avatar = {
let avatar_type = if let Some(logo) = &client.logo_uri {
AvatarType::Image(logo.to_string())
} else if let Some(name) = &client.client_name
&& let Some(char) = name.chars().next()
{
AvatarType::Initial(char)
} else {
AvatarType::Initial('?')
};
Avatar { avatar_type }
};
let user_avatar = Avatar::for_local_user(&services, &user_id).await; let user_avatar = Avatar::for_local_user(&services, &user_id).await;
response!(Grant::new( response!(Grant::new(
@@ -116,102 +135,11 @@ async fn route_authorization_code(
.unwrap(), .unwrap(),
user_id, user_id,
user_avatar, user_avatar,
client, client.client_uri.clone(),
client_name,
client_avatar,
client.policy_uri.clone(),
client.tos_uri.clone(),
ClientScopes { scopes }, ClientScopes { scopes },
None,
)) ))
} }
#[derive(Deserialize)]
#[serde(tag = "stage", rename_all = "snake_case")]
enum DeviceCodeForm {
Lookup {
user_code: String,
},
Confirm {
device_code: String,
},
}
template! {
struct DeviceCodeGrant use "device_code_grant.html.j2" {
user_card: UserCard,
body: DeviceCodeGrantBody
}
}
#[derive(Debug)]
enum DeviceCodeGrantBody {
Lookup {
user_code_error: bool,
},
Success,
}
async fn route_device_code(
State(services): State<crate::State>,
Extension(context): Extension<TemplateContext>,
user: User<true>,
Expect(Query(query)): Expect<Query<DeviceCodeVerifyQuery>>,
PostForm(form): PostForm<DeviceCodeForm>,
) -> Result {
let user_id = if let Some(user) = user.into_session() {
user.user_id
} else {
let next = LoginTarget::DeviceCode(query.clone());
let uri = format!(
"{}/account/login?{}",
ROUTE_PREFIX,
serde_urlencoded::to_string(LoginQuery { next: Some(next), ..Default::default() })
.unwrap()
);
return response!(Redirect::to(&uri));
};
let user_card = UserCard::for_local_user(&services, user_id.clone()).await;
match (form, query.user_code.clone()) {
| (None, Some(user_code)) | (Some(DeviceCodeForm::Lookup { user_code }), _) => {
let Some(grant_info) = services.oauth.grant_info_for_user_code(&user_code).await
else {
return response!(DeviceCodeGrant::new(
context,
user_card,
DeviceCodeGrantBody::Lookup { user_code_error: true }
));
};
let user_avatar = Avatar::for_local_user(&services, &user_id).await;
response!(Grant::new(
context,
serde_urlencoded::to_string(LoginQuery {
next: Some(LoginTarget::DeviceCode(query)),
intent: Some(LoginIntent::SwitchAccounts),
..Default::default()
})
.unwrap(),
user_id,
user_avatar,
grant_info.client_metadata,
ClientScopes { scopes: grant_info.requested_scopes },
Some(grant_info.device_code),
))
},
| (Some(DeviceCodeForm::Confirm { device_code }), _) => {
services
.oauth
.validate_device_code(user_id, &device_code)
.await
.map_err(WebError::BadRequest)?;
response!(DeviceCodeGrant::new(context, user_card, DeviceCodeGrantBody::Success))
},
| (None, None) =>
response!(DeviceCodeGrant::new(context, user_card, DeviceCodeGrantBody::Lookup {
user_code_error: false
})),
}
}
-14
View File
@@ -20,17 +20,3 @@
font-style: italic; font-style: italic;
text-align: center; text-align: center;
} }
.user-code {
display: flex;
flex-direction: column;
align-items: center;
input {
text-align: center;
font-weight: bold;
font-size: 200%;
padding: 0.2em;
width: 6em !important;
}
}
@@ -1,50 +0,0 @@
{% extends "_layout.html.j2" %}
{%- block head -%}
<link rel="stylesheet" href="{{ crate::ROUTE_PREFIX }}/resources/grant.css">
{%- endblock -%}
{%- block title -%}
Authorize client
{%- endblock -%}
{%- block content -%}
<div class="panel narrow">
<h1>Authorize device</h1>
{{ user_card }}
{% match body %}
{% when DeviceCodeGrantBody::Lookup { user_code_error } %}
<p>
Enter the code displayed on your device, or in the app you're signing into.
</p>
<p>
<em class="negative">Never enter a code given to you by someone else.</em>
Your homeserver administrator will never ask you to enter a code on this page.
</p>
<form method="post">
<p>
<div class="user-code">
<label for="user_code">Validation code</label>
<input
type="text"
id="user_code"
name="user_code"
maxlength="6"
spellcheck="false"
pattern="\d{6}"
title="Enter the six-digit validation code"
>
</div>
{% if user_code_error %}
<small class="error">Invalid code</small>
{% endif %}
</p>
<input type="hidden" name="stage" value="lookup">
<button type="submit">Continue</button>
</form>
{% when DeviceCodeGrantBody::Success %}
<p>Success! 🎉 You can close this tab and return to your device.</p>
{% endmatch %}
</div>
{% endblock %}
+3 -8
View File
@@ -9,7 +9,6 @@ Authorize client
{%- endblock -%} {%- endblock -%}
{%- block content -%} {%- block content -%}
{% let client_name = client_metadata.client_name.as_deref().unwrap_or("Unknown application") %}
<div class="panel narrow"> <div class="panel narrow">
<h1>Authorize {{ client_name }}</h1> <h1>Authorize {{ client_name }}</h1>
<div class="avatars"> <div class="avatars">
@@ -19,17 +18,17 @@ Authorize client
<div class="separator" aria-hidden="true"> <div class="separator" aria-hidden="true">
</div> </div>
{{ Avatar::for_client(client_metadata) }} {{ client_avatar }}
</div> </div>
<div class="identity"> <div class="identity">
Signed in as <code>{{ user_id }}</code>. <a href="{{ crate::ROUTE_PREFIX }}/account/logout?{{ logout_query }}">Switch accounts</a> Signed in as <code>{{ user_id }}</code>. <a href="{{ crate::ROUTE_PREFIX }}/account/logout?{{ logout_query }}">Switch accounts</a>
</div> </div>
<p> <p>
<b>{{ client_name }}</b> (<a href="{{ client_metadata.client_uri }}">{{ client_metadata.client_uri.domain().unwrap() }}</a>) would like <b>{{ client_name }}</b> (<a href="{{ client_uri }}">{{ client_uri.domain().unwrap() }}</a>) would like
your permission to: your permission to:
{{ scopes }} {{ scopes }}
</p> </p>
{% match (&client_metadata.policy_uri, &client_metadata.tos_uri) %} {% match (&policy_uri, &tos_uri) %}
{% when (Some(policy_uri), Some(tos_uri)) %} {% when (Some(policy_uri), Some(tos_uri)) %}
<p> <p>
{{ client_name }}'s <a href="{{ policy_uri }}">policies</a> {{ client_name }}'s <a href="{{ policy_uri }}">policies</a>
@@ -50,10 +49,6 @@ Authorize client
{% endmatch %} {% endmatch %}
<form method="post"> <form method="post">
<input type="hidden" name="stage" value="confirm">
{% if let Some(device_code) = device_code %}
<input type="hidden" name="device_code" value="{{ device_code }}">
{% endif %}
<button type="submit">Continue</button> <button type="submit">Continue</button>
</form> </form>
</div> </div>
+1 -5
View File
@@ -9,7 +9,7 @@
http::request::Parts, http::request::Parts,
response::{IntoResponse, Redirect, Response}, response::{IntoResponse, Redirect, Response},
}; };
use conduwuit_service::oauth::grant::{AuthorizationCodeQuery, DeviceCodeVerifyQuery}; use conduwuit_service::oauth::grant::AuthorizationCodeQuery;
use ruma::{OwnedUserId, UserId}; use ruma::{OwnedUserId, UserId};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tower_sessions::Session; use tower_sessions::Session;
@@ -38,7 +38,6 @@ pub(crate) enum LoginTarget {
ChangeEmail, ChangeEmail,
CrossSigningReset, CrossSigningReset,
Deactivate, Deactivate,
DeviceCode(DeviceCodeVerifyQuery),
DeviceInfo(DevicePath), DeviceInfo(DevicePath),
RemoveDevice(DevicePath), RemoveDevice(DevicePath),
} }
@@ -60,9 +59,6 @@ pub(crate) fn target_path(&self) -> String {
| Self::ChangeEmail => "account/email/change/".into(), | Self::ChangeEmail => "account/email/change/".into(),
| Self::CrossSigningReset => "account/cross_signing_reset".into(), | Self::CrossSigningReset => "account/cross_signing_reset".into(),
| Self::Deactivate => "account/deactivate".into(), | Self::Deactivate => "account/deactivate".into(),
| Self::DeviceCode(code) =>
format!("oauth2/grant/device_code?{}", serde_urlencoded::to_string(code).unwrap())
.into(),
| Self::DeviceInfo(path) => format!("account/device/{}/", path.device).into(), | Self::DeviceInfo(path) => format!("account/device/{}/", path.device).into(),
| Self::RemoveDevice(path) => format!("account/device/{}/remove", path.device).into(), | Self::RemoveDevice(path) => format!("account/device/{}/remove", path.device).into(),
}; };