mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-04-02 03:25:53 +00:00
Compare commits
53 Commits
tom/max-pe
...
v0.5.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
587abe9d14 | ||
|
|
c499042a76 | ||
|
|
86e450a835 | ||
|
|
4c796029bb | ||
|
|
fc3615c46b | ||
|
|
7375f7a68e | ||
|
|
ae28fe92d2 | ||
|
|
00eeeb78de | ||
|
|
a028049e6f | ||
|
|
7b159bc8c8 | ||
|
|
66fcedf08b | ||
|
|
3f790844f3 | ||
|
|
89be9d1efc | ||
|
|
60dd6baffd | ||
|
|
99a10998b4 | ||
|
|
05c6b5df75 | ||
|
|
74db426c6b | ||
|
|
344d68dabc | ||
|
|
d3ee9c407a | ||
|
|
da956b1a2a | ||
|
|
244bf6ed2f | ||
|
|
52a51f1de0 | ||
|
|
6e118f445b | ||
|
|
e3cf288f39 | ||
|
|
6e6c9ae4cb | ||
|
|
5a2a1b6240 | ||
|
|
fb536ca1ce | ||
|
|
d22d47954f | ||
|
|
d48cc46643 | ||
|
|
8cf2d175d6 | ||
|
|
205ac22008 | ||
|
|
d353446488 | ||
|
|
77e8fd1744 | ||
|
|
7fa7b129c0 | ||
|
|
247bc15659 | ||
|
|
88a35e139d | ||
|
|
37574ef5cc | ||
|
|
1c816850ed | ||
|
|
3483059e1c | ||
|
|
d865dd4454 | ||
|
|
adc7c5ac49 | ||
|
|
112403e470 | ||
|
|
ea0a124981 | ||
|
|
bf205fb13c | ||
|
|
9a6408f98f | ||
|
|
ca77970ff3 | ||
|
|
42f4ec34cd | ||
|
|
ecf74bb31f | ||
|
|
8c716befdc | ||
|
|
a8209d1dd9 | ||
|
|
9552dd7485 | ||
|
|
88c84f221f | ||
|
|
a10bd71945 |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -7,6 +7,5 @@
|
||||
"continuwuity",
|
||||
"homeserver",
|
||||
"homeservers"
|
||||
],
|
||||
"rust-analyzer.cargo.features": ["full"]
|
||||
]
|
||||
}
|
||||
|
||||
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,3 +1,49 @@
|
||||
# Continuwuity 0.5.3 (2026-01-12)
|
||||
|
||||
## Features
|
||||
|
||||
- Improve the display of nested configuration with the `!admin server show-config` command. Contributed by @Jade (#1279)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed `M_BAD_JSON` error when sending invites to other servers or when providing joins. Contributed by @nex (#1286)
|
||||
|
||||
|
||||
## Docs
|
||||
|
||||
- Improve admin command documentation generation. Contributed by @ginger (#1280)
|
||||
|
||||
|
||||
## Misc
|
||||
|
||||
- Improve timeout-related code for federation and URL previews. Contributed by @Jade
|
||||
|
||||
|
||||
# Continuwuity 0.5.2 (2026-01-09)
|
||||
|
||||
## Features
|
||||
|
||||
- Added support for issuing additional registration tokens, stored in the database, which supplement the existing registration token hardcoded in the config file. These tokens may optionally expire after a certain number of uses or after a certain amount of time has passed. Additionally, the `registration_token_file` configuration option is superseded by this feature and **has been removed**. Use the new `!admin token` command family to manage registration tokens. Contributed by @ginger (#783).
|
||||
- Implemented a configuration defined admin list independent of the admin room. Contributed by @Terryiscool160. (#1253)
|
||||
- Added support for invite and join anti-spam via Draupnir and Meowlnir, similar to that of synapse-http-antispam. Contributed by @nex. (#1263)
|
||||
- Implemented account locking functionality, to complement user suspension. Contributed by @nex. (#1266)
|
||||
- Added admin command to forcefully log out all of a user's existing sessions. Contributed by @nex. (#1271)
|
||||
- Implemented toggling the ability for an account to log in without mutating any of its data. Contributed by @nex. (#1272)
|
||||
- Add support for custom room create event timestamps, to allow generating custom prefixes in hashed room IDs. Contributed by @nex. (#1277)
|
||||
- Certain potentially dangerous admin commands are now restricted to only be usable in the admin room and server console. Contributed by @ginger.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed unreliable room summary fetching and improved error messages. Contributed by @nex. (#1257)
|
||||
- Client requested timeout parameter is now applied to e2ee key lookups and claims. Related federation requests are now also concurrent. Contributed by @nex. (#1261)
|
||||
- Fixed the whoami endpoint returning HTTP 404 instead of HTTP 403, which confused some appservices. Contributed by @nex. (#1276)
|
||||
|
||||
## Misc
|
||||
|
||||
- The `console` feature is now enabled by default, allowing the server console to be used for running admin commands directly. To automatically open the console on startup, set the `admin_console_automatic` config option to `true`. Contributed by @ginger.
|
||||
- We now (finally) document our container image mirrors. Contributed by @Jade
|
||||
|
||||
|
||||
# Continuwuity 0.5.0 (2025-12-30)
|
||||
|
||||
**This release contains a CRITICAL vulnerability patch, and you must update as soon as possible**
|
||||
|
||||
257
Cargo.lock
generated
257
Cargo.lock
generated
@@ -72,56 +72,12 @@ dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
@@ -199,7 +155,20 @@ version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4"
|
||||
dependencies = [
|
||||
"askama_derive",
|
||||
"askama_derive 0.14.0",
|
||||
"itoa",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7125972258312e79827b60c9eb93938334100245081cf701a2dee981b17427"
|
||||
dependencies = [
|
||||
"askama_macros",
|
||||
"itoa",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
@@ -212,7 +181,7 @@ version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f"
|
||||
dependencies = [
|
||||
"askama_parser",
|
||||
"askama_parser 0.14.0",
|
||||
"basic-toml",
|
||||
"memchr",
|
||||
"proc-macro2",
|
||||
@@ -223,6 +192,32 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_derive"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994"
|
||||
dependencies = [
|
||||
"askama_parser 0.15.1",
|
||||
"basic-toml",
|
||||
"memchr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_macros"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46"
|
||||
dependencies = [
|
||||
"askama_derive 0.15.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_parser"
|
||||
version = "0.14.0"
|
||||
@@ -235,6 +230,19 @@ dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama_parser"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3c63392767bb2df6aa65a6e1e3b80fd89bb7af6d58359b924c0695620f1512e"
|
||||
dependencies = [
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"unicode-ident",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs"
|
||||
version = "0.7.1"
|
||||
@@ -757,6 +765,39 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_toml"
|
||||
version = "0.22.3"
|
||||
@@ -839,25 +880,14 @@ dependencies = [
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap-markdown"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2a2617956a06d4885b490697b5307ebb09fec10b088afc18c81762d848c2339"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -878,16 +908,6 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
|
||||
[[package]]
|
||||
name = "clap_mangen"
|
||||
version = "0.2.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ea63a92086df93893164221ad4f24142086d535b3a0957b9b9bea2dc86301"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"roff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
version = "0.1.54"
|
||||
@@ -903,12 +923,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "compression-codecs"
|
||||
version = "0.4.31"
|
||||
@@ -940,7 +954,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"conduwuit_admin",
|
||||
@@ -972,7 +986,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_admin"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"conduwuit_api",
|
||||
@@ -994,7 +1008,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_api"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum 0.7.9",
|
||||
@@ -1027,14 +1041,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_build_metadata"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"built",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_core"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"arrayvec",
|
||||
@@ -1095,7 +1109,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_database"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"conduwuit_core",
|
||||
@@ -1114,7 +1128,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_macros"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
@@ -1124,7 +1138,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_router"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"axum-client-ip",
|
||||
@@ -1159,7 +1173,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_service"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
@@ -1200,9 +1214,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_web"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"askama 0.14.0",
|
||||
"axum 0.7.9",
|
||||
"conduwuit_build_metadata",
|
||||
"conduwuit_service",
|
||||
@@ -1635,7 +1649,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "draupnir-antispam"
|
||||
version = "0.1.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"ruma-common",
|
||||
"serde",
|
||||
@@ -2639,12 +2653,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
@@ -2995,7 +3003,7 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
[[package]]
|
||||
name = "meowlnir-antispam"
|
||||
version = "0.1.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"ruma-common",
|
||||
"serde",
|
||||
@@ -3272,12 +3280,6 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.6"
|
||||
@@ -4076,16 +4078,10 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roff"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
|
||||
|
||||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.10.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"draupnir-antispam",
|
||||
@@ -4107,7 +4103,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.10.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4119,7 +4115,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.18.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"assign",
|
||||
@@ -4142,7 +4138,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.13.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64 0.22.1",
|
||||
@@ -4174,7 +4170,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.28.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"indexmap",
|
||||
@@ -4199,7 +4195,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"headers",
|
||||
@@ -4221,7 +4217,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.9.5"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror 2.0.17",
|
||||
@@ -4230,7 +4226,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-identity-service-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4240,7 +4236,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.13.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro-crate",
|
||||
@@ -4255,7 +4251,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4267,7 +4263,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.15.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
@@ -4506,6 +4502,10 @@ name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry"
|
||||
@@ -4961,12 +4961,6 @@ dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "subslice"
|
||||
version = "0.2.3"
|
||||
@@ -5700,12 +5694,6 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
@@ -6228,20 +6216,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"askama 0.15.1",
|
||||
"cargo_metadata",
|
||||
"clap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xtask-generate-commands"
|
||||
version = "0.5.1"
|
||||
dependencies = [
|
||||
"clap-markdown",
|
||||
"clap_builder",
|
||||
"clap_mangen",
|
||||
"conduwuit",
|
||||
"conduwuit_admin",
|
||||
]
|
||||
|
||||
21
Cargo.toml
21
Cargo.toml
@@ -1,27 +1,18 @@
|
||||
#cargo-features = ["profile-rustflags"]
|
||||
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["src/*", "xtask/*"]
|
||||
members = ["src/*", "xtask/"]
|
||||
default-members = ["src/*"]
|
||||
|
||||
[workspace.package]
|
||||
authors = [
|
||||
"June Clementine Strawberry <june@girlboss.ceo>",
|
||||
"strawberry <strawberry@puppygock.gay>", # woof
|
||||
"Jason Volk <jason@zemos.net>",
|
||||
]
|
||||
categories = ["network-programming"]
|
||||
description = "a very cool Matrix chat homeserver written in Rust"
|
||||
authors = ["Continuwuity Team and contributors <team@continuwuity.org>"]
|
||||
description = "A Matrix homeserver written in Rust, the official continuation of the conduwuit homeserver."
|
||||
edition = "2024"
|
||||
homepage = "https://continuwuity.org/"
|
||||
keywords = ["chat", "matrix", "networking", "server", "uwu"]
|
||||
license = "Apache-2.0"
|
||||
# See also `rust-toolchain.toml`
|
||||
readme = "README.md"
|
||||
repository = "https://forgejo.ellis.link/continuwuation/continuwuity"
|
||||
rust-version = "1.86.0"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "conduwuit"
|
||||
@@ -351,7 +342,7 @@ version = "0.1.2"
|
||||
# Used for matrix spec type definitions and helpers
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
||||
rev = "79abd5d331bca596b7f37e367a9f2cebccd9f64d"
|
||||
rev = "f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -848,6 +839,8 @@ unknown_lints = "allow"
|
||||
|
||||
###################
|
||||
cargo = { level = "warn", priority = -1 }
|
||||
# Nobody except for us should be consuming these crates, they don't need metadata
|
||||
cargo_common_metadata = { level = "allow" }
|
||||
|
||||
## some sadness
|
||||
multiple_crate_versions = { level = "allow", priority = 1 }
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
The `console` feature is now enabled by default, allowing the server console to be used for running admin commands directly.
|
||||
@@ -1 +0,0 @@
|
||||
Certain potentially dangerous admin commands are now restricted to only be usable in the admin room and server console.
|
||||
@@ -1 +0,0 @@
|
||||
Implemented a configuration defined admin list independent of the admin room. (@Terryiscool160).
|
||||
@@ -1 +0,0 @@
|
||||
Fixed unreliable room summary fetching and improved error messages. Contributed by @nex.
|
||||
@@ -1,2 +0,0 @@
|
||||
Client requested timeout parameter is now applied to e2ee key lookups and claims. Related federation requests are now
|
||||
also concurrent. Contributed by @nex.
|
||||
@@ -1,2 +0,0 @@
|
||||
Added support for invite and join anti-spam via Draupnir and Meowlnir, similar to that of synapse-http-antispam.
|
||||
Contributed by @nex.
|
||||
@@ -1 +0,0 @@
|
||||
Implemented account locking functionality, to complement user suspension. Contributed by @nex.
|
||||
@@ -340,7 +340,9 @@
|
||||
# this to be high to account for extremely large room joins, slow
|
||||
# homeservers, your own resources etc.
|
||||
#
|
||||
#federation_timeout = 300
|
||||
# Joins have 6x the timeout.
|
||||
#
|
||||
#federation_timeout = 60
|
||||
|
||||
# MSC4284 Policy server request timeout (seconds). Generally policy
|
||||
# servers should respond near instantly, however may slow down under
|
||||
@@ -389,7 +391,15 @@
|
||||
#
|
||||
#appservice_idle_timeout = 300
|
||||
|
||||
# Notification gateway pusher idle connection pool timeout.
|
||||
# Notification gateway pusher request connection timeout (seconds).
|
||||
#
|
||||
#pusher_conn_timeout = 15
|
||||
|
||||
# Notification gateway pusher total request timeout (seconds).
|
||||
#
|
||||
#pusher_timeout = 60
|
||||
|
||||
# Notification gateway pusher idle connection pool timeout (seconds).
|
||||
#
|
||||
#pusher_idle_timeout = 15
|
||||
|
||||
@@ -421,7 +431,7 @@
|
||||
# `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
#
|
||||
# If you would like registration only via token reg, please configure
|
||||
# `registration_token` or `registration_token_file`.
|
||||
# `registration_token`.
|
||||
#
|
||||
#allow_registration = false
|
||||
|
||||
@@ -452,22 +462,13 @@
|
||||
# `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
# to true to allow open registration without any conditions.
|
||||
#
|
||||
# YOU NEED TO EDIT THIS OR USE registration_token_file.
|
||||
# If you do not want to set a static token, the `!admin token` commands
|
||||
# may also be used to manage registration tokens.
|
||||
#
|
||||
# example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
||||
#
|
||||
#registration_token =
|
||||
|
||||
# Path to a file on the system that gets read for additional registration
|
||||
# tokens. Multiple tokens can be added if you separate them with
|
||||
# whitespace
|
||||
#
|
||||
# continuwuity must be able to access the file, and it must not be empty
|
||||
#
|
||||
# example: "/etc/continuwuity/.reg_token"
|
||||
#
|
||||
#registration_token_file =
|
||||
|
||||
# The public site key for reCaptcha. If this is provided, reCaptcha
|
||||
# becomes required during registration. If both captcha *and*
|
||||
# registration token are enabled, both will be required during
|
||||
@@ -1455,6 +1456,11 @@
|
||||
#
|
||||
#url_preview_max_spider_size = 256000
|
||||
|
||||
# Total request timeout for URL previews (seconds). This includes
|
||||
# connection, request, and response body reading time.
|
||||
#
|
||||
#url_preview_timeout = 120
|
||||
|
||||
# Option to decide whether you would like to run the domain allowlist
|
||||
# checks (contains and explicit) on the root domain or not. Does not apply
|
||||
# to URL contains allowlist. Defaults to false.
|
||||
@@ -1753,10 +1759,6 @@
|
||||
#
|
||||
#config_reload_signal = true
|
||||
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
#
|
||||
#ldap = false
|
||||
|
||||
[global.tls]
|
||||
|
||||
# Path to a valid TLS certificate file.
|
||||
@@ -1924,6 +1926,8 @@
|
||||
#
|
||||
#admin_filter = ""
|
||||
|
||||
[global.antispam]
|
||||
|
||||
[global.antispam.meowlnir]
|
||||
|
||||
# The base URL on which to contact Meowlnir (before /_meowlnir/antispam).
|
||||
|
||||
@@ -52,7 +52,7 @@ ENV BINSTALL_VERSION=1.16.6
|
||||
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
||||
ENV CARGO_SBOM_VERSION=0.9.1
|
||||
# renovate: datasource=crate depName=lddtree
|
||||
ENV LDDTREE_VERSION=0.3.7
|
||||
ENV LDDTREE_VERSION=0.4.0
|
||||
# renovate: datasource=crate depName=timelord-cli
|
||||
ENV TIMELORD_VERSION=3.0.1
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ ENV BINSTALL_VERSION=1.16.6
|
||||
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
||||
ENV CARGO_SBOM_VERSION=0.9.1
|
||||
# renovate: datasource=crate depName=lddtree
|
||||
ENV LDDTREE_VERSION=0.3.7
|
||||
ENV LDDTREE_VERSION=0.4.0
|
||||
|
||||
# Install unpackaged tools
|
||||
RUN <<EOF
|
||||
|
||||
@@ -57,14 +57,9 @@
|
||||
"name": "/reference/config"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"type": "dir",
|
||||
"label": "Admin Command Reference",
|
||||
"name": "/reference/admin"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"label": "Server Reference",
|
||||
"name": "/reference/server"
|
||||
"name": "/reference/admin/"
|
||||
},
|
||||
{
|
||||
"type": "divider"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
{
|
||||
"text": "Admin Command Reference",
|
||||
"link": "/reference/admin"
|
||||
"link": "/reference/admin/"
|
||||
},
|
||||
{
|
||||
"text": "Server Reference",
|
||||
|
||||
@@ -13,8 +13,8 @@ ### Use a registry
|
||||
| --------------- | --------------------------------------------------------------- | -----------------------|
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/latest) | Latest tagged image. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/main) | Main branch image. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/latest-maxperf) | Performance optimised version. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/main-maxperf) | Performance optimised version. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/latest-maxperf) | [Performance optimised version.](./generic.mdx#performance-optimised-builds) |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/main-maxperf) | [Performance optimised version.](./generic.mdx#performance-optimised-builds) |
|
||||
|
||||
Use
|
||||
|
||||
|
||||
@@ -8,10 +8,5 @@
|
||||
"type": "file",
|
||||
"name": "admin",
|
||||
"label": "Admin Commands"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "server",
|
||||
"label": "Server command"
|
||||
}
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
29
docs/reference/admin/appservices.md
Normal file
29
docs/reference/admin/appservices.md
Normal file
@@ -0,0 +1,29 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin appservices`
|
||||
|
||||
Commands for managing appservices
|
||||
|
||||
|
||||
## `!admin appservices register`
|
||||
|
||||
Register an appservice using its registration YAML
|
||||
|
||||
This command needs a YAML generated by an appservice (such as a bridge), which must be provided in a Markdown code block below the command.
|
||||
|
||||
Registering a new bridge using the ID of an existing bridge will replace the old one.
|
||||
|
||||
## `!admin appservices unregister`
|
||||
|
||||
Unregister an appservice using its ID
|
||||
|
||||
You can find the ID using the `list-appservices` command.
|
||||
|
||||
## `!admin appservices show-appservice-config`
|
||||
|
||||
Show an appservice's config using its ID
|
||||
|
||||
You can find the ID using the `list-appservices` command.
|
||||
|
||||
## `!admin appservices list-registered`
|
||||
|
||||
List all the currently registered appservices
|
||||
9
docs/reference/admin/check.md
Normal file
9
docs/reference/admin/check.md
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin check`
|
||||
|
||||
Commands for checking integrity
|
||||
|
||||
|
||||
## `!admin check check-all-users`
|
||||
|
||||
Uses the iterator in `src/database/key_value/users.rs` to iterator over every user in our database (remote and local). Reports total count, any errors if there were any, etc
|
||||
139
docs/reference/admin/debug.md
Normal file
139
docs/reference/admin/debug.md
Normal file
@@ -0,0 +1,139 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin debug`
|
||||
|
||||
Commands for debugging things
|
||||
|
||||
|
||||
## `!admin debug echo`
|
||||
|
||||
Echo input of admin command
|
||||
|
||||
## `!admin debug get-auth-chain`
|
||||
|
||||
Get the auth_chain of a PDU
|
||||
|
||||
## `!admin debug parse-pdu`
|
||||
|
||||
Parse and print a PDU from a JSON
|
||||
|
||||
The PDU event is only checked for validity and is not added to the database.
|
||||
|
||||
This command needs a JSON blob provided in a Markdown code block below the command.
|
||||
|
||||
## `!admin debug get-pdu`
|
||||
|
||||
Retrieve and print a PDU by EventID from the Continuwuity database
|
||||
|
||||
## `!admin debug get-short-pdu`
|
||||
|
||||
Retrieve and print a PDU by PduId from the Continuwuity database
|
||||
|
||||
## `!admin debug get-remote-pdu`
|
||||
|
||||
Attempts to retrieve a PDU from a remote server. **Does not** insert it into the database or persist it anywhere
|
||||
|
||||
## `!admin debug get-remote-pdu-list`
|
||||
|
||||
Same as `get-remote-pdu` but accepts a codeblock newline delimited list of PDUs and a single server to fetch from
|
||||
|
||||
## `!admin debug get-room-state`
|
||||
|
||||
Gets all the room state events for the specified room.
|
||||
|
||||
This is functionally equivalent to `GET /_matrix/client/v3/rooms/{roomid}/state`, except the admin command does *not* check if the sender user is allowed to see state events. This is done because it's implied that server admins here have database access and can see/get room info themselves anyways if they were malicious admins.
|
||||
|
||||
Of course the check is still done on the actual client API.
|
||||
|
||||
## `!admin debug get-signing-keys`
|
||||
|
||||
Get and display signing keys from local cache or remote server
|
||||
|
||||
## `!admin debug get-verify-keys`
|
||||
|
||||
Get and display signing keys from local cache or remote server
|
||||
|
||||
## `!admin debug ping`
|
||||
|
||||
Sends a federation request to the remote server's `/_matrix/federation/v1/version` endpoint and measures the latency it took for the server to respond
|
||||
|
||||
## `!admin debug force-device-list-updates`
|
||||
|
||||
Forces device lists for all local and remote users to be updated (as having new keys available)
|
||||
|
||||
## `!admin debug change-log-level`
|
||||
|
||||
Change tracing log level/filter on the fly
|
||||
|
||||
This accepts the same format as the `log` config option.
|
||||
|
||||
## `!admin debug verify-json`
|
||||
|
||||
Verify JSON signatures
|
||||
|
||||
This command needs a JSON blob provided in a Markdown code block below the command.
|
||||
|
||||
## `!admin debug verify-pdu`
|
||||
|
||||
Verify PDU
|
||||
|
||||
This re-verifies a PDU existing in the database found by ID.
|
||||
|
||||
## `!admin debug first-pdu-in-room`
|
||||
|
||||
Prints the very first PDU in the specified room (typically m.room.create)
|
||||
|
||||
## `!admin debug latest-pdu-in-room`
|
||||
|
||||
Prints the latest ("last") PDU in the specified room (typically a message)
|
||||
|
||||
## `!admin debug force-set-room-state-from-server`
|
||||
|
||||
Forcefully replaces the room state of our local copy of the specified room, with the copy (auth chain and room state events) the specified remote server says.
|
||||
|
||||
A common desire for room deletion is to simply "reset" our copy of the room. While this admin command is not a replacement for that, if you know you have split/broken room state and you know another server in the room that has the best/working room state, this command can let you use their room state. Such example is your server saying users are in a room, but other servers are saying they're not in the room in question.
|
||||
|
||||
This command will get the latest PDU in the room we know about, and request the room state at that point in time via `/_matrix/federation/v1/state/{roomId}`.
|
||||
|
||||
## `!admin debug resolve-true-destination`
|
||||
|
||||
Runs a server name through Continuwuity's true destination resolution process
|
||||
|
||||
Useful for debugging well-known issues
|
||||
|
||||
## `!admin debug memory-stats`
|
||||
|
||||
Print extended memory usage
|
||||
|
||||
Optional argument is a character mask (a sequence of characters in any order) which enable additional extended statistics. Known characters are "abdeglmx". For convenience, a '*' will enable everything.
|
||||
|
||||
## `!admin debug runtime-metrics`
|
||||
|
||||
Print general tokio runtime metric totals
|
||||
|
||||
## `!admin debug runtime-interval`
|
||||
|
||||
Print detailed tokio runtime metrics accumulated since last command invocation
|
||||
|
||||
## `!admin debug time`
|
||||
|
||||
Print the current time
|
||||
|
||||
## `!admin debug list-dependencies`
|
||||
|
||||
List dependencies
|
||||
|
||||
## `!admin debug database-stats`
|
||||
|
||||
Get database statistics
|
||||
|
||||
## `!admin debug trim-memory`
|
||||
|
||||
Trim memory usage
|
||||
|
||||
## `!admin debug database-files`
|
||||
|
||||
List database files
|
||||
|
||||
## `!admin debug tester`
|
||||
|
||||
Developer test stubs
|
||||
29
docs/reference/admin/federation.md
Normal file
29
docs/reference/admin/federation.md
Normal file
@@ -0,0 +1,29 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin federation`
|
||||
|
||||
Commands for managing federation
|
||||
|
||||
|
||||
## `!admin federation incoming-federation`
|
||||
|
||||
List all rooms we are currently handling an incoming pdu from
|
||||
|
||||
## `!admin federation disable-room`
|
||||
|
||||
Disables incoming federation handling for a room
|
||||
|
||||
## `!admin federation enable-room`
|
||||
|
||||
Enables incoming federation handling for a room again
|
||||
|
||||
## `!admin federation fetch-support-well-known`
|
||||
|
||||
Fetch `/.well-known/matrix/support` from the specified server
|
||||
|
||||
Despite the name, this is not a federation endpoint and does not go through the federation / server resolution process as per-spec this is supposed to be served at the server_name.
|
||||
|
||||
Respecting homeservers put this file here for listing administration, moderation, and security inquiries. This command provides a way to easily fetch that information.
|
||||
|
||||
## `!admin federation remote-user-in-rooms`
|
||||
|
||||
Lists all the rooms we share/track with the specified *remote* user
|
||||
23
docs/reference/admin/index.md
Normal file
23
docs/reference/admin/index.md
Normal file
@@ -0,0 +1,23 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# Admin Command Reference
|
||||
|
||||
Admin commands allow server administrators to manage the server from within their Matrix client. "Server administrators" by default means only those users which are members of the admin room, but additional server admins may be added using the `admins_list` configuration option.
|
||||
|
||||
## Running commands
|
||||
|
||||
* All commands listed here may be used by server administrators in the admin room by sending them as messages.
|
||||
* If the `admin_escape_commands` configuration option is enabled, server administrators may run certain commands in public rooms by prefixing them with a single backslash. These commands will only run on _their_ homeserver, even if they are a member of another homeserver's admin room. Some sensitive commands cannot be used outside the admin room and will return an error.
|
||||
* All commands listed here may be used in the server's console, if it is enabled. Commands entered in the console do not require the `!admin` prefix.
|
||||
|
||||
## Categories
|
||||
|
||||
- [`!admin appservices`](appservices/): Commands for managing appservices
|
||||
- [`!admin users`](users/): Commands for managing local users
|
||||
- [`!admin token`](token/): Commands for managing registration tokens
|
||||
- [`!admin rooms`](rooms/): Commands for managing rooms
|
||||
- [`!admin federation`](federation/): Commands for managing federation
|
||||
- [`!admin server`](server/): Commands for managing the server
|
||||
- [`!admin media`](media/): Commands for managing media
|
||||
- [`!admin check`](check/): Commands for checking integrity
|
||||
- [`!admin debug`](debug/): Commands for debugging things
|
||||
- [`!admin query`](query/): Low-level queries for database getters and iterators
|
||||
38
docs/reference/admin/media.md
Normal file
38
docs/reference/admin/media.md
Normal file
@@ -0,0 +1,38 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin media`
|
||||
|
||||
Commands for managing media
|
||||
|
||||
|
||||
## `!admin media delete`
|
||||
|
||||
Deletes a single media file from our database and on the filesystem via a single MXC URL or event ID (not redacted)
|
||||
|
||||
## `!admin media delete-list`
|
||||
|
||||
Deletes a codeblock list of MXC URLs from our database and on the filesystem. This will always ignore errors
|
||||
|
||||
## `!admin media delete-past-remote-media`
|
||||
|
||||
Deletes all remote (and optionally local) media created before/after
|
||||
[duration] ago, using filesystem metadata first created at date, or
|
||||
fallback to last modified date. This will always ignore errors by
|
||||
default.
|
||||
|
||||
* Examples:
|
||||
* Delete all remote media older than a year:
|
||||
|
||||
`!admin media delete-past-remote-media -b 1y`
|
||||
|
||||
* Delete all remote and local media from 3 days ago, up until now:
|
||||
|
||||
`!admin media delete-past-remote-media -a 3d
|
||||
-yes-i-want-to-delete-local-media`
|
||||
|
||||
## `!admin media delete-all-from-user`
|
||||
|
||||
Deletes all the local media from a local user on our server. This will always ignore errors by default
|
||||
|
||||
## `!admin media delete-all-from-server`
|
||||
|
||||
Deletes all remote media from the specified remote server. This will always ignore errors by default
|
||||
181
docs/reference/admin/query.md
Normal file
181
docs/reference/admin/query.md
Normal file
@@ -0,0 +1,181 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin query`
|
||||
|
||||
Low-level queries for database getters and iterators
|
||||
|
||||
|
||||
## `!admin query account-data`
|
||||
|
||||
account_data.rs iterators and getters
|
||||
|
||||
### `!admin query account-data changes-since`
|
||||
|
||||
Returns all changes to the account data that happened after `since`
|
||||
|
||||
### `!admin query account-data account-data-get`
|
||||
|
||||
Searches the account data for a specific kind
|
||||
|
||||
## `!admin query appservice`
|
||||
|
||||
appservice.rs iterators and getters
|
||||
|
||||
### `!admin query appservice get-registration`
|
||||
|
||||
Gets the appservice registration info/details from the ID as a string
|
||||
|
||||
### `!admin query appservice all`
|
||||
|
||||
Gets all appservice registrations with their ID and registration info
|
||||
|
||||
## `!admin query presence`
|
||||
|
||||
presence.rs iterators and getters
|
||||
|
||||
### `!admin query presence get-presence`
|
||||
|
||||
Returns the latest presence event for the given user
|
||||
|
||||
### `!admin query presence presence-since`
|
||||
|
||||
Iterator of the most recent presence updates that happened after the event with id `since`
|
||||
|
||||
## `!admin query room-alias`
|
||||
|
||||
rooms/alias.rs iterators and getters
|
||||
|
||||
### `!admin query room-alias local-aliases-for-room`
|
||||
|
||||
Iterator of all our local room aliases for the room ID
|
||||
|
||||
### `!admin query room-alias all-local-aliases`
|
||||
|
||||
Iterator of all our local aliases in our database with their room IDs
|
||||
|
||||
## `!admin query room-state-cache`
|
||||
|
||||
rooms/state_cache iterators and getters
|
||||
|
||||
## `!admin query room-timeline`
|
||||
|
||||
rooms/timeline iterators and getters
|
||||
|
||||
## `!admin query globals`
|
||||
|
||||
globals.rs iterators and getters
|
||||
|
||||
### `!admin query globals signing-keys-for`
|
||||
|
||||
This returns an empty `Ok(BTreeMap<..>)` when there are no keys found for the server
|
||||
|
||||
## `!admin query sending`
|
||||
|
||||
sending.rs iterators and getters
|
||||
|
||||
### `!admin query sending active-requests`
|
||||
|
||||
Queries database for all `servercurrentevent_data`
|
||||
|
||||
### `!admin query sending active-requests-for`
|
||||
|
||||
Queries database for `servercurrentevent_data` but for a specific destination
|
||||
|
||||
This command takes only *one* format of these arguments:
|
||||
|
||||
appservice_id server_name user_id AND push_key
|
||||
|
||||
See src/service/sending/mod.rs for the definition of the `Destination` enum
|
||||
|
||||
### `!admin query sending queued-requests`
|
||||
|
||||
Queries database for `servernameevent_data` which are the queued up requests that will eventually be sent
|
||||
|
||||
This command takes only *one* format of these arguments:
|
||||
|
||||
appservice_id server_name user_id AND push_key
|
||||
|
||||
See src/service/sending/mod.rs for the definition of the `Destination` enum
|
||||
|
||||
## `!admin query users`
|
||||
|
||||
users.rs iterators and getters
|
||||
|
||||
## `!admin query resolver`
|
||||
|
||||
resolver service
|
||||
|
||||
### `!admin query resolver destinations-cache`
|
||||
|
||||
Query the destinations cache
|
||||
|
||||
### `!admin query resolver overrides-cache`
|
||||
|
||||
Query the overrides cache
|
||||
|
||||
## `!admin query pusher`
|
||||
|
||||
pusher service
|
||||
|
||||
### `!admin query pusher get-pushers`
|
||||
|
||||
Returns all the pushers for the user
|
||||
|
||||
## `!admin query short`
|
||||
|
||||
short service
|
||||
|
||||
## `!admin query raw`
|
||||
|
||||
raw service
|
||||
|
||||
### `!admin query raw raw-maps`
|
||||
|
||||
List database maps
|
||||
|
||||
### `!admin query raw raw-get`
|
||||
|
||||
Raw database query
|
||||
|
||||
### `!admin query raw raw-del`
|
||||
|
||||
Raw database delete (for string keys)
|
||||
|
||||
### `!admin query raw raw-keys`
|
||||
|
||||
Raw database keys iteration
|
||||
|
||||
### `!admin query raw raw-keys-sizes`
|
||||
|
||||
Raw database key size breakdown
|
||||
|
||||
### `!admin query raw raw-keys-total`
|
||||
|
||||
Raw database keys total bytes
|
||||
|
||||
### `!admin query raw raw-vals-sizes`
|
||||
|
||||
Raw database values size breakdown
|
||||
|
||||
### `!admin query raw raw-vals-total`
|
||||
|
||||
Raw database values total bytes
|
||||
|
||||
### `!admin query raw raw-iter`
|
||||
|
||||
Raw database items iteration
|
||||
|
||||
### `!admin query raw raw-keys-from`
|
||||
|
||||
Raw database keys iteration
|
||||
|
||||
### `!admin query raw raw-iter-from`
|
||||
|
||||
Raw database items iteration
|
||||
|
||||
### `!admin query raw raw-count`
|
||||
|
||||
Raw database record count
|
||||
|
||||
### `!admin query raw compact`
|
||||
|
||||
Compact database
|
||||
83
docs/reference/admin/rooms.md
Normal file
83
docs/reference/admin/rooms.md
Normal file
@@ -0,0 +1,83 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin rooms`
|
||||
|
||||
Commands for managing rooms
|
||||
|
||||
|
||||
## `!admin rooms list-rooms`
|
||||
|
||||
List all rooms the server knows about
|
||||
|
||||
## `!admin rooms info`
|
||||
|
||||
View information about a room we know about
|
||||
|
||||
### `!admin rooms info list-joined-members`
|
||||
|
||||
List joined members in a room
|
||||
|
||||
### `!admin rooms info view-room-topic`
|
||||
|
||||
Displays room topic
|
||||
|
||||
Room topics can be huge, so this is in its own separate command
|
||||
|
||||
## `!admin rooms moderation`
|
||||
|
||||
Manage moderation of remote or local rooms
|
||||
|
||||
### `!admin rooms moderation ban-room`
|
||||
|
||||
Bans a room from local users joining and evicts all our local users (including server admins) from the room. Also blocks any invites (local and remote) for the banned room, and disables federation entirely with it
|
||||
|
||||
### `!admin rooms moderation ban-list-of-rooms`
|
||||
|
||||
Bans a list of rooms (room IDs and room aliases) from a newline delimited codeblock similar to `user deactivate-all`. Applies the same steps as ban-room
|
||||
|
||||
### `!admin rooms moderation unban-room`
|
||||
|
||||
Unbans a room to allow local users to join again
|
||||
|
||||
### `!admin rooms moderation list-banned-rooms`
|
||||
|
||||
List of all rooms we have banned
|
||||
|
||||
## `!admin rooms alias`
|
||||
|
||||
Manage rooms' aliases
|
||||
|
||||
### `!admin rooms alias set`
|
||||
|
||||
Make an alias point to a room
|
||||
|
||||
### `!admin rooms alias remove`
|
||||
|
||||
Remove a local alias
|
||||
|
||||
### `!admin rooms alias which`
|
||||
|
||||
Show which room is using an alias
|
||||
|
||||
### `!admin rooms alias list`
|
||||
|
||||
List aliases currently being used
|
||||
|
||||
## `!admin rooms directory`
|
||||
|
||||
Manage the room directory
|
||||
|
||||
### `!admin rooms directory publish`
|
||||
|
||||
Publish a room to the room directory
|
||||
|
||||
### `!admin rooms directory unpublish`
|
||||
|
||||
Unpublish a room to the room directory
|
||||
|
||||
### `!admin rooms directory list`
|
||||
|
||||
List rooms that are published
|
||||
|
||||
## `!admin rooms exists`
|
||||
|
||||
Check if we know about a room
|
||||
53
docs/reference/admin/server.md
Normal file
53
docs/reference/admin/server.md
Normal file
@@ -0,0 +1,53 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin server`
|
||||
|
||||
Commands for managing the server
|
||||
|
||||
|
||||
## `!admin server uptime`
|
||||
|
||||
Time elapsed since startup
|
||||
|
||||
## `!admin server show-config`
|
||||
|
||||
Show configuration values
|
||||
|
||||
## `!admin server reload-config`
|
||||
|
||||
Reload configuration values
|
||||
|
||||
## `!admin server list-features`
|
||||
|
||||
List the features built into the server
|
||||
|
||||
## `!admin server memory-usage`
|
||||
|
||||
Print database memory usage statistics
|
||||
|
||||
## `!admin server clear-caches`
|
||||
|
||||
Clears all of Continuwuity's caches
|
||||
|
||||
## `!admin server backup-database`
|
||||
|
||||
Performs an online backup of the database (only available for RocksDB at the moment)
|
||||
|
||||
## `!admin server list-backups`
|
||||
|
||||
List database backups
|
||||
|
||||
## `!admin server admin-notice`
|
||||
|
||||
Send a message to the admin room
|
||||
|
||||
## `!admin server reload-mods`
|
||||
|
||||
Hot-reload the server
|
||||
|
||||
## `!admin server restart`
|
||||
|
||||
Restart the server
|
||||
|
||||
## `!admin server shutdown`
|
||||
|
||||
Shutdown the server
|
||||
17
docs/reference/admin/token.md
Normal file
17
docs/reference/admin/token.md
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin token`
|
||||
|
||||
Commands for managing registration tokens
|
||||
|
||||
|
||||
## `!admin token issue`
|
||||
|
||||
Issue a new registration token
|
||||
|
||||
## `!admin token revoke`
|
||||
|
||||
Revoke a registration token
|
||||
|
||||
## `!admin token list`
|
||||
|
||||
List all registration tokens
|
||||
141
docs/reference/admin/users.md
Normal file
141
docs/reference/admin/users.md
Normal file
@@ -0,0 +1,141 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin users`
|
||||
|
||||
Commands for managing local users
|
||||
|
||||
|
||||
## `!admin users create-user`
|
||||
|
||||
Create a new user
|
||||
|
||||
## `!admin users reset-password`
|
||||
|
||||
Reset user password
|
||||
|
||||
## `!admin users deactivate`
|
||||
|
||||
Deactivate a user
|
||||
|
||||
User will be removed from all rooms by default. Use --no-leave-rooms to not leave all rooms by default.
|
||||
|
||||
## `!admin users deactivate-all`
|
||||
|
||||
Deactivate a list of users
|
||||
|
||||
Recommended to use in conjunction with list-local-users.
|
||||
|
||||
Users will be removed from joined rooms by default.
|
||||
|
||||
Can be overridden with --no-leave-rooms.
|
||||
|
||||
Removing a mass amount of users from a room may cause a significant amount of leave events. The time to leave rooms may depend significantly on joined rooms and servers.
|
||||
|
||||
This command needs a newline separated list of users provided in a Markdown code block below the command.
|
||||
|
||||
## `!admin users logout`
|
||||
|
||||
Forcefully log a user out of all of their devices.
|
||||
|
||||
This will invalidate all access tokens for the specified user, effectively logging them out from all sessions. Note that this is destructive and may result in data loss for the user, such as encryption keys. Use with caution. Can only be used in the admin room.
|
||||
|
||||
## `!admin users suspend`
|
||||
|
||||
Suspend a user
|
||||
|
||||
Suspended users are able to log in, sync, and read messages, but are not able to send events nor redact them, cannot change their profile, and are unable to join, invite to, or knock on rooms.
|
||||
|
||||
Suspended users can still leave rooms and deactivate their account. Suspending them effectively makes them read-only.
|
||||
|
||||
## `!admin users unsuspend`
|
||||
|
||||
Unsuspend a user
|
||||
|
||||
Reverses the effects of the `suspend` command, allowing the user to send messages, change their profile, create room invites, etc.
|
||||
|
||||
## `!admin users lock`
|
||||
|
||||
Lock a user
|
||||
|
||||
Locked users are unable to use their accounts beyond logging out. This is akin to a temporary deactivation that does not change the user's password. This can be used to quickly prevent a user from accessing their account.
|
||||
|
||||
## `!admin users unlock`
|
||||
|
||||
Unlock a user
|
||||
|
||||
Reverses the effects of the `lock` command, allowing the user to use their account again.
|
||||
|
||||
## `!admin users enable-login`
|
||||
|
||||
Enable login for a user
|
||||
|
||||
## `!admin users disable-login`
|
||||
|
||||
Disable login for a user
|
||||
|
||||
Disables login for the specified user without deactivating or locking their account. This prevents the user from obtaining new access tokens, but does not invalidate existing sessions.
|
||||
|
||||
## `!admin users list-users`
|
||||
|
||||
List local users in the database
|
||||
|
||||
## `!admin users list-joined-rooms`
|
||||
|
||||
Lists all the rooms (local and remote) that the specified user is joined in
|
||||
|
||||
## `!admin users force-join-room`
|
||||
|
||||
Manually join a local user to a room
|
||||
|
||||
## `!admin users force-leave-room`
|
||||
|
||||
Manually leave a local user from a room
|
||||
|
||||
## `!admin users force-leave-remote-room`
|
||||
|
||||
Manually leave a remote room for a local user
|
||||
|
||||
## `!admin users force-demote`
|
||||
|
||||
Forces the specified user to drop their power levels to the room default, if their permissions allow and the auth check permits
|
||||
|
||||
## `!admin users make-user-admin`
|
||||
|
||||
Grant server-admin privileges to a user
|
||||
|
||||
## `!admin users put-room-tag`
|
||||
|
||||
Puts a room tag for the specified user and room ID.
|
||||
|
||||
This is primarily useful if you'd like to set your admin room to the special "System Alerts" section in Element as a way to permanently see your admin room without it being buried away in your favourites or rooms. To do this, you would pass your user, your admin room's internal ID, and the tag name `m.server_notice`.
|
||||
|
||||
## `!admin users delete-room-tag`
|
||||
|
||||
Deletes the room tag for the specified user and room ID
|
||||
|
||||
## `!admin users get-room-tags`
|
||||
|
||||
Gets all the room tags for the specified user and room ID
|
||||
|
||||
## `!admin users redact-event`
|
||||
|
||||
Attempts to forcefully redact the specified event ID from the sender user
|
||||
|
||||
This is only valid for local users
|
||||
|
||||
## `!admin users force-join-list-of-local-users`
|
||||
|
||||
Force joins a specified list of local users to join the specified room.
|
||||
|
||||
Specify a codeblock of usernames.
|
||||
|
||||
At least 1 server admin must be in the room to reduce abuse.
|
||||
|
||||
Requires the `--yes-i-want-to-do-this` flag.
|
||||
|
||||
## `!admin users force-join-all-local-users`
|
||||
|
||||
Force joins all local users to the specified room.
|
||||
|
||||
At least 1 server admin must be in the room to reduce abuse.
|
||||
|
||||
Requires the `--yes-i-want-to-do-this` flag.
|
||||
@@ -1,21 +0,0 @@
|
||||
# Command-Line Help for `continuwuity`
|
||||
|
||||
This document contains the help content for the `continuwuity` command-line program.
|
||||
|
||||
**Command Overview:**
|
||||
|
||||
* [`continuwuity`↴](#continuwuity)
|
||||
|
||||
## `continuwuity`
|
||||
|
||||
a very cool Matrix chat homeserver written in Rust
|
||||
|
||||
**Usage:** `continuwuity [OPTIONS]`
|
||||
|
||||
###### **Options:**
|
||||
|
||||
* `-c`, `--config <CONFIG>` — Path to the config TOML file (optional)
|
||||
* `-O`, `--option <OPTION>` — Override a configuration variable using TOML 'key=value' syntax
|
||||
* `--read-only` — Run in a stricter read-only --maintenance mode
|
||||
* `--maintenance` — Run in maintenance mode while refusing connections
|
||||
* `--execute <EXECUTE>` — Execute console command automatically after startup
|
||||
26
package-lock.json
generated
26
package-lock.json
generated
@@ -47,7 +47,6 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -729,7 +728,6 @@
|
||||
"integrity": "sha512-wf41bbFIzqQsGkrDal2eVC4cxN6II1k4bUo1g7OFuvWeEOJzjoeK4R5xxKM9g5hRjbGAJs6OiQaGpASvUnDrsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rspack/core": "1.6.5",
|
||||
"@rspack/lite-tapable": "~1.1.0",
|
||||
@@ -989,16 +987,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rspack/lite-tapable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.0.1.tgz",
|
||||
"integrity": "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/plugin-react-refresh": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/plugin-react-refresh/-/plugin-react-refresh-1.5.3.tgz",
|
||||
@@ -1025,7 +1013,6 @@
|
||||
"integrity": "sha512-vYbHDoAy7fjQC8hYM4rRgkrsN48CZNSFpD1WEr6lZGXDQcWWrrpFjWw3LK2OQ04bZ0OXZeqapP2rgg/jTyPaZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@mdx-js/mdx": "^3.1.1",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
@@ -1288,7 +1275,6 @@
|
||||
"integrity": "sha512-m93nmR0iY3N9Y+9Xi2xCA0NfDnTZVYauJl2SJ9bqRhJmxFHAbWe5f4Ik3VI0gK1g3lvfQ3eBpZihkpUtxCJFBw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rspress/shared": "2.0.0-rc.1",
|
||||
"@unhead/react": "^2.0.19",
|
||||
@@ -1423,7 +1409,6 @@
|
||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
@@ -1592,7 +1577,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -1774,7 +1758,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.25",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
@@ -1973,7 +1956,8 @@
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
@@ -4250,7 +4234,6 @@
|
||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -4261,7 +4244,6 @@
|
||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -4298,7 +4280,6 @@
|
||||
"integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -4325,7 +4306,6 @@
|
||||
"integrity": "sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.23.1",
|
||||
"react-router": "6.30.2"
|
||||
@@ -4663,6 +4643,7 @@
|
||||
"integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -4836,7 +4817,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Name: continuwuity
|
||||
Version: {{{ git_repo_version }}}
|
||||
Release: 1%{?dist}
|
||||
Summary: Very cool Matrix chat homeserver written in Rust
|
||||
Summary: A Matrix homeserver written in Rust.
|
||||
|
||||
License: Apache-2.0 AND MIT
|
||||
|
||||
@@ -23,7 +23,7 @@ Requires: glibc
|
||||
Requires: libstdc++
|
||||
|
||||
%global _description %{expand:
|
||||
A cool hard fork of Conduit, a Matrix homeserver written in Rust}
|
||||
A Matrix homeserver written in Rust, the official continuation of the conduwuit homeserver.}
|
||||
|
||||
%description %{_description}
|
||||
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
tag-message = "chore: Release v{{version}}"
|
||||
tag-prefix = ""
|
||||
shared-version = true
|
||||
|
||||
publish = false
|
||||
|
||||
sign-commit = true
|
||||
sign-tag = true
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_admin"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -2,49 +2,60 @@
|
||||
use conduwuit::Result;
|
||||
|
||||
use crate::{
|
||||
appservice, appservice::AppserviceCommand, check, check::CheckCommand, context::Context,
|
||||
debug, debug::DebugCommand, federation, federation::FederationCommand, media,
|
||||
media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand, server,
|
||||
server::ServerCommand, user, user::UserCommand,
|
||||
appservice::{self, AppserviceCommand},
|
||||
check::{self, CheckCommand},
|
||||
context::Context,
|
||||
debug::{self, DebugCommand},
|
||||
federation::{self, FederationCommand},
|
||||
media::{self, MediaCommand},
|
||||
query::{self, QueryCommand},
|
||||
room::{self, RoomCommand},
|
||||
server::{self, ServerCommand},
|
||||
token::{self, TokenCommand},
|
||||
user::{self, UserCommand},
|
||||
};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = conduwuit_core::name(), version = conduwuit_core::version())]
|
||||
pub enum AdminCommand {
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing appservices
|
||||
/// Commands for managing appservices
|
||||
Appservices(AppserviceCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing local users
|
||||
/// Commands for managing local users
|
||||
Users(UserCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing rooms
|
||||
/// Commands for managing registration tokens
|
||||
Token(TokenCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// Commands for managing rooms
|
||||
Rooms(RoomCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing federation
|
||||
/// Commands for managing federation
|
||||
Federation(FederationCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing the server
|
||||
/// Commands for managing the server
|
||||
Server(ServerCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing media
|
||||
/// Commands for managing media
|
||||
Media(MediaCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for checking integrity
|
||||
/// Commands for checking integrity
|
||||
Check(CheckCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for debugging things
|
||||
/// Commands for debugging things
|
||||
Debug(DebugCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Low-level queries for database getters and iterators
|
||||
/// Low-level queries for database getters and iterators
|
||||
Query(QueryCommand),
|
||||
}
|
||||
|
||||
@@ -64,6 +75,11 @@ pub(super) async fn process(command: AdminCommand, context: &Context<'_>) -> Res
|
||||
context.bail_restricted()?;
|
||||
user::process(command, context).await
|
||||
},
|
||||
| Token(command) => {
|
||||
// token commands are all restricted
|
||||
context.bail_restricted()?;
|
||||
token::process(command, context).await
|
||||
},
|
||||
| Rooms(command) => room::process(command, context).await,
|
||||
| Federation(command) => federation::process(command, context).await,
|
||||
| Server(command) => server::process(command, context).await,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[admin_command_dispatch]
|
||||
pub enum AppserviceCommand {
|
||||
/// - Register an appservice using its registration YAML
|
||||
/// Register an appservice using its registration YAML
|
||||
///
|
||||
/// This command needs a YAML generated by an appservice (such as a bridge),
|
||||
/// which must be provided in a Markdown code block below the command.
|
||||
@@ -17,7 +17,7 @@ pub enum AppserviceCommand {
|
||||
/// the old one.
|
||||
Register,
|
||||
|
||||
/// - Unregister an appservice using its ID
|
||||
/// Unregister an appservice using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
Unregister {
|
||||
@@ -25,7 +25,7 @@ pub enum AppserviceCommand {
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - Show an appservice's config using its ID
|
||||
/// Show an appservice's config using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
#[clap(alias("show"))]
|
||||
@@ -34,7 +34,7 @@ pub enum AppserviceCommand {
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - List all the currently registered appservices
|
||||
/// List all the currently registered appservices
|
||||
#[clap(alias("list"))]
|
||||
ListRegistered,
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
|
||||
use crate::Context;
|
||||
|
||||
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
|
||||
/// every user in our database (remote and local). Reports total count, any
|
||||
/// errors if there were any, etc
|
||||
#[implement(Context, params = "<'_>")]
|
||||
pub(super) async fn check_all_users(&self) -> Result {
|
||||
let timer = tokio::time::Instant::now();
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum CheckCommand {
|
||||
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
|
||||
/// every user in our database (remote and local). Reports total count, any
|
||||
/// errors if there were any, etc
|
||||
CheckAllUsers,
|
||||
}
|
||||
|
||||
@@ -12,18 +12,18 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum DebugCommand {
|
||||
/// - Echo input of admin command
|
||||
/// Echo input of admin command
|
||||
Echo {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
/// - Get the auth_chain of a PDU
|
||||
/// Get the auth_chain of a PDU
|
||||
GetAuthChain {
|
||||
/// An event ID (the $ character followed by the base64 reference hash)
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Parse and print a PDU from a JSON
|
||||
/// Parse and print a PDU from a JSON
|
||||
///
|
||||
/// The PDU event is only checked for validity and is not added to the
|
||||
/// database.
|
||||
@@ -32,13 +32,13 @@ pub enum DebugCommand {
|
||||
/// the command.
|
||||
ParsePdu,
|
||||
|
||||
/// - Retrieve and print a PDU by EventID from the Continuwuity database
|
||||
/// Retrieve and print a PDU by EventID from the Continuwuity database
|
||||
GetPdu {
|
||||
/// An event ID (a $ followed by the base64 reference hash)
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Retrieve and print a PDU by PduId from the Continuwuity database
|
||||
/// Retrieve and print a PDU by PduId from the Continuwuity database
|
||||
GetShortPdu {
|
||||
/// Shortroomid integer
|
||||
shortroomid: ShortRoomId,
|
||||
@@ -47,7 +47,7 @@ pub enum DebugCommand {
|
||||
shorteventid: ShortEventId,
|
||||
},
|
||||
|
||||
/// - Attempts to retrieve a PDU from a remote server. **Does not** insert
|
||||
/// Attempts to retrieve a PDU from a remote server. **Does not** insert
|
||||
/// it into the database
|
||||
/// or persist it anywhere.
|
||||
GetRemotePdu {
|
||||
@@ -59,7 +59,7 @@ pub enum DebugCommand {
|
||||
server: OwnedServerName,
|
||||
},
|
||||
|
||||
/// - Same as `get-remote-pdu` but accepts a codeblock newline delimited
|
||||
/// Same as `get-remote-pdu` but accepts a codeblock newline delimited
|
||||
/// list of PDUs and a single server to fetch from
|
||||
GetRemotePduList {
|
||||
/// Argument for us to attempt to fetch all the events from the
|
||||
@@ -71,7 +71,7 @@ pub enum DebugCommand {
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Gets all the room state events for the specified room.
|
||||
/// Gets all the room state events for the specified room.
|
||||
///
|
||||
/// This is functionally equivalent to `GET
|
||||
/// /_matrix/client/v3/rooms/{roomid}/state`, except the admin command does
|
||||
@@ -86,7 +86,7 @@ pub enum DebugCommand {
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Get and display signing keys from local cache or remote server.
|
||||
/// Get and display signing keys from local cache or remote server.
|
||||
GetSigningKeys {
|
||||
server_name: Option<OwnedServerName>,
|
||||
|
||||
@@ -97,23 +97,23 @@ pub enum DebugCommand {
|
||||
query: bool,
|
||||
},
|
||||
|
||||
/// - Get and display signing keys from local cache or remote server.
|
||||
/// Get and display signing keys from local cache or remote server.
|
||||
GetVerifyKeys {
|
||||
server_name: Option<OwnedServerName>,
|
||||
},
|
||||
|
||||
/// - Sends a federation request to the remote server's
|
||||
/// Sends a federation request to the remote server's
|
||||
/// `/_matrix/federation/v1/version` endpoint and measures the latency it
|
||||
/// took for the server to respond
|
||||
Ping {
|
||||
server: OwnedServerName,
|
||||
},
|
||||
|
||||
/// - Forces device lists for all local and remote users to be updated (as
|
||||
/// Forces device lists for all local and remote users to be updated (as
|
||||
/// having new keys available)
|
||||
ForceDeviceListUpdates,
|
||||
|
||||
/// - Change tracing log level/filter on the fly
|
||||
/// Change tracing log level/filter on the fly
|
||||
///
|
||||
/// This accepts the same format as the `log` config option.
|
||||
ChangeLogLevel {
|
||||
@@ -125,34 +125,34 @@ pub enum DebugCommand {
|
||||
reset: bool,
|
||||
},
|
||||
|
||||
/// - Verify JSON signatures
|
||||
/// Verify JSON signatures
|
||||
///
|
||||
/// This command needs a JSON blob provided in a Markdown code block below
|
||||
/// the command.
|
||||
VerifyJson,
|
||||
|
||||
/// - Verify PDU
|
||||
/// Verify PDU
|
||||
///
|
||||
/// This re-verifies a PDU existing in the database found by ID.
|
||||
VerifyPdu {
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Prints the very first PDU in the specified room (typically
|
||||
/// Prints the very first PDU in the specified room (typically
|
||||
/// m.room.create)
|
||||
FirstPduInRoom {
|
||||
/// The room ID
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Prints the latest ("last") PDU in the specified room (typically a
|
||||
/// Prints the latest ("last") PDU in the specified room (typically a
|
||||
/// message)
|
||||
LatestPduInRoom {
|
||||
/// The room ID
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Forcefully replaces the room state of our local copy of the specified
|
||||
/// Forcefully replaces the room state of our local copy of the specified
|
||||
/// room, with the copy (auth chain and room state events) the specified
|
||||
/// remote server says.
|
||||
///
|
||||
@@ -176,7 +176,7 @@ pub enum DebugCommand {
|
||||
event_id: Option<OwnedEventId>,
|
||||
},
|
||||
|
||||
/// - Runs a server name through Continuwuity's true destination resolution
|
||||
/// Runs a server name through Continuwuity's true destination resolution
|
||||
/// process
|
||||
///
|
||||
/// Useful for debugging well-known issues
|
||||
@@ -187,7 +187,7 @@ pub enum DebugCommand {
|
||||
no_cache: bool,
|
||||
},
|
||||
|
||||
/// - Print extended memory usage
|
||||
/// Print extended memory usage
|
||||
///
|
||||
/// Optional argument is a character mask (a sequence of characters in any
|
||||
/// order) which enable additional extended statistics. Known characters are
|
||||
@@ -196,23 +196,23 @@ pub enum DebugCommand {
|
||||
opts: Option<String>,
|
||||
},
|
||||
|
||||
/// - Print general tokio runtime metric totals.
|
||||
/// Print general tokio runtime metric totals.
|
||||
RuntimeMetrics,
|
||||
|
||||
/// - Print detailed tokio runtime metrics accumulated since last command
|
||||
/// Print detailed tokio runtime metrics accumulated since last command
|
||||
/// invocation.
|
||||
RuntimeInterval,
|
||||
|
||||
/// - Print the current time
|
||||
/// Print the current time
|
||||
Time,
|
||||
|
||||
/// - List dependencies
|
||||
/// List dependencies
|
||||
ListDependencies {
|
||||
#[arg(short, long)]
|
||||
names: bool,
|
||||
},
|
||||
|
||||
/// - Get database statistics
|
||||
/// Get database statistics
|
||||
DatabaseStats {
|
||||
property: Option<String>,
|
||||
|
||||
@@ -220,10 +220,10 @@ pub enum DebugCommand {
|
||||
map: Option<String>,
|
||||
},
|
||||
|
||||
/// - Trim memory usage
|
||||
/// Trim memory usage
|
||||
TrimMemory,
|
||||
|
||||
/// - List database files
|
||||
/// List database files
|
||||
DatabaseFiles {
|
||||
map: Option<String>,
|
||||
|
||||
@@ -231,7 +231,7 @@ pub enum DebugCommand {
|
||||
level: Option<i32>,
|
||||
},
|
||||
|
||||
/// - Developer test stubs
|
||||
/// Developer test stubs
|
||||
#[command(subcommand)]
|
||||
#[allow(non_snake_case)]
|
||||
#[clap(hide(true))]
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum FederationCommand {
|
||||
/// - List all rooms we are currently handling an incoming pdu from
|
||||
/// List all rooms we are currently handling an incoming pdu from
|
||||
IncomingFederation,
|
||||
|
||||
/// - Disables incoming federation handling for a room.
|
||||
/// Disables incoming federation handling for a room.
|
||||
DisableRoom {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Enables incoming federation handling for a room again.
|
||||
/// Enables incoming federation handling for a room again.
|
||||
EnableRoom {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Fetch `/.well-known/matrix/support` from the specified server
|
||||
/// Fetch `/.well-known/matrix/support` from the specified server
|
||||
///
|
||||
/// Despite the name, this is not a federation endpoint and does not go
|
||||
/// through the federation / server resolution process as per-spec this is
|
||||
@@ -35,7 +35,7 @@ pub enum FederationCommand {
|
||||
server_name: OwnedServerName,
|
||||
},
|
||||
|
||||
/// - Lists all the rooms we share/track with the specified *remote* user
|
||||
/// Lists all the rooms we share/track with the specified *remote* user
|
||||
RemoteUserInRooms {
|
||||
user_id: OwnedUserId,
|
||||
},
|
||||
|
||||
@@ -10,20 +10,20 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MediaCommand {
|
||||
/// - Deletes a single media file from our database and on the filesystem
|
||||
/// Deletes a single media file from our database and on the filesystem
|
||||
/// via a single MXC URL or event ID (not redacted)
|
||||
Delete {
|
||||
/// The MXC URL to delete
|
||||
#[arg(long)]
|
||||
mxc: Option<OwnedMxcUri>,
|
||||
|
||||
/// - The message event ID which contains the media and thumbnail MXC
|
||||
/// The message event ID which contains the media and thumbnail MXC
|
||||
/// URLs
|
||||
#[arg(long)]
|
||||
event_id: Option<OwnedEventId>,
|
||||
},
|
||||
|
||||
/// - Deletes a codeblock list of MXC URLs from our database and on the
|
||||
/// Deletes a codeblock list of MXC URLs from our database and on the
|
||||
/// filesystem. This will always ignore errors.
|
||||
DeleteList,
|
||||
|
||||
@@ -40,33 +40,33 @@ pub enum MediaCommand {
|
||||
/// * Delete all remote and local media from 3 days ago, up until now:
|
||||
///
|
||||
/// `!admin media delete-past-remote-media -a 3d
|
||||
/// --yes-i-want-to-delete-local-media`
|
||||
///-yes-i-want-to-delete-local-media`
|
||||
#[command(verbatim_doc_comment)]
|
||||
DeletePastRemoteMedia {
|
||||
/// - The relative time (e.g. 30s, 5m, 7d) from now within which to
|
||||
/// The relative time (e.g. 30s, 5m, 7d) from now within which to
|
||||
/// search
|
||||
duration: String,
|
||||
|
||||
/// - Only delete media created before [duration] ago
|
||||
/// Only delete media created before [duration] ago
|
||||
#[arg(long, short)]
|
||||
before: bool,
|
||||
|
||||
/// - Only delete media created after [duration] ago
|
||||
/// Only delete media created after [duration] ago
|
||||
#[arg(long, short)]
|
||||
after: bool,
|
||||
|
||||
/// - Long argument to additionally delete local media
|
||||
/// Long argument to additionally delete local media
|
||||
#[arg(long)]
|
||||
yes_i_want_to_delete_local_media: bool,
|
||||
},
|
||||
|
||||
/// - Deletes all the local media from a local user on our server. This will
|
||||
/// Deletes all the local media from a local user on our server. This will
|
||||
/// always ignore errors by default.
|
||||
DeleteAllFromUser {
|
||||
username: String,
|
||||
},
|
||||
|
||||
/// - Deletes all remote media from the specified remote server. This will
|
||||
/// Deletes all remote media from the specified remote server. This will
|
||||
/// always ignore errors by default.
|
||||
DeleteAllFromServer {
|
||||
server_name: OwnedServerName,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
pub(crate) mod query;
|
||||
pub(crate) mod room;
|
||||
pub(crate) mod server;
|
||||
pub(crate) mod token;
|
||||
pub(crate) mod user;
|
||||
|
||||
extern crate conduwuit_api as api;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||
pub enum AccountDataCommand {
|
||||
/// - Returns all changes to the account data that happened after `since`.
|
||||
/// Returns all changes to the account data that happened after `since`.
|
||||
ChangesSince {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
@@ -19,7 +19,7 @@ pub enum AccountDataCommand {
|
||||
room_id: Option<OwnedRoomId>,
|
||||
},
|
||||
|
||||
/// - Searches the account data for a specific kind.
|
||||
/// Searches the account data for a specific kind.
|
||||
AccountDataGet {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||
pub enum AppserviceCommand {
|
||||
/// - Gets the appservice registration info/details from the ID as a string
|
||||
/// Gets the appservice registration info/details from the ID as a string
|
||||
GetRegistration {
|
||||
/// Appservice registration ID
|
||||
appservice_id: String,
|
||||
},
|
||||
|
||||
/// - Gets all appservice registrations with their ID and registration info
|
||||
/// Gets all appservice registrations with their ID and registration info
|
||||
All,
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ pub enum GlobalsCommand {
|
||||
|
||||
LastCheckForAnnouncementsId,
|
||||
|
||||
/// - This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
|
||||
/// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
|
||||
/// for the server.
|
||||
SigningKeysFor {
|
||||
origin: OwnedServerName,
|
||||
|
||||
@@ -28,55 +28,55 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// Query tables from database
|
||||
pub enum QueryCommand {
|
||||
/// - account_data.rs iterators and getters
|
||||
/// account_data.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
AccountData(AccountDataCommand),
|
||||
|
||||
/// - appservice.rs iterators and getters
|
||||
/// appservice.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Appservice(AppserviceCommand),
|
||||
|
||||
/// - presence.rs iterators and getters
|
||||
/// presence.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Presence(PresenceCommand),
|
||||
|
||||
/// - rooms/alias.rs iterators and getters
|
||||
/// rooms/alias.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomAlias(RoomAliasCommand),
|
||||
|
||||
/// - rooms/state_cache iterators and getters
|
||||
/// rooms/state_cache iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomStateCache(RoomStateCacheCommand),
|
||||
|
||||
/// - rooms/timeline iterators and getters
|
||||
/// rooms/timeline iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomTimeline(RoomTimelineCommand),
|
||||
|
||||
/// - globals.rs iterators and getters
|
||||
/// globals.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Globals(GlobalsCommand),
|
||||
|
||||
/// - sending.rs iterators and getters
|
||||
/// sending.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Sending(SendingCommand),
|
||||
|
||||
/// - users.rs iterators and getters
|
||||
/// users.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Users(UsersCommand),
|
||||
|
||||
/// - resolver service
|
||||
/// resolver service
|
||||
#[command(subcommand)]
|
||||
Resolver(ResolverCommand),
|
||||
|
||||
/// - pusher service
|
||||
/// pusher service
|
||||
#[command(subcommand)]
|
||||
Pusher(PusherCommand),
|
||||
|
||||
/// - short service
|
||||
/// short service
|
||||
#[command(subcommand)]
|
||||
Short(ShortCommand),
|
||||
|
||||
/// - raw service
|
||||
/// raw service
|
||||
#[command(subcommand)]
|
||||
Raw(RawCommand),
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/presence.rs
|
||||
pub enum PresenceCommand {
|
||||
/// - Returns the latest presence event for the given user.
|
||||
/// Returns the latest presence event for the given user.
|
||||
GetPresence {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
},
|
||||
|
||||
/// - Iterator of the most recent presence updates that happened after the
|
||||
/// Iterator of the most recent presence updates that happened after the
|
||||
/// event with id `since`.
|
||||
PresenceSince {
|
||||
/// UNIX timestamp since (u64)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum PusherCommand {
|
||||
/// - Returns all the pushers for the user.
|
||||
/// Returns all the pushers for the user.
|
||||
GetPushers {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
/// Query tables from database
|
||||
pub enum RawCommand {
|
||||
/// - List database maps
|
||||
/// List database maps
|
||||
RawMaps,
|
||||
|
||||
/// - Raw database query
|
||||
/// Raw database query
|
||||
RawGet {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -32,7 +32,7 @@ pub enum RawCommand {
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// - Raw database delete (for string keys)
|
||||
/// Raw database delete (for string keys)
|
||||
RawDel {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -41,7 +41,7 @@ pub enum RawCommand {
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
/// Raw database keys iteration
|
||||
RawKeys {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -50,7 +50,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database key size breakdown
|
||||
/// Raw database key size breakdown
|
||||
RawKeysSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -59,7 +59,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database keys total bytes
|
||||
/// Raw database keys total bytes
|
||||
RawKeysTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -68,7 +68,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database values size breakdown
|
||||
/// Raw database values size breakdown
|
||||
RawValsSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -77,7 +77,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database values total bytes
|
||||
/// Raw database values total bytes
|
||||
RawValsTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -86,7 +86,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
/// Raw database items iteration
|
||||
RawIter {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -95,7 +95,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
/// Raw database keys iteration
|
||||
RawKeysFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -108,7 +108,7 @@ pub enum RawCommand {
|
||||
limit: Option<usize>,
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
/// Raw database items iteration
|
||||
RawIterFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -121,7 +121,7 @@ pub enum RawCommand {
|
||||
limit: Option<usize>,
|
||||
},
|
||||
|
||||
/// - Raw database record count
|
||||
/// Raw database record count
|
||||
RawCount {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -130,7 +130,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Compact database
|
||||
/// Compact database
|
||||
Compact {
|
||||
#[arg(short, long, alias("column"))]
|
||||
map: Option<Vec<String>>,
|
||||
|
||||
@@ -13,13 +13,13 @@ pub enum RoomAliasCommand {
|
||||
alias: OwnedRoomAliasId,
|
||||
},
|
||||
|
||||
/// - Iterator of all our local room aliases for the room ID
|
||||
/// Iterator of all our local room aliases for the room ID
|
||||
LocalAliasesForRoom {
|
||||
/// Full room ID
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Iterator of all our local aliases in our database with their room IDs
|
||||
/// Iterator of all our local aliases in our database with their room IDs
|
||||
AllLocalAliases,
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/sending.rs
|
||||
pub enum SendingCommand {
|
||||
/// - Queries database for all `servercurrentevent_data`
|
||||
/// Queries database for all `servercurrentevent_data`
|
||||
ActiveRequests,
|
||||
|
||||
/// - Queries database for `servercurrentevent_data` but for a specific
|
||||
/// Queries database for `servercurrentevent_data` but for a specific
|
||||
/// destination
|
||||
///
|
||||
/// This command takes only *one* format of these arguments:
|
||||
@@ -34,7 +34,7 @@ pub enum SendingCommand {
|
||||
push_key: Option<String>,
|
||||
},
|
||||
|
||||
/// - Queries database for `servernameevent_data` which are the queued up
|
||||
/// Queries database for `servernameevent_data` which are the queued up
|
||||
/// requests that will eventually be sent
|
||||
///
|
||||
/// This command takes only *one* format of these arguments:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomAliasCommand {
|
||||
/// - Make an alias point to a room.
|
||||
/// Make an alias point to a room.
|
||||
Set {
|
||||
#[arg(short, long)]
|
||||
/// Set the alias even if a room is already using it
|
||||
@@ -22,20 +22,20 @@ pub enum RoomAliasCommand {
|
||||
room_alias_localpart: String,
|
||||
},
|
||||
|
||||
/// - Remove a local alias
|
||||
/// Remove a local alias
|
||||
Remove {
|
||||
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
|
||||
room_alias_localpart: String,
|
||||
},
|
||||
|
||||
/// - Show which room is using an alias
|
||||
/// Show which room is using an alias
|
||||
Which {
|
||||
/// The alias localpart to look up (`alias`, not
|
||||
/// `#alias:servername.tld`)
|
||||
room_alias_localpart: String,
|
||||
},
|
||||
|
||||
/// - List aliases currently being used
|
||||
/// List aliases currently being used
|
||||
List {
|
||||
/// If set, only list the aliases for this room
|
||||
room_id: Option<OwnedRoomId>,
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomDirectoryCommand {
|
||||
/// - Publish a room to the room directory
|
||||
/// Publish a room to the room directory
|
||||
Publish {
|
||||
/// The room id of the room to publish
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Unpublish a room to the room directory
|
||||
/// Unpublish a room to the room directory
|
||||
Unpublish {
|
||||
/// The room id of the room to unpublish
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - List rooms that are published
|
||||
/// List rooms that are published
|
||||
List {
|
||||
page: Option<usize>,
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomInfoCommand {
|
||||
/// - List joined members in a room
|
||||
/// List joined members in a room
|
||||
ListJoinedMembers {
|
||||
room_id: OwnedRoomId,
|
||||
|
||||
@@ -17,7 +17,7 @@ pub enum RoomInfoCommand {
|
||||
local_only: bool,
|
||||
},
|
||||
|
||||
/// - Displays room topic
|
||||
/// Displays room topic
|
||||
///
|
||||
/// Room topics can be huge, so this is in its
|
||||
/// own separate command
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomCommand {
|
||||
/// - List all rooms the server knows about
|
||||
/// List all rooms the server knows about
|
||||
#[clap(alias = "list")]
|
||||
ListRooms {
|
||||
page: Option<usize>,
|
||||
@@ -37,22 +37,22 @@ pub enum RoomCommand {
|
||||
},
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - View information about a room we know about
|
||||
/// View information about a room we know about
|
||||
Info(RoomInfoCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Manage moderation of remote or local rooms
|
||||
/// Manage moderation of remote or local rooms
|
||||
Moderation(RoomModerationCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Manage rooms' aliases
|
||||
/// Manage rooms' aliases
|
||||
Alias(RoomAliasCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Manage the room directory
|
||||
/// Manage the room directory
|
||||
Directory(RoomDirectoryCommand),
|
||||
|
||||
/// - Check if we know about a room
|
||||
/// Check if we know about a room
|
||||
Exists {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomModerationCommand {
|
||||
/// - Bans a room from local users joining and evicts all our local users
|
||||
/// Bans a room from local users joining and evicts all our local users
|
||||
/// (including server
|
||||
/// admins)
|
||||
/// from the room. Also blocks any invites (local and remote) for the
|
||||
@@ -24,19 +24,19 @@ pub enum RoomModerationCommand {
|
||||
room: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Bans a list of rooms (room IDs and room aliases) from a newline
|
||||
/// Bans a list of rooms (room IDs and room aliases) from a newline
|
||||
/// delimited codeblock similar to `user deactivate-all`. Applies the same
|
||||
/// steps as ban-room
|
||||
BanListOfRooms,
|
||||
|
||||
/// - Unbans a room to allow local users to join again
|
||||
/// Unbans a room to allow local users to join again
|
||||
UnbanRoom {
|
||||
/// The room in the format of `!roomid:example.com` or a room alias in
|
||||
/// the format of `#roomalias:example.com`
|
||||
room: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - List of all rooms we have banned
|
||||
/// List of all rooms we have banned
|
||||
ListBannedRooms {
|
||||
#[arg(long)]
|
||||
/// Whether to only output room IDs without supplementary room
|
||||
|
||||
@@ -10,18 +10,18 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum ServerCommand {
|
||||
/// - Time elapsed since startup
|
||||
/// Time elapsed since startup
|
||||
Uptime,
|
||||
|
||||
/// - Show configuration values
|
||||
/// Show configuration values
|
||||
ShowConfig,
|
||||
|
||||
/// - Reload configuration values
|
||||
/// Reload configuration values
|
||||
ReloadConfig {
|
||||
path: Option<PathBuf>,
|
||||
},
|
||||
|
||||
/// - List the features built into the server
|
||||
/// List the features built into the server
|
||||
ListFeatures {
|
||||
#[arg(short, long)]
|
||||
available: bool,
|
||||
@@ -33,35 +33,35 @@ pub enum ServerCommand {
|
||||
comma: bool,
|
||||
},
|
||||
|
||||
/// - Print database memory usage statistics
|
||||
/// Print database memory usage statistics
|
||||
MemoryUsage,
|
||||
|
||||
/// - Clears all of Continuwuity's caches
|
||||
/// Clears all of Continuwuity's caches
|
||||
ClearCaches,
|
||||
|
||||
/// - Performs an online backup of the database (only available for RocksDB
|
||||
/// Performs an online backup of the database (only available for RocksDB
|
||||
/// at the moment)
|
||||
BackupDatabase,
|
||||
|
||||
/// - List database backups
|
||||
/// List database backups
|
||||
ListBackups,
|
||||
|
||||
/// - Send a message to the admin room.
|
||||
/// Send a message to the admin room.
|
||||
AdminNotice {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
/// - Hot-reload the server
|
||||
/// Hot-reload the server
|
||||
#[clap(alias = "reload")]
|
||||
ReloadMods,
|
||||
|
||||
#[cfg(unix)]
|
||||
/// - Restart the server
|
||||
/// Restart the server
|
||||
Restart {
|
||||
#[arg(short, long)]
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Shutdown the server
|
||||
/// Shutdown the server
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
76
src/admin/token/commands.rs
Normal file
76
src/admin/token/commands.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use conduwuit::{Err, Result, utils};
|
||||
use conduwuit_macros::admin_command;
|
||||
use futures::StreamExt;
|
||||
use service::registration_tokens::TokenExpires;
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn issue_token(&self, expires: super::TokenExpires) -> Result {
|
||||
let expires = {
|
||||
if expires.immortal {
|
||||
None
|
||||
} else if let Some(max_uses) = expires.max_uses {
|
||||
Some(TokenExpires::AfterUses(max_uses))
|
||||
} else if expires.once {
|
||||
Some(TokenExpires::AfterUses(1))
|
||||
} else if let Some(max_age) = expires
|
||||
.max_age
|
||||
.as_deref()
|
||||
.map(|max_age| utils::time::timepoint_from_now(utils::time::parse_duration(max_age)?))
|
||||
.transpose()?
|
||||
{
|
||||
Some(TokenExpires::AfterTime(max_age))
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
};
|
||||
|
||||
let (token, info) = self
|
||||
.services
|
||||
.registration_tokens
|
||||
.issue_token(self.sender_or_service_user().into(), expires);
|
||||
|
||||
self.write_str(&format!(
|
||||
"New registration token issued: `{token}`. {}.",
|
||||
if let Some(expires) = info.expires {
|
||||
format!("{expires}")
|
||||
} else {
|
||||
"Never expires".to_owned()
|
||||
}
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn revoke_token(&self, token: String) -> Result {
|
||||
let Some(token) = self
|
||||
.services
|
||||
.registration_tokens
|
||||
.validate_token(token)
|
||||
.await
|
||||
else {
|
||||
return Err!("This token does not exist or has already expired.");
|
||||
};
|
||||
|
||||
self.services.registration_tokens.revoke_token(token)?;
|
||||
|
||||
self.write_str("Token revoked successfully.").await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_tokens(&self) -> Result {
|
||||
let tokens: Vec<_> = self
|
||||
.services
|
||||
.registration_tokens
|
||||
.iterate_tokens()
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
self.write_str(&format!("Found {} registration tokens:\n", tokens.len()))
|
||||
.await?;
|
||||
|
||||
for token in tokens {
|
||||
self.write_str(&format!("- {token}\n")).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
51
src/admin/token/mod.rs
Normal file
51
src/admin/token/mod.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
mod commands;
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
use conduwuit::Result;
|
||||
|
||||
use crate::admin_command_dispatch;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum TokenCommand {
|
||||
/// Issue a new registration token
|
||||
#[clap(name = "issue")]
|
||||
IssueToken {
|
||||
/// When this token will expire.
|
||||
#[command(flatten)]
|
||||
expires: TokenExpires,
|
||||
},
|
||||
|
||||
/// Revoke a registration token
|
||||
#[clap(name = "revoke")]
|
||||
RevokeToken {
|
||||
/// The token to revoke.
|
||||
token: String,
|
||||
},
|
||||
|
||||
/// List all registration tokens
|
||||
#[clap(name = "list")]
|
||||
ListTokens,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[group(required = true, multiple = false)]
|
||||
pub struct TokenExpires {
|
||||
/// The maximum number of times this token is allowed to be used before it
|
||||
/// expires.
|
||||
#[arg(long)]
|
||||
max_uses: Option<u64>,
|
||||
|
||||
/// The maximum age of this token (e.g. 30s, 5m, 7d). It will expire after
|
||||
/// this much time has passed.
|
||||
#[arg(long)]
|
||||
max_age: Option<String>,
|
||||
|
||||
/// This token will never expire.
|
||||
#[arg(long)]
|
||||
immortal: bool,
|
||||
|
||||
/// A shortcut for `--max-uses 1`.
|
||||
#[arg(long)]
|
||||
once: bool,
|
||||
}
|
||||
@@ -280,7 +280,12 @@ pub(super) async fn unsuspend(&self, user_id: String) -> Result {
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn reset_password(&self, username: String, password: Option<String>) -> Result {
|
||||
pub(super) async fn reset_password(
|
||||
&self,
|
||||
logout: bool,
|
||||
username: String,
|
||||
password: Option<String>,
|
||||
) -> Result {
|
||||
let user_id = parse_local_user_id(self.services, &username)?;
|
||||
|
||||
if user_id == self.services.globals.server_user {
|
||||
@@ -303,7 +308,18 @@ pub(super) async fn reset_password(&self, username: String, password: Option<Str
|
||||
write!(self, "Successfully reset the password for user {user_id}: `{new_password}`")
|
||||
},
|
||||
}
|
||||
.await
|
||||
.await?;
|
||||
|
||||
if logout {
|
||||
self.services
|
||||
.users
|
||||
.all_device_ids(&user_id)
|
||||
.for_each(|device_id| self.services.users.remove_device(&user_id, device_id))
|
||||
.await;
|
||||
write!(self, "\nAll existing sessions have been logged out.").await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
@@ -1017,3 +1033,72 @@ pub(super) async fn unlock(&self, user_id: String) -> Result {
|
||||
self.write_str(&format!("User {user_id} has been unlocked."))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn logout(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
if user_id == self.services.globals.server_user {
|
||||
return Err!("Not allowed to log out the server service account.",);
|
||||
}
|
||||
|
||||
if !self.services.users.exists(&user_id).await {
|
||||
return Err!("User {user_id} does not exist.");
|
||||
}
|
||||
if self.services.users.is_admin(&user_id).await {
|
||||
return Err!("You cannot forcefully log out admin users.");
|
||||
}
|
||||
self.services
|
||||
.users
|
||||
.all_device_ids(&user_id)
|
||||
.for_each(|device_id| self.services.users.remove_device(&user_id, device_id))
|
||||
.await;
|
||||
self.write_str(&format!("User {user_id} has been logged out from all devices."))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn disable_login(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
if user_id == self.services.globals.server_user {
|
||||
return Err!("Not allowed to disable login for the server service account.",);
|
||||
}
|
||||
|
||||
if !self.services.users.exists(&user_id).await {
|
||||
return Err!("User {user_id} does not exist.");
|
||||
}
|
||||
if self.services.users.is_admin(&user_id).await {
|
||||
return Err!("Admin users cannot have their login disallowed.");
|
||||
}
|
||||
self.services.users.disable_login(&user_id);
|
||||
|
||||
self.write_str(&format!(
|
||||
"{user_id} can no longer log in. Their existing sessions remain unaffected."
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn enable_login(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
if !self.services.users.exists(&user_id).await {
|
||||
return Err!("User {user_id} does not exist.");
|
||||
}
|
||||
self.services.users.enable_login(&user_id);
|
||||
|
||||
self.write_str(&format!("{user_id} can now log in.")).await
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum UserCommand {
|
||||
/// - Create a new user
|
||||
/// Create a new user
|
||||
#[clap(alias = "create")]
|
||||
CreateUser {
|
||||
/// Username of the new user
|
||||
@@ -18,15 +18,18 @@ pub enum UserCommand {
|
||||
password: Option<String>,
|
||||
},
|
||||
|
||||
/// - Reset user password
|
||||
/// Reset user password
|
||||
ResetPassword {
|
||||
/// Log out existing sessions
|
||||
#[arg(short, long)]
|
||||
logout: bool,
|
||||
/// Username of the user for whom the password should be reset
|
||||
username: String,
|
||||
/// New password for the user, if unspecified one is generated
|
||||
password: Option<String>,
|
||||
},
|
||||
|
||||
/// - Deactivate a user
|
||||
/// Deactivate a user
|
||||
///
|
||||
/// User will be removed from all rooms by default.
|
||||
/// Use --no-leave-rooms to not leave all rooms by default.
|
||||
@@ -36,7 +39,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Deactivate a list of users
|
||||
/// Deactivate a list of users
|
||||
///
|
||||
/// Recommended to use in conjunction with list-local-users.
|
||||
///
|
||||
@@ -59,7 +62,19 @@ pub enum UserCommand {
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Suspend a user
|
||||
/// Forcefully log a user out of all of their devices.
|
||||
///
|
||||
/// This will invalidate all access tokens for the specified user,
|
||||
/// effectively logging them out from all sessions.
|
||||
/// Note that this is destructive and may result in data loss for the user,
|
||||
/// such as encryption keys. Use with caution. Can only be used in the admin
|
||||
/// room.
|
||||
Logout {
|
||||
/// Username of the user to log out
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// Suspend a user
|
||||
///
|
||||
/// Suspended users are able to log in, sync, and read messages, but are not
|
||||
/// able to send events nor redact them, cannot change their profile, and
|
||||
@@ -72,7 +87,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Unsuspend a user
|
||||
/// Unsuspend a user
|
||||
///
|
||||
/// Reverses the effects of the `suspend` command, allowing the user to send
|
||||
/// messages, change their profile, create room invites, etc.
|
||||
@@ -81,7 +96,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Lock a user
|
||||
/// Lock a user
|
||||
///
|
||||
/// Locked users are unable to use their accounts beyond logging out. This
|
||||
/// is akin to a temporary deactivation that does not change the user's
|
||||
@@ -92,7 +107,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Unlock a user
|
||||
/// Unlock a user
|
||||
///
|
||||
/// Reverses the effects of the `lock` command, allowing the user to use
|
||||
/// their account again.
|
||||
@@ -101,48 +116,64 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - List local users in the database
|
||||
/// Enable login for a user
|
||||
EnableLogin {
|
||||
/// Username of the user to enable login for
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// Disable login for a user
|
||||
///
|
||||
/// Disables login for the specified user without deactivating or locking
|
||||
/// their account. This prevents the user from obtaining new access tokens,
|
||||
/// but does not invalidate existing sessions.
|
||||
DisableLogin {
|
||||
/// Username of the user to disable login for
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// List local users in the database
|
||||
#[clap(alias = "list")]
|
||||
ListUsers,
|
||||
|
||||
/// - Lists all the rooms (local and remote) that the specified user is
|
||||
/// Lists all the rooms (local and remote) that the specified user is
|
||||
/// joined in
|
||||
ListJoinedRooms {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Manually join a local user to a room.
|
||||
/// Manually join a local user to a room.
|
||||
ForceJoinRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Manually leave a local user from a room.
|
||||
/// Manually leave a local user from a room.
|
||||
ForceLeaveRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Manually leave a remote room for a local user.
|
||||
/// Manually leave a remote room for a local user.
|
||||
ForceLeaveRemoteRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
via: Option<String>,
|
||||
},
|
||||
|
||||
/// - Forces the specified user to drop their power levels to the room
|
||||
/// Forces the specified user to drop their power levels to the room
|
||||
/// default, if their permissions allow and the auth check permits
|
||||
ForceDemote {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Grant server-admin privileges to a user.
|
||||
/// Grant server-admin privileges to a user.
|
||||
MakeUserAdmin {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Puts a room tag for the specified user and room ID.
|
||||
/// Puts a room tag for the specified user and room ID.
|
||||
///
|
||||
/// This is primarily useful if you'd like to set your admin room
|
||||
/// to the special "System Alerts" section in Element as a way to
|
||||
@@ -155,20 +186,20 @@ pub enum UserCommand {
|
||||
tag: String,
|
||||
},
|
||||
|
||||
/// - Deletes the room tag for the specified user and room ID
|
||||
/// Deletes the room tag for the specified user and room ID
|
||||
DeleteRoomTag {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomId,
|
||||
tag: String,
|
||||
},
|
||||
|
||||
/// - Gets all the room tags for the specified user and room ID
|
||||
/// Gets all the room tags for the specified user and room ID
|
||||
GetRoomTags {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Attempts to forcefully redact the specified event ID from the sender
|
||||
/// Attempts to forcefully redact the specified event ID from the sender
|
||||
/// user
|
||||
///
|
||||
/// This is only valid for local users
|
||||
@@ -176,7 +207,7 @@ pub enum UserCommand {
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Force joins a specified list of local users to join the specified
|
||||
/// Force joins a specified list of local users to join the specified
|
||||
/// room.
|
||||
///
|
||||
/// Specify a codeblock of usernames.
|
||||
@@ -191,7 +222,7 @@ pub enum UserCommand {
|
||||
yes_i_want_to_do_this: bool,
|
||||
},
|
||||
|
||||
/// - Force joins all local users to the specified room.
|
||||
/// Force joins all local users to the specified room.
|
||||
///
|
||||
/// At least 1 server admin must be in the room to reduce abuse.
|
||||
///
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_api"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -179,13 +179,18 @@ pub(crate) async fn register_route(
|
||||
},
|
||||
}
|
||||
|
||||
return Err!(Request(Forbidden("Registration has been disabled.")));
|
||||
return Err!(Request(Forbidden(
|
||||
"This server is not accepting registrations at this time."
|
||||
)));
|
||||
}
|
||||
|
||||
if is_guest
|
||||
&& (!services.config.allow_guest_registration
|
||||
|| (services.config.allow_registration
|
||||
&& services.globals.registration_token.is_some()))
|
||||
&& services
|
||||
.registration_tokens
|
||||
.get_config_file_token()
|
||||
.is_some()))
|
||||
{
|
||||
info!(
|
||||
"Guest registration disabled / registration enabled with token configured, \
|
||||
@@ -203,7 +208,9 @@ pub(crate) async fn register_route(
|
||||
rejecting registration. Guest's initial device name: \"{}\"",
|
||||
body.initial_device_display_name.as_deref().unwrap_or("")
|
||||
);
|
||||
return Err!(Request(Forbidden("Registration is temporarily disabled.")));
|
||||
return Err!(Request(Forbidden(
|
||||
"This server is not accepting registrations at this time."
|
||||
)));
|
||||
}
|
||||
|
||||
let user_id = match (body.username.as_ref(), is_guest) {
|
||||
@@ -301,7 +308,13 @@ pub(crate) async fn register_route(
|
||||
let skip_auth = body.appservice_info.is_some() || is_guest;
|
||||
|
||||
// Populate required UIAA flows
|
||||
if services.globals.registration_token.is_some() {
|
||||
if services
|
||||
.registration_tokens
|
||||
.iterate_tokens()
|
||||
.next()
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
// Registration token required
|
||||
uiaainfo.flows.push(AuthFlow {
|
||||
stages: vec![AuthType::RegistrationToken],
|
||||
@@ -323,7 +336,19 @@ pub(crate) async fn register_route(
|
||||
}
|
||||
|
||||
if uiaainfo.flows.is_empty() && !skip_auth {
|
||||
// No registration token necessary, but clients must still go through the flow
|
||||
// Registration isn't _disabled_, but there's no captcha configured and no
|
||||
// registration tokens currently set. Bail out by default unless open
|
||||
// registration was explicitly enabled.
|
||||
if !services
|
||||
.config
|
||||
.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
{
|
||||
return Err!(Request(Forbidden(
|
||||
"This server is not accepting registrations at this time."
|
||||
)));
|
||||
}
|
||||
|
||||
// We have open registration enabled (😧), provide a dummy stage
|
||||
uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Dummy] }],
|
||||
completed: Vec::new(),
|
||||
@@ -699,7 +724,7 @@ pub(crate) async fn change_password_route(
|
||||
Ok(change_password::v3::Response {})
|
||||
}
|
||||
|
||||
/// # `GET _matrix/client/r0/account/whoami`
|
||||
/// # `GET /_matrix/client/v3/account/whoami`
|
||||
///
|
||||
/// Get `user_id` of the sender user.
|
||||
///
|
||||
@@ -708,11 +733,17 @@ pub(crate) async fn whoami_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<whoami::v3::Request>,
|
||||
) -> Result<whoami::v3::Response> {
|
||||
let is_guest = services
|
||||
.users
|
||||
.is_deactivated(body.sender_user())
|
||||
.await
|
||||
.map_err(|_| {
|
||||
err!(Request(Forbidden("Application service has not registered this user.")))
|
||||
})? && body.appservice_info.is_none();
|
||||
Ok(whoami::v3::Response {
|
||||
user_id: body.sender_user().to_owned(),
|
||||
device_id: body.sender_device.clone(),
|
||||
is_guest: services.users.is_deactivated(body.sender_user()).await?
|
||||
&& body.appservice_info.is_none(),
|
||||
is_guest,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -846,19 +877,20 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route(
|
||||
|
||||
/// # `GET /_matrix/client/v1/register/m.login.registration_token/validity`
|
||||
///
|
||||
/// Checks if the provided registration token is valid at the time of checking
|
||||
///
|
||||
/// Currently does not have any ratelimiting, and this isn't very practical as
|
||||
/// there is only one registration token allowed.
|
||||
/// Checks if the provided registration token is valid at the time of checking.
|
||||
pub(crate) async fn check_registration_token_validity(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<check_registration_token_validity::v1::Request>,
|
||||
) -> Result<check_registration_token_validity::v1::Response> {
|
||||
let Some(reg_token) = services.globals.registration_token.clone() else {
|
||||
return Err!(Request(Forbidden("Server does not allow token registration")));
|
||||
};
|
||||
// TODO: ratelimit this pretty heavily
|
||||
|
||||
Ok(check_registration_token_validity::v1::Response { valid: reg_token == body.token })
|
||||
let valid = services
|
||||
.registration_tokens
|
||||
.validate_token(body.token.clone())
|
||||
.await
|
||||
.is_some();
|
||||
|
||||
Ok(check_registration_token_validity::v1::Response { valid })
|
||||
}
|
||||
|
||||
/// Runs through all the deactivation steps:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use ruma::{
|
||||
OwnedServerName, RoomId, UserId,
|
||||
RoomId, UserId,
|
||||
api::{client::membership::invite_user, federation::membership::create_invite},
|
||||
events::{
|
||||
invite_permission_config::FilterLevel,
|
||||
@@ -203,19 +203,10 @@ pub(crate) async fn invite_helper(
|
||||
))));
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(serde_json::to_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event missing origin field."))))?,
|
||||
)?)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}"))))
|
||||
})?;
|
||||
|
||||
let pdu_id = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value, true)
|
||||
.handle_incoming_pdu(recipient_user.server_name(), room_id, &event_id, value, true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
|
||||
@@ -178,7 +178,20 @@ pub async fn leave_room(
|
||||
.rooms
|
||||
.state_cache
|
||||
.left_state(user_id, room_id)
|
||||
.await?
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
// `left_state` may return an Err if the user _is_ in the room they're
|
||||
// trying to leave, but the membership cache is incorrect and
|
||||
// they're cached as being joined. In this situation
|
||||
// we save a `None` to the `roomuserid_leftcount` table, which generates
|
||||
// and sends a dummy leave to the client.
|
||||
warn!(
|
||||
?err,
|
||||
"Trying to leave room not cached as leave, sending dummy leave \
|
||||
event to client"
|
||||
);
|
||||
})
|
||||
.unwrap_or_default()
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -238,6 +238,7 @@ pub(crate) async fn create_room_route(
|
||||
event_type: TimelineEventType::RoomCreate,
|
||||
content: to_raw_value(&create_content)?,
|
||||
state_key: Some(StateKey::new()),
|
||||
timestamp: body.origin_server_ts,
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
@@ -256,6 +257,14 @@ pub(crate) async fn create_room_route(
|
||||
},
|
||||
};
|
||||
drop(state_lock);
|
||||
if let Some(expected_room_id) = body.room_id.as_ref() {
|
||||
if expected_room_id.as_str() != room_id.as_str() {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Custom room ID {expected_room_id} does not match the generated room ID \
|
||||
{room_id}.",
|
||||
)));
|
||||
}
|
||||
}
|
||||
debug!("Room created with ID {room_id}");
|
||||
let state_lock = services.rooms.state.mutex.lock(&room_id).await;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use conduwuit::{
|
||||
Err, Error, Result, debug, err, info,
|
||||
utils::{self, ReadyExt, hash},
|
||||
warn,
|
||||
};
|
||||
use conduwuit_core::{debug_error, debug_warn};
|
||||
use conduwuit_service::{Services, uiaa::SESSION_ID_LENGTH};
|
||||
@@ -12,6 +13,7 @@
|
||||
use ruma::{
|
||||
OwnedUserId, UserId,
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
session::{
|
||||
get_login_token,
|
||||
get_login_types::{
|
||||
@@ -184,6 +186,15 @@ pub(crate) async fn handle_login(
|
||||
return Err!(Request(Unknown("User ID does not belong to this homeserver")));
|
||||
}
|
||||
|
||||
if services.users.is_locked(&user_id).await? {
|
||||
return Err(Error::BadRequest(ErrorKind::UserLocked, "This account has been locked."));
|
||||
}
|
||||
|
||||
if services.users.is_login_disabled(&user_id).await {
|
||||
warn!(%user_id, "user attempted to log in with a login-disabled account");
|
||||
return Err!(Request(Forbidden("This account is not permitted to log in.")));
|
||||
}
|
||||
|
||||
if cfg!(feature = "ldap") && services.config.ldap.enable {
|
||||
match Box::pin(ldap_login(services, &user_id, &lowercased_user_id, password)).await {
|
||||
| Ok(user_id) => Ok(user_id),
|
||||
|
||||
@@ -178,15 +178,6 @@ async fn create_join_event(
|
||||
}
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event does not have an origin server name."))))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|e| err!(Request(BadJson("Event has an invalid origin server name: {e}"))))?;
|
||||
|
||||
trace!("Signing send_join event");
|
||||
services
|
||||
.server_keys
|
||||
@@ -204,7 +195,7 @@ async fn create_join_event(
|
||||
let pdu_id = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value.clone(), true)
|
||||
.handle_incoming_pdu(sender.server_name(), room_id, &event_id, value.clone(), true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
|
||||
|
||||
@@ -136,15 +136,6 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
return Err!(Request(InvalidParam("state_key does not match sender user of event.")));
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event does not have an origin server name."))))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|e| err!(Request(BadJson("Event has an invalid origin server name: {e}"))))?;
|
||||
|
||||
let mut event: JsonObject = serde_json::from_str(body.pdu.get())
|
||||
.map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?;
|
||||
|
||||
@@ -163,7 +154,7 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
let pdu_id = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, &body.room_id, &event_id, value.clone(), true)
|
||||
.handle_incoming_pdu(sender.server_name(), &body.room_id, &event_id, value.clone(), true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_build_metadata"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_core"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -146,22 +146,6 @@ pub fn check(config: &Config) -> Result {
|
||||
));
|
||||
}
|
||||
|
||||
// check if we can read the token file path, and check if the file is empty
|
||||
if config.registration_token_file.as_ref().is_some_and(|path| {
|
||||
let Ok(token) = std::fs::read_to_string(path).inspect_err(|e| {
|
||||
error!("Failed to read the registration token file: {e}");
|
||||
}) else {
|
||||
return true;
|
||||
};
|
||||
|
||||
token == String::new()
|
||||
}) {
|
||||
return Err!(Config(
|
||||
"registration_token_file",
|
||||
"Registration token file was specified but is empty or failed to be read"
|
||||
));
|
||||
}
|
||||
|
||||
if config.max_request_size < 10_000_000 {
|
||||
return Err!(Config(
|
||||
"max_request_size",
|
||||
@@ -187,29 +171,9 @@ pub fn check(config: &Config) -> Result {
|
||||
));
|
||||
}
|
||||
|
||||
if config.allow_registration
|
||||
&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
&& config.registration_token.is_none()
|
||||
&& config.registration_token_file.is_none()
|
||||
&& config.recaptcha_site_key.is_none()
|
||||
{
|
||||
return Err!(Config(
|
||||
"registration_token",
|
||||
"!! You have `allow_registration` enabled without a token or captcha configured \
|
||||
which means you are allowing ANYONE to register on your continuwuity instance \
|
||||
without any 2nd-step (e.g. registration token, captcha), which is FREQUENTLY \
|
||||
abused by malicious actors. If this is not the intended behaviour, please set a \
|
||||
registration token. For security and safety reasons, continuwuity will shut down. \
|
||||
If you are extra sure this is the desired behaviour you want, please set the \
|
||||
following config option to true:
|
||||
`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`"
|
||||
));
|
||||
}
|
||||
|
||||
if config.allow_registration
|
||||
&& config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
&& config.registration_token.is_none()
|
||||
&& config.registration_token_file.is_none()
|
||||
{
|
||||
warn!(
|
||||
"Open registration is enabled via setting \
|
||||
|
||||
@@ -53,8 +53,7 @@
|
||||
### For more information, see:
|
||||
### https://continuwuity.org/configuration.html
|
||||
"#,
|
||||
ignore = "config_paths catchall well_known tls blurhashing \
|
||||
allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure antispam"
|
||||
ignore = "config_paths catchall"
|
||||
)]
|
||||
pub struct Config {
|
||||
// Paths to config file(s). Not supposed to be set manually in the config file,
|
||||
@@ -105,7 +104,7 @@ pub struct Config {
|
||||
#[serde(default = "default_port")]
|
||||
port: ListeningPort,
|
||||
|
||||
// external structure; separate section
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub tls: TlsConfig,
|
||||
|
||||
@@ -435,7 +434,9 @@ pub struct Config {
|
||||
/// this to be high to account for extremely large room joins, slow
|
||||
/// homeservers, your own resources etc.
|
||||
///
|
||||
/// default: 300
|
||||
/// Joins have 6x the timeout.
|
||||
///
|
||||
/// default: 60
|
||||
#[serde(default = "default_federation_timeout")]
|
||||
pub federation_timeout: u64,
|
||||
|
||||
@@ -501,7 +502,19 @@ pub struct Config {
|
||||
#[serde(default = "default_appservice_idle_timeout")]
|
||||
pub appservice_idle_timeout: u64,
|
||||
|
||||
/// Notification gateway pusher idle connection pool timeout.
|
||||
/// Notification gateway pusher request connection timeout (seconds).
|
||||
///
|
||||
/// default: 15
|
||||
#[serde(default = "default_pusher_conn_timeout")]
|
||||
pub pusher_conn_timeout: u64,
|
||||
|
||||
/// Notification gateway pusher total request timeout (seconds).
|
||||
///
|
||||
/// default: 60
|
||||
#[serde(default = "default_pusher_timeout")]
|
||||
pub pusher_timeout: u64,
|
||||
|
||||
/// Notification gateway pusher idle connection pool timeout (seconds).
|
||||
///
|
||||
/// default: 15
|
||||
#[serde(default = "default_pusher_idle_timeout")]
|
||||
@@ -545,7 +558,7 @@ pub struct Config {
|
||||
/// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
///
|
||||
/// If you would like registration only via token reg, please configure
|
||||
/// `registration_token` or `registration_token_file`.
|
||||
/// `registration_token`.
|
||||
#[serde(default)]
|
||||
pub allow_registration: bool,
|
||||
|
||||
@@ -576,22 +589,14 @@ pub struct Config {
|
||||
/// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
/// to true to allow open registration without any conditions.
|
||||
///
|
||||
/// YOU NEED TO EDIT THIS OR USE registration_token_file.
|
||||
/// If you do not want to set a static token, the `!admin token` commands
|
||||
/// may also be used to manage registration tokens.
|
||||
///
|
||||
/// example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
||||
///
|
||||
/// display: sensitive
|
||||
pub registration_token: Option<String>,
|
||||
|
||||
/// Path to a file on the system that gets read for additional registration
|
||||
/// tokens. Multiple tokens can be added if you separate them with
|
||||
/// whitespace
|
||||
///
|
||||
/// continuwuity must be able to access the file, and it must not be empty
|
||||
///
|
||||
/// example: "/etc/continuwuity/.reg_token"
|
||||
pub registration_token_file: Option<PathBuf>,
|
||||
|
||||
/// The public site key for reCaptcha. If this is provided, reCaptcha
|
||||
/// becomes required during registration. If both captcha *and*
|
||||
/// registration token are enabled, both will be required during
|
||||
@@ -718,7 +723,7 @@ pub struct Config {
|
||||
#[serde(default = "default_default_room_version")]
|
||||
pub default_room_version: RoomVersionId,
|
||||
|
||||
// external structure; separate section
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub well_known: WellKnownConfig,
|
||||
|
||||
@@ -1671,6 +1676,13 @@ pub struct Config {
|
||||
#[serde(default = "default_url_preview_max_spider_size")]
|
||||
pub url_preview_max_spider_size: usize,
|
||||
|
||||
/// Total request timeout for URL previews (seconds). This includes
|
||||
/// connection, request, and response body reading time.
|
||||
///
|
||||
/// default: 120
|
||||
#[serde(default = "default_url_preview_timeout")]
|
||||
pub url_preview_timeout: u64,
|
||||
|
||||
/// Option to decide whether you would like to run the domain allowlist
|
||||
/// checks (contains and explicit) on the root domain or not. Does not apply
|
||||
/// to URL contains allowlist. Defaults to false.
|
||||
@@ -2017,19 +2029,22 @@ pub struct Config {
|
||||
/// etc. This is a hidden argument that should NOT be used in production as
|
||||
/// it is highly insecure and I will personally yell at you if I catch you
|
||||
/// using this.
|
||||
///
|
||||
/// display: hidden
|
||||
#[serde(default)]
|
||||
pub allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure:
|
||||
bool,
|
||||
|
||||
// external structure; separate section
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub ldap: LdapConfig,
|
||||
|
||||
/// Configuration for antispam support
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub antispam: Option<Antispam>,
|
||||
|
||||
// external structure; separate section
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub blurhashing: BlurhashConfig,
|
||||
#[serde(flatten)]
|
||||
@@ -2246,8 +2261,11 @@ struct ListeningAddr {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[config_example_generator(filename = "conduwuit-example.toml", section = "global.antispam")]
|
||||
pub struct Antispam {
|
||||
/// display: nested
|
||||
pub meowlnir: Option<MeowlnirConfig>,
|
||||
/// display: nested
|
||||
pub draupnir: Option<DraupnirConfig>,
|
||||
}
|
||||
|
||||
@@ -2296,7 +2314,7 @@ pub struct DraupnirConfig {
|
||||
pub secret: String,
|
||||
}
|
||||
|
||||
const DEPRECATED_KEYS: &[&str; 9] = &[
|
||||
const DEPRECATED_KEYS: &[&str] = &[
|
||||
"cache_capacity",
|
||||
"conduit_cache_capacity_modifier",
|
||||
"max_concurrent_requests",
|
||||
@@ -2306,6 +2324,7 @@ pub struct DraupnirConfig {
|
||||
"well_known_support_role",
|
||||
"well_known_support_email",
|
||||
"well_known_support_mxid",
|
||||
"registration_token_file",
|
||||
];
|
||||
|
||||
impl Config {
|
||||
@@ -2462,7 +2481,7 @@ fn default_well_known_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_federation_conn_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_federation_timeout() -> u64 { 25 }
|
||||
fn default_federation_timeout() -> u64 { 60 }
|
||||
|
||||
fn default_policy_server_request_timeout() -> u64 { 10 }
|
||||
|
||||
@@ -2480,6 +2499,10 @@ fn default_appservice_timeout() -> u64 { 35 }
|
||||
|
||||
fn default_appservice_idle_timeout() -> u64 { 300 }
|
||||
|
||||
fn default_pusher_conn_timeout() -> u64 { 15 }
|
||||
|
||||
fn default_pusher_timeout() -> u64 { 60 }
|
||||
|
||||
fn default_pusher_idle_timeout() -> u64 { 15 }
|
||||
|
||||
fn default_max_fetch_prev_events() -> u16 { 192_u16 }
|
||||
@@ -2607,6 +2630,8 @@ fn default_url_preview_max_spider_size() -> usize {
|
||||
256_000 // 256KB
|
||||
}
|
||||
|
||||
fn default_url_preview_timeout() -> u64 { 120 }
|
||||
|
||||
fn default_new_user_displayname_suffix() -> String { "🏳️⚧️".to_owned() }
|
||||
|
||||
fn default_sentry_endpoint() -> Option<Url> { None }
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_database"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -141,6 +141,10 @@ pub(super) fn open_list(db: &Arc<Engine>, maps: &[Descriptor]) -> Result<Maps> {
|
||||
name: "referencedevents",
|
||||
..descriptor::RANDOM
|
||||
},
|
||||
Descriptor {
|
||||
name: "registrationtoken_info",
|
||||
..descriptor::RANDOM_SMALL
|
||||
},
|
||||
Descriptor {
|
||||
name: "roomid_invitedcount",
|
||||
..descriptor::RANDOM_SMALL
|
||||
@@ -390,6 +394,10 @@ pub(super) fn open_list(db: &Arc<Engine>, maps: &[Descriptor]) -> Result<Maps> {
|
||||
name: "userid_lock",
|
||||
..descriptor::RANDOM_SMALL
|
||||
},
|
||||
Descriptor {
|
||||
name: "userid_logindisabled",
|
||||
..descriptor::RANDOM_SMALL
|
||||
},
|
||||
Descriptor {
|
||||
name: "userid_presenceid",
|
||||
..descriptor::RANDOM_SMALL
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_macros"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -78,6 +78,8 @@ fn generate_example(input: &ItemStruct, args: &[Meta], write: bool) -> Result<To
|
||||
}
|
||||
|
||||
let mut summary: Vec<TokenStream2> = Vec::new();
|
||||
let mut nested_displays: Vec<TokenStream2> = Vec::new();
|
||||
|
||||
if let Fields::Named(FieldsNamed { named, .. }) = &input.fields {
|
||||
for field in named {
|
||||
let Some(ident) = &field.ident else {
|
||||
@@ -92,35 +94,6 @@ fn generate_example(input: &ItemStruct, args: &[Meta], write: bool) -> Result<To
|
||||
continue;
|
||||
};
|
||||
|
||||
let doc = get_doc_comment(field)
|
||||
.unwrap_or_else(|| undocumented.into())
|
||||
.trim_end()
|
||||
.to_owned();
|
||||
|
||||
let doc = if doc.ends_with('#') {
|
||||
format!("{doc}\n")
|
||||
} else {
|
||||
format!("{doc}\n#\n")
|
||||
};
|
||||
|
||||
let default = get_doc_comment_line(field, "default")
|
||||
.or_else(|| get_default(field))
|
||||
.unwrap_or_default();
|
||||
|
||||
let default = if !default.is_empty() {
|
||||
format!(" {default}")
|
||||
} else {
|
||||
default
|
||||
};
|
||||
|
||||
if let Some(file) = file.as_mut() {
|
||||
file.write_fmt(format_args!("\n{doc}"))
|
||||
.expect("written to config file");
|
||||
|
||||
file.write_fmt(format_args!("#{ident} ={default}\n"))
|
||||
.expect("written to config file");
|
||||
}
|
||||
|
||||
let display = get_doc_comment_line(field, "display");
|
||||
let display_directive = |key| {
|
||||
display
|
||||
@@ -129,17 +102,77 @@ fn generate_example(input: &ItemStruct, args: &[Meta], write: bool) -> Result<To
|
||||
.flat_map(|display| display.split(' '))
|
||||
.any(|directive| directive == key)
|
||||
};
|
||||
let is_nested = display_directive("nested");
|
||||
let is_hidden = display_directive("hidden");
|
||||
|
||||
if !display_directive("hidden") {
|
||||
let value = if display_directive("sensitive") {
|
||||
quote! { "***********" }
|
||||
// Only generate config file entries for non-nested, visible types
|
||||
if !is_nested && !is_hidden {
|
||||
let doc = get_doc_comment(field)
|
||||
.unwrap_or_else(|| undocumented.into())
|
||||
.trim_end()
|
||||
.to_owned();
|
||||
|
||||
let doc = if doc.ends_with('#') {
|
||||
format!("{doc}\n")
|
||||
} else {
|
||||
quote! { format_args!("{:?}", self.#ident) }
|
||||
format!("{doc}\n#\n")
|
||||
};
|
||||
|
||||
let name = ident.to_string();
|
||||
let default = get_doc_comment_line(field, "default")
|
||||
.or_else(|| get_default(field))
|
||||
.unwrap_or_default();
|
||||
|
||||
let default = if !default.is_empty() {
|
||||
format!(" {default}")
|
||||
} else {
|
||||
default
|
||||
};
|
||||
|
||||
if let Some(file) = file.as_mut() {
|
||||
file.write_fmt(format_args!("\n{doc}"))
|
||||
.expect("written to config file");
|
||||
|
||||
file.write_fmt(format_args!("#{ident} ={default}\n"))
|
||||
.expect("written to config file");
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Display implementation for all fields
|
||||
let name = ident.to_string();
|
||||
|
||||
if display_directive("sensitive") {
|
||||
summary.push(quote! {
|
||||
writeln!(out, "| {} | {} |", #name, #value)?;
|
||||
writeln!(out, "| {} | {} |", #name, "***********")?;
|
||||
});
|
||||
} else if is_nested {
|
||||
let is_option = matches!(type_name.as_str(), "Option");
|
||||
if is_option {
|
||||
summary.push(quote! {
|
||||
writeln!(out, "| {} | {} |", #name,
|
||||
if self.#ident.is_some() { "[configured]" } else { "None" })?;
|
||||
});
|
||||
|
||||
nested_displays.push(quote! {
|
||||
if let Some(nested) = &self.#ident {
|
||||
writeln!(out)?;
|
||||
writeln!(out, "## {}", #name)?;
|
||||
write!(out, "{}", nested)?;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
summary.push(quote! {
|
||||
writeln!(out, "| {} | [configured] |", #name)?;
|
||||
});
|
||||
|
||||
nested_displays.push(quote! {
|
||||
writeln!(out)?;
|
||||
writeln!(out, "## {}", #name)?;
|
||||
write!(out, "{}", &self.#ident)?;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
summary.push(quote! {
|
||||
writeln!(out, "| {} | {:?} |", #name, self.#ident)?;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -159,6 +192,7 @@ fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(out, "| name | value |")?;
|
||||
writeln!(out, "| :--- | :--- |")?;
|
||||
#( #summary )*
|
||||
#( #nested_displays )*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
name = "conduwuit"
|
||||
default-run = "conduwuit"
|
||||
authors.workspace = true
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
version.workspace = true
|
||||
metadata.crane.workspace = true
|
||||
|
||||
@@ -23,14 +20,13 @@ crate-type = [
|
||||
|
||||
[package.metadata.deb]
|
||||
name = "continuwuity"
|
||||
maintainer = "continuwuity developers <contact@continuwuity.org>"
|
||||
copyright = "2024, continuwuity developers"
|
||||
maintainer = "Continuwuity Team and contributors <team@continuwuity.org>"
|
||||
license-file = ["../../LICENSE", "3"]
|
||||
depends = "$auto, ca-certificates"
|
||||
breaks = ["conduwuit (<<0.5.0)"]
|
||||
replaces = ["conduwuit (<<0.5.0)"]
|
||||
extended-description = """\
|
||||
a cool hard fork of Conduit, a Matrix homeserver written in Rust"""
|
||||
A Matrix homeserver written in Rust, the official continuation of the conduwuit homeserver."""
|
||||
section = "net"
|
||||
priority = "optional"
|
||||
conf-files = ["/etc/conduwuit/conduwuit.toml"]
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_router"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_service"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -47,6 +47,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
})?
|
||||
.local_address(url_preview_bind_addr)
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.timeout(Duration::from_secs(config.url_preview_timeout))
|
||||
.redirect(redirect::Policy::limited(3))
|
||||
.build()?,
|
||||
|
||||
@@ -68,6 +69,11 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
.dns_resolver(resolver.resolver.hooked.clone())
|
||||
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
|
||||
.read_timeout(Duration::from_secs(config.federation_timeout))
|
||||
.timeout(Duration::from_secs(
|
||||
config
|
||||
.federation_timeout
|
||||
.saturating_add(config.federation_conn_timeout),
|
||||
))
|
||||
.pool_max_idle_per_host(config.federation_idle_per_host.into())
|
||||
.pool_idle_timeout(Duration::from_secs(config.federation_idle_timeout))
|
||||
.redirect(redirect::Policy::limited(3))
|
||||
@@ -76,7 +82,13 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
synapse: base(config)?
|
||||
.dns_resolver(resolver.resolver.hooked.clone())
|
||||
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
|
||||
.read_timeout(Duration::from_secs(305))
|
||||
.read_timeout(Duration::from_secs(config.federation_timeout.saturating_mul(6)))
|
||||
.timeout(Duration::from_secs(
|
||||
config
|
||||
.federation_timeout
|
||||
.saturating_mul(6)
|
||||
.saturating_add(config.federation_conn_timeout),
|
||||
))
|
||||
.pool_max_idle_per_host(0)
|
||||
.redirect(redirect::Policy::limited(3))
|
||||
.build()?,
|
||||
@@ -103,6 +115,8 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
|
||||
pusher: base(config)?
|
||||
.dns_resolver(resolver.resolver.clone())
|
||||
.connect_timeout(Duration::from_secs(config.pusher_conn_timeout))
|
||||
.timeout(Duration::from_secs(config.pusher_timeout))
|
||||
.pool_max_idle_per_host(1)
|
||||
.pool_idle_timeout(Duration::from_secs(config.pusher_idle_timeout))
|
||||
.redirect(redirect::Policy::limited(2))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use bytes::Bytes;
|
||||
use conduwuit::{
|
||||
Err, Error, Result, debug, debug::INFO_SPAN_LEVEL, debug_error, debug_warn, err,
|
||||
error::inspect_debug_log, implement, trace, utils::string::EMPTY,
|
||||
error::inspect_debug_log, implement, trace,
|
||||
};
|
||||
use http::{HeaderValue, header::AUTHORIZATION};
|
||||
use ipaddress::IPAddress;
|
||||
@@ -90,7 +90,9 @@ async fn perform<T>(
|
||||
|
||||
debug!(%method, %url, "Sending request");
|
||||
match client.execute(request).await {
|
||||
| Ok(response) => handle_response::<T>(dest, actual, &method, &url, response).await,
|
||||
| Ok(response) =>
|
||||
self.handle_response::<T>(dest, actual, &method, &url, response)
|
||||
.await,
|
||||
| Err(error) =>
|
||||
Err(handle_error(actual, &method, &url, error).expect_err("always returns error")),
|
||||
}
|
||||
@@ -119,7 +121,9 @@ fn validate_url(&self, url: &Url) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[implement(super::Service)]
|
||||
async fn handle_response<T>(
|
||||
&self,
|
||||
dest: &ServerName,
|
||||
actual: &ActualDest,
|
||||
method: &Method,
|
||||
@@ -162,7 +166,6 @@ async fn into_http_response(
|
||||
.expect("http::response::Builder is usable"),
|
||||
);
|
||||
|
||||
// TODO: handle timeout
|
||||
trace!("Waiting for response body...");
|
||||
let body = response
|
||||
.bytes()
|
||||
@@ -286,10 +289,13 @@ fn into_http_request<T>(actual: &ActualDest, request: T) -> Result<http::Request
|
||||
T: OutgoingRequest + Send,
|
||||
{
|
||||
const VERSIONS: [MatrixVersion; 1] = [MatrixVersion::V1_11];
|
||||
const SATIR: SendAccessToken<'_> = SendAccessToken::IfRequired(EMPTY);
|
||||
|
||||
let http_request = request
|
||||
.try_into_http_request::<Vec<u8>>(actual.string().as_str(), SATIR, &VERSIONS)
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
actual.string().as_str(),
|
||||
SendAccessToken::None,
|
||||
&VERSIONS,
|
||||
)
|
||||
.map_err(|e| err!(BadServerResponse("Invalid destination: {e:?}")))?;
|
||||
|
||||
Ok(http_request)
|
||||
|
||||
@@ -18,7 +18,6 @@ pub struct Service {
|
||||
pub server_user: OwnedUserId,
|
||||
pub admin_alias: OwnedRoomAliasId,
|
||||
pub turn_secret: String,
|
||||
pub registration_token: Option<String>,
|
||||
}
|
||||
|
||||
type RateLimitState = (Instant, u32); // Time if last failed try, number of failed tries
|
||||
@@ -41,19 +40,6 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
},
|
||||
);
|
||||
|
||||
let registration_token = config.registration_token_file.as_ref().map_or_else(
|
||||
|| config.registration_token.clone(),
|
||||
|path| {
|
||||
let Ok(token) = std::fs::read_to_string(path).inspect_err(|e| {
|
||||
error!("Failed to read the registration token file: {e}");
|
||||
}) else {
|
||||
return config.registration_token.clone();
|
||||
};
|
||||
|
||||
Some(token.trim().to_owned())
|
||||
},
|
||||
);
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
db,
|
||||
server: args.server.clone(),
|
||||
@@ -66,7 +52,6 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
)
|
||||
.expect("@conduit:server_name is valid"),
|
||||
turn_secret,
|
||||
registration_token,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
pub mod moderation;
|
||||
pub mod presence;
|
||||
pub mod pusher;
|
||||
pub mod registration_tokens;
|
||||
pub mod resolver;
|
||||
pub mod rooms;
|
||||
pub mod sending;
|
||||
|
||||
@@ -198,7 +198,7 @@ pub async fn send_request<T>(&self, dest: &str, request: T) -> Result<T::Incomin
|
||||
trace!("Push gateway destination: {dest}");
|
||||
|
||||
let http_request = request
|
||||
.try_into_http_request::<BytesMut>(&dest, SendAccessToken::IfRequired(""), &VERSIONS)
|
||||
.try_into_http_request::<BytesMut>(&dest, SendAccessToken::None, &VERSIONS)
|
||||
.map_err(|e| {
|
||||
err!(BadServerResponse(warn!(
|
||||
"Failed to find destination {dest} for push gateway: {e}"
|
||||
@@ -245,7 +245,7 @@ pub async fn send_request<T>(&self, dest: &str, request: T) -> Result<T::Incomin
|
||||
.expect("http::response::Builder is usable"),
|
||||
);
|
||||
|
||||
let body = response.bytes().await?; // TODO: handle timeout
|
||||
let body = response.bytes().await?;
|
||||
|
||||
if !status.is_success() {
|
||||
debug_warn!("Push gateway response body: {:?}", string_from_bytes(&body));
|
||||
@@ -288,7 +288,7 @@ pub async fn send_push_notice<E>(
|
||||
let mut notify = None;
|
||||
let mut tweaks = Vec::new();
|
||||
if event.room_id().is_none() {
|
||||
// TODO(hydra): does this matter?
|
||||
// This only affects v12+ create events
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
129
src/service/registration_tokens/data.rs
Normal file
129
src/service/registration_tokens/data.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
use std::{sync::Arc, time::SystemTime};
|
||||
|
||||
use conduwuit::utils::{
|
||||
self,
|
||||
stream::{ReadyExt, TryIgnore},
|
||||
};
|
||||
use database::{Database, Deserialized, Json, Map};
|
||||
use futures::Stream;
|
||||
use ruma::OwnedUserId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub(super) struct Data {
|
||||
registrationtoken_info: Arc<Map>,
|
||||
}
|
||||
|
||||
/// Metadata of a registration token.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DatabaseTokenInfo {
|
||||
/// The admin user who created this token.
|
||||
pub creator: OwnedUserId,
|
||||
/// The number of times this token has been used to create an account.
|
||||
pub uses: u64,
|
||||
/// When this token will expire, if it expires.
|
||||
pub expires: Option<TokenExpires>,
|
||||
}
|
||||
|
||||
impl DatabaseTokenInfo {
|
||||
pub(super) fn new(creator: OwnedUserId, expires: Option<TokenExpires>) -> Self {
|
||||
Self { creator, uses: 0, expires }
|
||||
}
|
||||
|
||||
/// Determine whether this token info represents a valid token, i.e. one
|
||||
/// that has not expired according to its [`Self::expires`] property. If
|
||||
/// [`Self::expires`] is [`None`], this function will always return `true`.
|
||||
#[must_use]
|
||||
pub fn is_valid(&self) -> bool {
|
||||
match self.expires {
|
||||
| Some(TokenExpires::AfterUses(max_uses)) => self.uses < max_uses,
|
||||
| Some(TokenExpires::AfterTime(expiry_time)) => {
|
||||
let now = SystemTime::now();
|
||||
|
||||
expiry_time >= now
|
||||
},
|
||||
| None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DatabaseTokenInfo {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Token created by {} and used {} times. ", &self.creator, self.uses)?;
|
||||
if let Some(expires) = &self.expires {
|
||||
write!(f, "{expires}.")?;
|
||||
} else {
|
||||
write!(f, "Never expires.")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum TokenExpires {
|
||||
AfterUses(u64),
|
||||
AfterTime(SystemTime),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TokenExpires {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
| Self::AfterUses(max_uses) => write!(f, "Expires after {max_uses} uses"),
|
||||
| Self::AfterTime(max_age) => {
|
||||
let now = SystemTime::now();
|
||||
let formatted_expiry = utils::time::format(*max_age, "%+");
|
||||
|
||||
match max_age.duration_since(now) {
|
||||
| Ok(duration) => write!(
|
||||
f,
|
||||
"Expires in {} ({formatted_expiry})",
|
||||
utils::time::pretty(duration)
|
||||
),
|
||||
| Err(_) => write!(f, "Expired at {formatted_expiry}"),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub(super) fn new(db: &Arc<Database>) -> Self {
|
||||
Self {
|
||||
registrationtoken_info: db["registrationtoken_info"].clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Associate a registration token with its metadata in the database.
|
||||
pub(super) fn save_token(&self, token: &str, info: &DatabaseTokenInfo) {
|
||||
self.registrationtoken_info.raw_put(token, Json(info));
|
||||
}
|
||||
|
||||
/// Delete a registration token.
|
||||
pub(super) fn revoke_token(&self, token: &str) { self.registrationtoken_info.remove(token); }
|
||||
|
||||
/// Look up a registration token's metadata.
|
||||
pub(super) async fn lookup_token_info(&self, token: &str) -> Option<DatabaseTokenInfo> {
|
||||
self.registrationtoken_info
|
||||
.get(token)
|
||||
.await
|
||||
.deserialized()
|
||||
.ok()
|
||||
}
|
||||
|
||||
/// Iterate over all valid tokens and delete expired ones.
|
||||
pub(super) fn iterate_and_clean_tokens(
|
||||
&self,
|
||||
) -> impl Stream<Item = (&str, DatabaseTokenInfo)> + Send + '_ {
|
||||
self.registrationtoken_info
|
||||
.stream()
|
||||
.ignore_err()
|
||||
.ready_filter_map(|item: (&str, DatabaseTokenInfo)| {
|
||||
if item.1.is_valid() {
|
||||
Some(item)
|
||||
} else {
|
||||
self.registrationtoken_info.remove(item.0);
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
172
src/service/registration_tokens/mod.rs
Normal file
172
src/service/registration_tokens/mod.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
mod data;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use conduwuit::{Err, Result, utils};
|
||||
use data::Data;
|
||||
pub use data::{DatabaseTokenInfo, TokenExpires};
|
||||
use futures::{Stream, StreamExt, stream};
|
||||
use ruma::OwnedUserId;
|
||||
|
||||
use crate::{Dep, config};
|
||||
|
||||
const RANDOM_TOKEN_LENGTH: usize = 16;
|
||||
|
||||
pub struct Service {
|
||||
db: Data,
|
||||
services: Services,
|
||||
}
|
||||
|
||||
struct Services {
|
||||
config: Dep<config::Service>,
|
||||
}
|
||||
|
||||
/// A validated registration token which may be used to create an account.
|
||||
#[derive(Debug)]
|
||||
pub struct ValidToken {
|
||||
pub token: String,
|
||||
pub source: ValidTokenSource,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ValidToken {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "`{}` --- {}", self.token, &self.source)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for ValidToken {
|
||||
fn eq(&self, other: &str) -> bool { self.token == other }
|
||||
}
|
||||
|
||||
/// The source of a valid database token.
|
||||
#[derive(Debug)]
|
||||
pub enum ValidTokenSource {
|
||||
/// The static token set in the homeserver's config file, which is
|
||||
/// always valid.
|
||||
ConfigFile,
|
||||
/// A database token which has been checked to be valid.
|
||||
Database(DatabaseTokenInfo),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ValidTokenSource {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
| Self::ConfigFile => write!(f, "Token defined in config."),
|
||||
| Self::Database(info) => info.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
Ok(Arc::new(Self {
|
||||
db: Data::new(args.db),
|
||||
services: Services {
|
||||
config: args.depend::<config::Service>("config"),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
|
||||
}
|
||||
|
||||
impl Service {
|
||||
/// Issue a new registration token and save it in the database.
|
||||
pub fn issue_token(
|
||||
&self,
|
||||
creator: OwnedUserId,
|
||||
expires: Option<TokenExpires>,
|
||||
) -> (String, DatabaseTokenInfo) {
|
||||
let token = utils::random_string(RANDOM_TOKEN_LENGTH);
|
||||
let info = DatabaseTokenInfo::new(creator, expires);
|
||||
|
||||
self.db.save_token(&token, &info);
|
||||
(token, info)
|
||||
}
|
||||
|
||||
/// Get the registration token set in the config file, if it exists.
|
||||
pub fn get_config_file_token(&self) -> Option<ValidToken> {
|
||||
self.services
|
||||
.config
|
||||
.registration_token
|
||||
.clone()
|
||||
.map(|token| ValidToken {
|
||||
token,
|
||||
source: ValidTokenSource::ConfigFile,
|
||||
})
|
||||
}
|
||||
|
||||
/// Validate a registration token.
|
||||
pub async fn validate_token(&self, token: String) -> Option<ValidToken> {
|
||||
// Check the registration token in the config first
|
||||
if self
|
||||
.get_config_file_token()
|
||||
.is_some_and(|valid_token| valid_token == *token)
|
||||
{
|
||||
return Some(ValidToken {
|
||||
token,
|
||||
source: ValidTokenSource::ConfigFile,
|
||||
});
|
||||
}
|
||||
|
||||
// Now check the database
|
||||
if let Some(token_info) = self.db.lookup_token_info(&token).await
|
||||
&& token_info.is_valid()
|
||||
{
|
||||
return Some(ValidToken {
|
||||
token,
|
||||
source: ValidTokenSource::Database(token_info),
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise it's not valid
|
||||
None
|
||||
}
|
||||
|
||||
/// Mark a valid token as having been used to create a new account.
|
||||
pub fn mark_token_as_used(&self, ValidToken { token, source }: ValidToken) {
|
||||
match source {
|
||||
| ValidTokenSource::ConfigFile => {
|
||||
// we don't track uses of the config file token, do nothing
|
||||
},
|
||||
| ValidTokenSource::Database(mut info) => {
|
||||
info.uses = info.uses.saturating_add(1);
|
||||
|
||||
self.db.save_token(&token, &info);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to revoke a valid token.
|
||||
///
|
||||
/// Note that some tokens (like the one set in the config file) cannot be
|
||||
/// revoked.
|
||||
pub fn revoke_token(&self, ValidToken { token, source }: ValidToken) -> Result {
|
||||
match source {
|
||||
| ValidTokenSource::ConfigFile => {
|
||||
// the config file token cannot be revoked
|
||||
Err!(
|
||||
"The token set in the config file cannot be revoked. Edit the config file \
|
||||
to change it."
|
||||
)
|
||||
},
|
||||
| ValidTokenSource::Database(_) => {
|
||||
self.db.revoke_token(&token);
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate over all valid registration tokens.
|
||||
pub fn iterate_tokens(&self) -> impl Stream<Item = ValidToken> + Send + '_ {
|
||||
let db_tokens = self
|
||||
.db
|
||||
.iterate_and_clean_tokens()
|
||||
.map(|(token, info)| ValidToken {
|
||||
token: token.to_owned(),
|
||||
source: ValidTokenSource::Database(info),
|
||||
});
|
||||
|
||||
stream::iter(self.get_config_file_token()).chain(db_tokens)
|
||||
}
|
||||
}
|
||||
@@ -131,10 +131,6 @@ pub async fn handle_incoming_pdu<'a>(
|
||||
.await?
|
||||
.origin_server_ts();
|
||||
|
||||
if incoming_pdu.origin_server_ts() < first_ts_in_room {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// 9. Fetch any missing prev events doing all checks listed here starting at 1.
|
||||
// These are timeline events
|
||||
let (sorted_prev_events, mut eventid_info) = self
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId,
|
||||
UserId,
|
||||
canonical_json::to_canonical_value,
|
||||
events::{StateEventType, TimelineEventType, room::create::RoomCreateEventContent},
|
||||
uint,
|
||||
};
|
||||
@@ -210,7 +209,7 @@ fn from_evt(
|
||||
} else {
|
||||
Some(to_raw_value(&unsigned)?)
|
||||
},
|
||||
hashes: EventHash { sha256: "aaa".to_owned() },
|
||||
hashes: EventHash { sha256: String::new() },
|
||||
signatures: None,
|
||||
};
|
||||
|
||||
@@ -269,12 +268,6 @@ fn from_evt(
|
||||
},
|
||||
}
|
||||
|
||||
pdu_json.insert(
|
||||
"origin".to_owned(),
|
||||
to_canonical_value(self.services.globals.server_name())
|
||||
.expect("server name is a valid CanonicalJsonValue"),
|
||||
);
|
||||
|
||||
trace!("hashing and signing event {}", pdu.event_id);
|
||||
if let Err(e) = self
|
||||
.services
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::{fmt::Debug, mem};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use conduwuit::{Err, Result, debug_error, err, trace, utils, warn};
|
||||
use reqwest::Client;
|
||||
use conduwuit::{Err, Result, debug_error, err, implement, trace, utils, warn};
|
||||
use ruma::api::{
|
||||
IncomingResponse, MatrixVersion, OutgoingRequest, SendAccessToken, appservice::Registration,
|
||||
};
|
||||
@@ -11,8 +10,9 @@
|
||||
///
|
||||
/// Only returns Ok(None) if there is no url specified in the appservice
|
||||
/// registration file
|
||||
pub(crate) async fn send_request<T>(
|
||||
client: &Client,
|
||||
#[implement(super::Service)]
|
||||
pub async fn send_appservice_request<T>(
|
||||
&self,
|
||||
registration: Registration,
|
||||
request: T,
|
||||
) -> Result<Option<T::IncomingResponse>>
|
||||
@@ -35,7 +35,7 @@ pub(crate) async fn send_request<T>(
|
||||
let mut http_request = request
|
||||
.try_into_http_request::<BytesMut>(
|
||||
&dest,
|
||||
SendAccessToken::IfRequired(hs_token),
|
||||
SendAccessToken::Appservice(hs_token),
|
||||
&VERSIONS,
|
||||
)
|
||||
.map_err(|e| {
|
||||
@@ -58,6 +58,8 @@ pub(crate) async fn send_request<T>(
|
||||
|
||||
let reqwest_request = reqwest::Request::try_from(http_request)?;
|
||||
|
||||
let client = &self.services.client.appservice;
|
||||
|
||||
let mut response = client.execute(reqwest_request).await.map_err(|e| {
|
||||
warn!("Could not send request to appservice \"{}\" at {dest}: {e:?}", registration.id);
|
||||
e
|
||||
@@ -75,7 +77,7 @@ pub(crate) async fn send_request<T>(
|
||||
.expect("http::response::Builder is usable"),
|
||||
);
|
||||
|
||||
let body = response.bytes().await?; // TODO: handle timeout
|
||||
let body = response.bytes().await?;
|
||||
|
||||
if !status.is_success() {
|
||||
debug_error!("Appservice response bytes: {:?}", utils::string_from_bytes(&body));
|
||||
|
||||
@@ -19,10 +19,7 @@
|
||||
warn,
|
||||
};
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
use ruma::{
|
||||
RoomId, ServerName, UserId,
|
||||
api::{OutgoingRequest, appservice::Registration},
|
||||
};
|
||||
use ruma::{RoomId, ServerName, UserId, api::OutgoingRequest};
|
||||
use tokio::{task, task::JoinSet};
|
||||
|
||||
use self::data::Data;
|
||||
@@ -319,22 +316,6 @@ pub async fn send_synapse_request<T>(
|
||||
.await
|
||||
}
|
||||
|
||||
/// Sends a request to an appservice
|
||||
///
|
||||
/// Only returns None if there is no url specified in the appservice
|
||||
/// registration file
|
||||
pub async fn send_appservice_request<T>(
|
||||
&self,
|
||||
registration: Registration,
|
||||
request: T,
|
||||
) -> Result<Option<T::IncomingResponse>>
|
||||
where
|
||||
T: OutgoingRequest + Debug + Send,
|
||||
{
|
||||
let client = &self.services.client.appservice;
|
||||
appservice::send_request(client, registration, request).await
|
||||
}
|
||||
|
||||
/// Clean up queued sending event data
|
||||
///
|
||||
/// Used after we remove an appservice registration or a user deletes a push
|
||||
|
||||
@@ -50,9 +50,7 @@
|
||||
};
|
||||
use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
|
||||
|
||||
use super::{
|
||||
Destination, EduBuf, EduVec, Msg, SendingEvent, Service, appservice, data::QueueItem,
|
||||
};
|
||||
use super::{Destination, EduBuf, EduVec, Msg, SendingEvent, Service, data::QueueItem};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TransactionStatus {
|
||||
@@ -720,18 +718,18 @@ async fn send_events_dest_appservice(
|
||||
|
||||
//debug_assert!(pdu_jsons.len() + edu_jsons.len() > 0, "sending empty
|
||||
// transaction");
|
||||
let client = &self.services.client.appservice;
|
||||
match appservice::send_request(
|
||||
client,
|
||||
appservice,
|
||||
ruma::api::appservice::event::push_events::v1::Request {
|
||||
events: pdu_jsons,
|
||||
txn_id: txn_id.into(),
|
||||
ephemeral: edu_jsons,
|
||||
to_device: Vec::new(), // TODO
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
||||
match self
|
||||
.send_appservice_request(
|
||||
appservice,
|
||||
ruma::api::appservice::event::push_events::v1::Request {
|
||||
events: pdu_jsons,
|
||||
txn_id: txn_id.into(),
|
||||
ephemeral: edu_jsons,
|
||||
to_device: Vec::new(), // TODO
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(_) => Ok(Destination::Appservice(id)),
|
||||
| Err(e) => Err((Destination::Appservice(id), e)),
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
account_data, admin, announcements, antispam, appservice, client, config, emergency,
|
||||
federation, globals, key_backups,
|
||||
manager::Manager,
|
||||
media, moderation, presence, pusher, resolver, rooms, sending, server_keys, service,
|
||||
service::{Args, Map, Service},
|
||||
media, moderation, presence, pusher, registration_tokens, resolver, rooms, sending,
|
||||
server_keys,
|
||||
service::{self, Args, Map, Service},
|
||||
sync, transaction_ids, uiaa, users,
|
||||
};
|
||||
|
||||
@@ -28,6 +29,7 @@ pub struct Services {
|
||||
pub media: Arc<media::Service>,
|
||||
pub presence: Arc<presence::Service>,
|
||||
pub pusher: Arc<pusher::Service>,
|
||||
pub registration_tokens: Arc<registration_tokens::Service>,
|
||||
pub resolver: Arc<resolver::Service>,
|
||||
pub rooms: rooms::Service,
|
||||
pub federation: Arc<federation::Service>,
|
||||
@@ -77,6 +79,7 @@ macro_rules! build {
|
||||
media: build!(media::Service),
|
||||
presence: build!(presence::Service),
|
||||
pusher: build!(pusher::Service),
|
||||
registration_tokens: build!(registration_tokens::Service),
|
||||
rooms: rooms::Service {
|
||||
alias: build!(rooms::alias::Service),
|
||||
auth_chain: build!(rooms::auth_chain::Service),
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use conduwuit::{
|
||||
Err, Error, Result, SyncRwLock, err, error, implement, utils,
|
||||
@@ -16,7 +13,7 @@
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{Dep, config, globals, users};
|
||||
use crate::{Dep, config, globals, registration_tokens, users};
|
||||
|
||||
pub struct Service {
|
||||
userdevicesessionid_uiaarequest: SyncRwLock<RequestMap>,
|
||||
@@ -28,6 +25,7 @@ struct Services {
|
||||
globals: Dep<globals::Service>,
|
||||
users: Dep<users::Service>,
|
||||
config: Dep<config::Service>,
|
||||
registration_tokens: Dep<registration_tokens::Service>,
|
||||
}
|
||||
|
||||
struct Data {
|
||||
@@ -50,6 +48,8 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
globals: args.depend::<globals::Service>("globals"),
|
||||
users: args.depend::<users::Service>("users"),
|
||||
config: args.depend::<config::Service>("config"),
|
||||
registration_tokens: args
|
||||
.depend::<registration_tokens::Service>("registration_tokens"),
|
||||
},
|
||||
}))
|
||||
}
|
||||
@@ -57,26 +57,6 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
|
||||
}
|
||||
|
||||
#[implement(Service)]
|
||||
pub async fn read_tokens(&self) -> Result<HashSet<String>> {
|
||||
let mut tokens = HashSet::new();
|
||||
if let Some(file) = &self.services.config.registration_token_file.as_ref() {
|
||||
match std::fs::read_to_string(file) {
|
||||
| Ok(text) => {
|
||||
text.split_ascii_whitespace().for_each(|token| {
|
||||
tokens.insert(token.to_owned());
|
||||
});
|
||||
},
|
||||
| Err(e) => error!("Failed to read the registration token file: {e}"),
|
||||
}
|
||||
}
|
||||
if let Some(token) = &self.services.config.registration_token {
|
||||
tokens.insert(token.to_owned());
|
||||
}
|
||||
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
/// Creates a new Uiaa session. Make sure the session token is unique.
|
||||
#[implement(Service)]
|
||||
pub fn create(
|
||||
@@ -229,8 +209,18 @@ pub async fn try_auth(
|
||||
}
|
||||
},
|
||||
| AuthData::RegistrationToken(t) => {
|
||||
let tokens = self.read_tokens().await?;
|
||||
if tokens.contains(t.token.trim()) {
|
||||
let token = t.token.trim().to_owned();
|
||||
|
||||
if let Some(valid_token) = self
|
||||
.services
|
||||
.registration_tokens
|
||||
.validate_token(token)
|
||||
.await
|
||||
{
|
||||
self.services
|
||||
.registration_tokens
|
||||
.mark_token_as_used(valid_token);
|
||||
|
||||
uiaainfo.completed.push(AuthType::RegistrationToken);
|
||||
} else {
|
||||
uiaainfo.auth_error = Some(StandardErrorBody {
|
||||
|
||||
@@ -78,6 +78,7 @@ struct Data {
|
||||
userid_password: Arc<Map>,
|
||||
userid_suspension: Arc<Map>,
|
||||
userid_lock: Arc<Map>,
|
||||
userid_logindisabled: Arc<Map>,
|
||||
userid_selfsigningkeyid: Arc<Map>,
|
||||
userid_usersigningkeyid: Arc<Map>,
|
||||
useridprofilekey_value: Arc<Map>,
|
||||
@@ -117,6 +118,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
userid_password: args.db["userid_password"].clone(),
|
||||
userid_suspension: args.db["userid_suspension"].clone(),
|
||||
userid_lock: args.db["userid_lock"].clone(),
|
||||
userid_logindisabled: args.db["userid_logindisabled"].clone(),
|
||||
userid_selfsigningkeyid: args.db["userid_selfsigningkeyid"].clone(),
|
||||
userid_usersigningkeyid: args.db["userid_usersigningkeyid"].clone(),
|
||||
useridprofilekey_value: args.db["useridprofilekey_value"].clone(),
|
||||
@@ -295,6 +297,16 @@ pub async fn is_locked(&self, user_id: &UserId) -> Result<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable_login(&self, user_id: &UserId) {
|
||||
self.db.userid_logindisabled.insert(user_id, "");
|
||||
}
|
||||
|
||||
pub fn enable_login(&self, user_id: &UserId) { self.db.userid_logindisabled.remove(user_id); }
|
||||
|
||||
pub async fn is_login_disabled(&self, user_id: &UserId) -> bool {
|
||||
self.db.userid_logindisabled.contains(user_id).await
|
||||
}
|
||||
|
||||
/// Check if account is active, infallible
|
||||
pub async fn is_active(&self, user_id: &UserId) -> bool {
|
||||
!self.is_deactivated(user_id).await.unwrap_or(true)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_web"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
[package]
|
||||
name = "xtask"
|
||||
authors.workspace = true
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
conduwuit-admin.workspace = true
|
||||
conduwuit.workspace = true
|
||||
clap.workspace = true
|
||||
# Required for working with JSON output from cargo metadata
|
||||
serde.workspace = true
|
||||
serde_json = "1.0"
|
||||
|
||||
askama = "0.15.1"
|
||||
cargo_metadata = "0.23.1"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,26 +0,0 @@
|
||||
[package]
|
||||
name = "xtask-generate-commands"
|
||||
authors.workspace = true
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap-markdown = "0.1.5"
|
||||
clap_builder = { version = "4.5.38", default-features = false }
|
||||
clap_mangen = "0.2"
|
||||
|
||||
conduwuit-admin.workspace = true
|
||||
|
||||
# Hack to prevent rebuilds
|
||||
conduwuit.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,113 +0,0 @@
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use clap_builder::{Command, CommandFactory};
|
||||
use conduwuit_admin::AdminCommand;
|
||||
|
||||
enum CommandType {
|
||||
Admin,
|
||||
Server,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut args = std::env::args().skip(1);
|
||||
let command_type = args.next();
|
||||
let task = args.next();
|
||||
|
||||
match (command_type, task) {
|
||||
| (None, _) => {
|
||||
return Err("Missing command type (admin or server)".into());
|
||||
},
|
||||
| (Some(cmd_type), None) => {
|
||||
return Err(format!("Missing task for {cmd_type} command").into());
|
||||
},
|
||||
| (Some(cmd_type), Some(task)) => {
|
||||
let command_type = match cmd_type.as_str() {
|
||||
| "admin" => CommandType::Admin,
|
||||
| "server" => CommandType::Server,
|
||||
| _ => return Err(format!("Invalid command type: {cmd_type}").into()),
|
||||
};
|
||||
|
||||
match task.as_str() {
|
||||
| "man" => match command_type {
|
||||
| CommandType::Admin => {
|
||||
let dir = Path::new("./admin-man");
|
||||
gen_admin_manpages(dir)?;
|
||||
},
|
||||
| CommandType::Server => {
|
||||
let dir = Path::new("./server-man");
|
||||
gen_server_manpages(dir)?;
|
||||
},
|
||||
},
|
||||
| "md" => {
|
||||
match command_type {
|
||||
| CommandType::Admin => {
|
||||
let command = AdminCommand::command().name("admin");
|
||||
|
||||
let res = clap_markdown::help_markdown_command_custom(
|
||||
&command,
|
||||
&clap_markdown::MarkdownOptions::default().show_footer(false),
|
||||
)
|
||||
.replace("\n\r", "\n")
|
||||
.replace("\r\n", "\n")
|
||||
.replace(" \n", "\n");
|
||||
|
||||
let mut file = File::create(Path::new("./docs/admin_reference.md"))?;
|
||||
file.write_all(res.trim_end().as_bytes())?;
|
||||
file.write_all(b"\n")?;
|
||||
},
|
||||
| CommandType::Server => {
|
||||
// Get the server command from the conduwuit crate
|
||||
let command = conduwuit::Args::command();
|
||||
|
||||
let res = clap_markdown::help_markdown_command_custom(
|
||||
&command,
|
||||
&clap_markdown::MarkdownOptions::default().show_footer(false),
|
||||
)
|
||||
.replace("\n\r", "\n")
|
||||
.replace("\r\n", "\n")
|
||||
.replace(" \n", "\n");
|
||||
|
||||
let mut file = File::create(Path::new("./docs/server_reference.md"))?;
|
||||
file.write_all(res.trim_end().as_bytes())?;
|
||||
file.write_all(b"\n")?;
|
||||
},
|
||||
}
|
||||
},
|
||||
| invalid => return Err(format!("Invalid task name: {invalid}").into()),
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gen_manpage_common(dir: &Path, c: &Command, prefix: Option<&str>) -> Result<(), io::Error> {
|
||||
fs::create_dir_all(dir)?;
|
||||
let sub_name = c.get_display_name().unwrap_or_else(|| c.get_name());
|
||||
let name = if let Some(prefix) = prefix {
|
||||
format!("{prefix}-{sub_name}")
|
||||
} else {
|
||||
sub_name.to_owned()
|
||||
};
|
||||
|
||||
let mut out = File::create(dir.join(format!("{name}.1")))?;
|
||||
let clap_mangen = clap_mangen::Man::new(c.to_owned().disable_help_flag(true));
|
||||
clap_mangen.render(&mut out)?;
|
||||
|
||||
for sub in c.get_subcommands() {
|
||||
gen_manpage_common(&dir.join(sub_name), sub, Some(&name))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gen_admin_manpages(dir: &Path) -> Result<(), io::Error> {
|
||||
gen_manpage_common(dir, &AdminCommand::command().name("admin"), None)
|
||||
}
|
||||
|
||||
fn gen_server_manpages(dir: &Path) -> Result<(), io::Error> {
|
||||
gen_manpage_common(dir, &conduwuit::Args::command(), None)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user