mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f84f0c46b1 | |||
| cef2260dc7 | |||
| 4ffe321b6a | |||
| 61a3749c24 | |||
| 374a216fc8 | |||
| e9f6dc29b6 | |||
| 688855631e | |||
| 0c0ae68070 | |||
| c8c14f248b | |||
| 6756bacfc8 | |||
| 26d0d80737 | |||
| 7ee9e09b05 | |||
| 61ab6b4eb8 | |||
| 4da00fa28a | |||
| a0aeebd237 | |||
| e02d7c8029 | |||
| 8882d1d4c7 | |||
| fa6c5aa942 | |||
| 6c54f592ee | |||
| 6a9123baf1 | |||
| 91923d0afa | |||
| 242414c0b8 | |||
| 8c6904ab33 | |||
| 3c07857e1f | |||
| 851d6e219f | |||
| 66aba9d5d0 | |||
| 5ca1341bf7 | |||
| baf76cd4dc | |||
| 53d51cf831 | |||
| 9bfc331a26 | |||
| 1cda559a18 | |||
| 94655acffd | |||
| 4bbbbb854e | |||
| 81388162f0 | |||
| a912dcc106 | |||
| 950d7ae3d9 | |||
| 7f36c44763 | |||
| 3e8403de64 | |||
| 2ef8a1edd7 | |||
| 6f17868525 | |||
| ee73a2b36d | |||
| 3dc4c7d4fc | |||
| 13917bb5c3 | |||
| f269fb5cfc | |||
| 6b0b8344d4 | |||
| 02948960fa | |||
| 30c9d6d2df | |||
| 74841b6711 | |||
| dabbdc7517 |
Generated
+78
-11
@@ -1088,6 +1088,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde-saphyr",
|
"serde-saphyr",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
"sha2 0.11.0",
|
"sha2 0.11.0",
|
||||||
"termimad",
|
"termimad",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -1107,18 +1108,29 @@ dependencies = [
|
|||||||
"axum",
|
"axum",
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
"conduwuit_api",
|
||||||
"conduwuit_build_metadata",
|
"conduwuit_build_metadata",
|
||||||
"conduwuit_core",
|
"conduwuit_core",
|
||||||
|
"conduwuit_database",
|
||||||
"conduwuit_service",
|
"conduwuit_service",
|
||||||
|
"form_urlencoded",
|
||||||
"futures",
|
"futures",
|
||||||
|
"lettre",
|
||||||
"memory-serve",
|
"memory-serve",
|
||||||
"rand 0.10.1",
|
"rand 0.10.1",
|
||||||
|
"recaptcha-verify",
|
||||||
|
"reqwest 0.12.28",
|
||||||
"ruma",
|
"ruma",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tower-sec-fetch",
|
"tower-sec-fetch",
|
||||||
|
"tower-sessions",
|
||||||
|
"tower-sessions-core",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"url",
|
||||||
"validator",
|
"validator",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1526,6 +1538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4151,7 +4164,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma"
|
name = "ruma"
|
||||||
version = "0.15.1"
|
version = "0.15.1"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assign",
|
"assign",
|
||||||
"js_int",
|
"js_int",
|
||||||
@@ -4170,7 +4183,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-appservice-api"
|
name = "ruma-appservice-api"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
@@ -4182,7 +4195,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-client-api"
|
name = "ruma-client-api"
|
||||||
version = "0.23.1"
|
version = "0.23.1"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"assign",
|
"assign",
|
||||||
@@ -4204,7 +4217,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-common"
|
name = "ruma-common"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
@@ -4237,7 +4250,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-events"
|
name = "ruma-events"
|
||||||
version = "0.33.0"
|
version = "0.33.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
@@ -4258,7 +4271,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-federation-api"
|
name = "ruma-federation-api"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"headers",
|
"headers",
|
||||||
@@ -4281,7 +4294,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identifiers-validation"
|
name = "ruma-identifiers-validation"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -4290,7 +4303,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-macros"
|
name = "ruma-macros"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -4306,7 +4319,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-push-gateway-api"
|
name = "ruma-push-gateway-api"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
@@ -4318,7 +4331,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-signatures"
|
name = "ruma-signatures"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
@@ -4334,7 +4347,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-state-res"
|
name = "ruma-state-res"
|
||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git?rev=3ecd80b92794d2d93f657a7b3db62d4be237526b#3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
source = "git+https://github.com/ruma/ruma.git?rev=9c9dccc93f054bbd28f23f630223fffa6289ecbc#9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
@@ -5543,6 +5556,22 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-cookies"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "151b5a3e3c45df17466454bb74e9ecedecc955269bdedbf4d150dfa393b55a36"
|
||||||
|
dependencies = [
|
||||||
|
"axum-core",
|
||||||
|
"cookie",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"parking_lot",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-http"
|
name = "tower-http"
|
||||||
version = "0.6.11"
|
version = "0.6.11"
|
||||||
@@ -5591,6 +5620,44 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-sessions"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "518dca34b74a17cadfcee06e616a09d2bd0c3984eff1769e1e76d58df978fc78"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"http",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"tower-cookies",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tower-sessions-core",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-sessions-core"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "568531ec3dfcf3ffe493de1958ae5662a0284ac5d767476ecdb6a34ff8c6b06c"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"axum-core",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"futures",
|
||||||
|
"http",
|
||||||
|
"parking_lot",
|
||||||
|
"rand 0.9.4",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
|
|||||||
+4
-2
@@ -344,7 +344,7 @@ version = "1.1.1"
|
|||||||
[workspace.dependencies.ruma]
|
[workspace.dependencies.ruma]
|
||||||
# version = "0.14.1"
|
# version = "0.14.1"
|
||||||
git = "https://github.com/ruma/ruma.git"
|
git = "https://github.com/ruma/ruma.git"
|
||||||
rev = "3ecd80b92794d2d93f657a7b3db62d4be237526b"
|
rev = "9c9dccc93f054bbd28f23f630223fffa6289ecbc"
|
||||||
features = [
|
features = [
|
||||||
"appservice-api-c",
|
"appservice-api-c",
|
||||||
"client-api",
|
"client-api",
|
||||||
@@ -379,7 +379,6 @@ features = [
|
|||||||
"unstable-msc4293",
|
"unstable-msc4293",
|
||||||
"unstable-msc4406",
|
"unstable-msc4406",
|
||||||
"unstable-msc4439",
|
"unstable-msc4439",
|
||||||
"unstable-msc4466",
|
|
||||||
"unstable-extensible-events",
|
"unstable-extensible-events",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -560,6 +559,9 @@ features = ["std"]
|
|||||||
[workspace.dependencies.nonzero_ext]
|
[workspace.dependencies.nonzero_ext]
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
|
||||||
|
[workspace.dependencies.serde_urlencoded]
|
||||||
|
version = "0.7.1"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Patches
|
# Patches
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Users may now be forbidden from deactivating their own accounts with the new `allow_deactivation` config option. Contributed by @ginger.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Added support for authenticating clients using the new OAuth 2.0 login API. Contributed by @ginger.
|
||||||
@@ -1 +0,0 @@
|
|||||||
Added support for MSC4466, which allows clients to customize how changes to a user's global profile are propagated. Contributed by @ginger.
|
|
||||||
+37
-9
@@ -521,17 +521,15 @@
|
|||||||
#
|
#
|
||||||
#recaptcha_private_site_key =
|
#recaptcha_private_site_key =
|
||||||
|
|
||||||
# Policy documents, such as terms and conditions or a privacy policy,
|
# Controls whether users are allowed to deactivate their own accounts
|
||||||
# which users must agree to when registering an account.
|
# through the account management panel or their Matrix clients. Server
|
||||||
|
# admins can always deactivate users using the relevant admin commands.
|
||||||
#
|
#
|
||||||
# Example:
|
# Note that, in some jurisdictions, you may be legally required to honor
|
||||||
# ```ignore
|
# users who request to deactivate their accounts if you set this option
|
||||||
# [global.registration_terms.privacy_policy]
|
# to `false`.
|
||||||
# en = { name = "Privacy Policy", url = "https://homeserver.example/en/privacy_policy.html" }
|
|
||||||
# es = { name = "Política de Privacidad", url = "https://homeserver.example/es/privacy_policy.html" }
|
|
||||||
# ```
|
|
||||||
#
|
#
|
||||||
#registration_terms = {}
|
#allow_deactivation = true
|
||||||
|
|
||||||
# Controls whether encrypted rooms and events are allowed.
|
# Controls whether encrypted rooms and events are allowed.
|
||||||
#
|
#
|
||||||
@@ -1987,3 +1985,33 @@
|
|||||||
# `require_email_for_registration`.
|
# `require_email_for_registration`.
|
||||||
#
|
#
|
||||||
#require_email_for_token_registration = false
|
#require_email_for_token_registration = false
|
||||||
|
|
||||||
|
#[global.registration_terms]
|
||||||
|
|
||||||
|
# The language code to provide to clients along with the policy documents.
|
||||||
|
#
|
||||||
|
#language = "en"
|
||||||
|
|
||||||
|
# Policy documents, such as terms and conditions or a privacy policy,
|
||||||
|
# which users must agree to when registering an account.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# ```ignore
|
||||||
|
# [global.registration_terms.documents]
|
||||||
|
# privacy_policy = { name = "Privacy Policy", url = "https://homeserver.example/en/privacy_policy.html" }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
#documents = {}
|
||||||
|
|
||||||
|
#[global.oauth]
|
||||||
|
|
||||||
|
# The compatibility mode to use for OAuth.
|
||||||
|
#
|
||||||
|
# - "disabled": OAuth will be unavailable. Users will only be able to log
|
||||||
|
# in using legacy authentication.
|
||||||
|
# - "hybrid": OAuth and legacy authentication will both be available. Some
|
||||||
|
# clients may only use one or the other.
|
||||||
|
# - "exclusive": Only OAuth will be available. Clients which require
|
||||||
|
# legacy authentication will be unable to log in.
|
||||||
|
#
|
||||||
|
#compatibility_mode = "hybrid"
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(name = conduwuit_core::name(), version = conduwuit_core::version())]
|
#[command(name = conduwuit_core::BRANDING, version = conduwuit_core::version())]
|
||||||
pub enum AdminCommand {
|
pub enum AdminCommand {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
/// Commands for managing appservices
|
/// Commands for managing appservices
|
||||||
|
|||||||
@@ -30,14 +30,37 @@ pub(super) async fn issue_token(&self, expires: super::TokenExpires) -> Result {
|
|||||||
.issue_token(self.sender_or_service_user().into(), expires);
|
.issue_token(self.sender_or_service_user().into(), expires);
|
||||||
|
|
||||||
self.write_str(&format!(
|
self.write_str(&format!(
|
||||||
"New registration token issued: `{token}`. {}.",
|
"New registration token issued: `{token}` . {}.",
|
||||||
if let Some(expires) = info.expires {
|
if let Some(expires) = info.expires {
|
||||||
format!("{expires}")
|
format!("{expires}")
|
||||||
} else {
|
} else {
|
||||||
"Never expires".to_owned()
|
"Never expires".to_owned()
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
.await
|
.await?;
|
||||||
|
|
||||||
|
if self
|
||||||
|
.services
|
||||||
|
.config
|
||||||
|
.oauth
|
||||||
|
.compatibility_mode
|
||||||
|
.oauth_available()
|
||||||
|
{
|
||||||
|
self.write_str(&format!(
|
||||||
|
"\nInvite link using this token: {}",
|
||||||
|
self.services
|
||||||
|
.config
|
||||||
|
.get_client_domain()
|
||||||
|
.join(&format!(
|
||||||
|
"{}/account/register/?flow=trusted&token={token}",
|
||||||
|
conduwuit::ROUTE_PREFIX
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
|
|||||||
+13
-149
@@ -1,13 +1,10 @@
|
|||||||
use std::{
|
use std::collections::{BTreeMap, HashSet};
|
||||||
collections::{BTreeMap, HashSet},
|
|
||||||
fmt::Write as _,
|
|
||||||
};
|
|
||||||
|
|
||||||
use api::client::{
|
use api::client::{
|
||||||
full_user_deactivate, leave_room, recreate_push_rules_and_return, remote_leave_room,
|
full_user_deactivate, leave_room, recreate_push_rules_and_return, remote_leave_room,
|
||||||
};
|
};
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Result, debug_warn, error, info,
|
Err, Result, debug_warn, info,
|
||||||
matrix::{Event, pdu::PartialPdu},
|
matrix::{Event, pdu::PartialPdu},
|
||||||
utils::{self, ReadyExt},
|
utils::{self, ReadyExt},
|
||||||
warn,
|
warn,
|
||||||
@@ -53,130 +50,22 @@ pub(super) async fn list_users(&self) -> Result {
|
|||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn create_user(&self, username: String, password: Option<String>) -> Result {
|
pub(super) async fn create_user(&self, username: String, password: Option<String>) -> Result {
|
||||||
// Validate user id
|
// Validate user id
|
||||||
let user_id = parse_local_user_id(self.services, &username)?;
|
let user_id = self
|
||||||
|
|
||||||
if let Err(e) = user_id.validate_strict() {
|
|
||||||
if self.services.config.emergency_password.is_none() {
|
|
||||||
return Err!("Username {user_id} contains disallowed characters or spaces: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.services.users.exists(&user_id).await {
|
|
||||||
return Err!("User {user_id} already exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
let password = password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH));
|
|
||||||
|
|
||||||
// Create user
|
|
||||||
self.services
|
|
||||||
.users
|
|
||||||
.create(&user_id, Some(HashedPassword::new(&password)?))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Default to pretty displayname
|
|
||||||
let mut displayname = user_id.localpart().to_owned();
|
|
||||||
|
|
||||||
// If `new_user_displayname_suffix` is set, registration will push whatever
|
|
||||||
// content is set to the user's display name with a space before it
|
|
||||||
if !self
|
|
||||||
.services
|
.services
|
||||||
.server
|
.users
|
||||||
.config
|
.determine_registration_user_id(Some(username), None, None)
|
||||||
.new_user_displayname_suffix
|
.await?;
|
||||||
.is_empty()
|
|
||||||
{
|
let password = HashedPassword::new(
|
||||||
write!(displayname, " {}", self.services.server.config.new_user_displayname_suffix)?;
|
&password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH)),
|
||||||
}
|
)?;
|
||||||
|
|
||||||
self.services
|
self.services
|
||||||
.users
|
.users
|
||||||
.set_displayname(&user_id, Some(displayname));
|
.create_local_account(&user_id, password, None)
|
||||||
|
.await;
|
||||||
|
|
||||||
// Initial account data
|
self.write_str(&format!("Created user {user_id}")).await
|
||||||
self.services
|
|
||||||
.account_data
|
|
||||||
.update(
|
|
||||||
None,
|
|
||||||
&user_id,
|
|
||||||
ruma::events::GlobalAccountDataEventType::PushRules
|
|
||||||
.to_string()
|
|
||||||
.into(),
|
|
||||||
&serde_json::to_value(ruma::events::push_rules::PushRulesEvent::new(
|
|
||||||
ruma::events::push_rules::PushRulesEventContent::new(
|
|
||||||
ruma::push::Ruleset::server_default(&user_id),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if !self.services.server.config.auto_join_rooms.is_empty() {
|
|
||||||
for room in &self.services.server.config.auto_join_rooms {
|
|
||||||
let Ok(room_id) = self.services.rooms.alias.resolve(room).await else {
|
|
||||||
error!(
|
|
||||||
%user_id,
|
|
||||||
"Failed to resolve room alias to room ID when attempting to auto join {room}, skipping"
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
if !self
|
|
||||||
.services
|
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.server_in_room(self.services.globals.server_name(), &room_id)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
warn!(
|
|
||||||
"Skipping room {room} to automatically join as we have never joined before."
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(room_server_name) = room.server_name() {
|
|
||||||
match self
|
|
||||||
.services
|
|
||||||
.rooms
|
|
||||||
.membership
|
|
||||||
.join_room(
|
|
||||||
&user_id,
|
|
||||||
&room_id,
|
|
||||||
Some("Automatically joining this room upon registration".to_owned()),
|
|
||||||
&[
|
|
||||||
self.services.globals.server_name().to_owned(),
|
|
||||||
room_server_name.to_owned(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
| Ok(_response) => {
|
|
||||||
info!("Automatically joined room {room} for user {user_id}");
|
|
||||||
},
|
|
||||||
| Err(e) => {
|
|
||||||
// don't return this error so we don't fail registrations
|
|
||||||
error!(
|
|
||||||
"Failed to automatically join room {room} for user {user_id}: {e}"
|
|
||||||
);
|
|
||||||
self.services
|
|
||||||
.admin
|
|
||||||
.send_text(&format!(
|
|
||||||
"Failed to automatically join room {room} for user {user_id}: \
|
|
||||||
{e}"
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we dont add a device since we're not the user, just the creator
|
|
||||||
|
|
||||||
// Make the first user to register an administrator and disable first-run mode.
|
|
||||||
self.services.firstrun.empower_first_user(&user_id).await?;
|
|
||||||
|
|
||||||
self.write_str(&format!("Created user with user_id: {user_id} and password: `{password}`"))
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
@@ -302,31 +191,6 @@ pub(super) async fn reset_password(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
|
||||||
pub(super) async fn issue_password_reset_link(&self, username: String) -> Result {
|
|
||||||
use conduwuit_service::password_reset::{PASSWORD_RESET_PATH, RESET_TOKEN_QUERY_PARAM};
|
|
||||||
|
|
||||||
self.bail_restricted()?;
|
|
||||||
|
|
||||||
let mut reset_url = self
|
|
||||||
.services
|
|
||||||
.config
|
|
||||||
.get_client_domain()
|
|
||||||
.join(PASSWORD_RESET_PATH)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let user_id = parse_local_user_id(self.services, &username)?;
|
|
||||||
let token = self.services.password_reset.issue_token(user_id).await?;
|
|
||||||
reset_url
|
|
||||||
.query_pairs_mut()
|
|
||||||
.append_pair(RESET_TOKEN_QUERY_PARAM, &token.token);
|
|
||||||
|
|
||||||
self.write_str(&format!("Password reset link issued for {username}: {reset_url}"))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) -> Result {
|
pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) -> Result {
|
||||||
if self.body.len() < 2
|
if self.body.len() < 2
|
||||||
|
|||||||
@@ -29,12 +29,6 @@ pub enum UserCommand {
|
|||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Issue a self-service password reset link for a user.
|
|
||||||
IssuePasswordResetLink {
|
|
||||||
/// Username of the user who may use the link
|
|
||||||
username: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Get a user's associated email address.
|
/// Get a user's associated email address.
|
||||||
GetEmail {
|
GetEmail {
|
||||||
user_id: String,
|
user_id: String,
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ zstd_compression = [
|
|||||||
"reqwest/zstd",
|
"reqwest/zstd",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
admin_api = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
axum-client-ip.workspace = true
|
axum-client-ip.workspace = true
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
pub mod rooms;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use axum::extract::State;
|
|
||||||
use conduwuit::{Err, Result};
|
|
||||||
use futures::StreamExt;
|
|
||||||
use ruma::OwnedRoomId;
|
|
||||||
use ruminuwuity::admin::continuwuity::rooms;
|
|
||||||
|
|
||||||
use crate::Ruma;
|
|
||||||
|
|
||||||
/// # `GET /_continuwuity/admin/rooms/list`
|
|
||||||
///
|
|
||||||
/// Lists all rooms known to this server, excluding banned ones.
|
|
||||||
pub(crate) async fn list_rooms(
|
|
||||||
State(services): State<crate::State>,
|
|
||||||
body: Ruma<rooms::list::v1::Request>,
|
|
||||||
) -> Result<rooms::list::v1::Response> {
|
|
||||||
let sender_user = body.sender_user();
|
|
||||||
if !services.users.is_admin(sender_user).await {
|
|
||||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rooms: Vec<OwnedRoomId> = services
|
|
||||||
.rooms
|
|
||||||
.metadata
|
|
||||||
.iter_ids()
|
|
||||||
.filter_map(|room_id| async move {
|
|
||||||
if !services.rooms.metadata.is_banned(&room_id).await {
|
|
||||||
Some(room_id.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
.await;
|
|
||||||
rooms.sort();
|
|
||||||
Ok(rooms::list::v1::Response::new(rooms))
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
pub mod ban;
|
|
||||||
pub mod list;
|
|
||||||
@@ -24,10 +24,10 @@ use ruma::{
|
|||||||
power_levels::RoomPowerLevelsEventContent,
|
power_levels::RoomPowerLevelsEventContent,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use service::{mailer::messages, uiaa::Identity, users::HashedPassword};
|
use service::{mailer::messages, uiaa::UiaaInitiator, users::HashedPassword};
|
||||||
|
|
||||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||||
use crate::Ruma;
|
use crate::{Ruma, router::ClientIdentity};
|
||||||
|
|
||||||
pub(crate) mod register;
|
pub(crate) mod register;
|
||||||
pub(crate) mod threepid;
|
pub(crate) mod threepid;
|
||||||
@@ -49,41 +49,16 @@ pub(crate) async fn get_register_available_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<get_username_availability::v3::Request>,
|
body: Ruma<get_username_availability::v3::Request>,
|
||||||
) -> Result<get_username_availability::v3::Response> {
|
) -> Result<get_username_availability::v3::Response> {
|
||||||
// Validate user id
|
let _ = services
|
||||||
let user_id =
|
.users
|
||||||
match UserId::parse_with_server_name(&body.username, services.globals.server_name()) {
|
.determine_registration_user_id(
|
||||||
| Ok(user_id) => {
|
Some(body.username.clone()),
|
||||||
if let Err(e) = user_id.validate_strict() {
|
None,
|
||||||
return Err!(Request(InvalidUsername(debug_warn!(
|
body.identity
|
||||||
"Username {} contains disallowed characters or spaces: {e}",
|
.as_ref()
|
||||||
body.username
|
.and_then(ClientIdentity::appservice_info),
|
||||||
))));
|
)
|
||||||
}
|
.await?;
|
||||||
|
|
||||||
user_id
|
|
||||||
},
|
|
||||||
| Err(e) => {
|
|
||||||
return Err!(Request(InvalidUsername(debug_warn!(
|
|
||||||
"Username {} is not valid: {e}",
|
|
||||||
body.username
|
|
||||||
))));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check if username is creative enough
|
|
||||||
if services.users.exists(&user_id).await {
|
|
||||||
return Err!(Request(UserInUse("User ID is not available.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref info) = body.appservice_info {
|
|
||||||
if !info.is_user_match(&user_id) {
|
|
||||||
return Err!(Request(Exclusive("Username is not in an appservice namespace.")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if services.appservice.is_exclusive_user_id(&user_id).await {
|
|
||||||
return Err!(Request(Exclusive("Username is reserved by an appservice.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(get_username_availability::v3::Response::new(true))
|
Ok(get_username_availability::v3::Response::new(true))
|
||||||
}
|
}
|
||||||
@@ -111,7 +86,7 @@ pub(crate) async fn change_password_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<change_password::v3::Request>,
|
body: Ruma<change_password::v3::Request>,
|
||||||
) -> Result<change_password::v3::Response> {
|
) -> Result<change_password::v3::Response> {
|
||||||
let identity = if let Some(ref user_id) = body.sender_user {
|
let identity = if let Some(identity) = body.identity.as_ref() {
|
||||||
// A signed-in user is trying to change their password, prompt them for their
|
// A signed-in user is trying to change their password, prompt them for their
|
||||||
// existing one
|
// existing one
|
||||||
|
|
||||||
@@ -121,7 +96,7 @@ pub(crate) async fn change_password_route(
|
|||||||
&body.auth,
|
&body.auth,
|
||||||
vec![AuthFlow::new(vec![AuthType::Password])],
|
vec![AuthFlow::new(vec![AuthType::Password])],
|
||||||
Box::default(),
|
Box::default(),
|
||||||
Some(Identity::from_user_id(user_id)),
|
Some(UiaaInitiator::new(identity.sender_user(), identity.sender_device())),
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else {
|
} else {
|
||||||
@@ -157,7 +132,12 @@ pub(crate) async fn change_password_route(
|
|||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.all_device_ids(&sender_user)
|
.all_device_ids(&sender_user)
|
||||||
.ready_filter(|id| *id != body.sender_device())
|
.ready_filter(|id| {
|
||||||
|
body.identity
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|identity| identity.sender_device())
|
||||||
|
.is_none_or(|sender_device| sender_device != *id)
|
||||||
|
})
|
||||||
.for_each(async |id| services.users.remove_device(&sender_user, &id).await)
|
.for_each(async |id| services.users.remove_device(&sender_user, &id).await)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -173,7 +153,12 @@ pub(crate) async fn change_password_route(
|
|||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|pusher_device| pusher_device != body.sender_device())
|
.is_some_and(|pusher_device| {
|
||||||
|
body.identity
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|identity| identity.sender_device())
|
||||||
|
.is_none_or(|sender_device| sender_device != *pusher_device)
|
||||||
|
})
|
||||||
.then_some(pushkey)
|
.then_some(pushkey)
|
||||||
})
|
})
|
||||||
.for_each(async |pushkey| {
|
.for_each(async |pushkey| {
|
||||||
@@ -241,9 +226,11 @@ pub(crate) async fn whoami_route(
|
|||||||
State(_): State<crate::State>,
|
State(_): State<crate::State>,
|
||||||
body: Ruma<whoami::v3::Request>,
|
body: Ruma<whoami::v3::Request>,
|
||||||
) -> Result<whoami::v3::Response> {
|
) -> Result<whoami::v3::Response> {
|
||||||
Ok(assign!(whoami::v3::Response::new(body.sender_user().to_owned(), false), {
|
Ok(
|
||||||
device_id: body.sender_device,
|
assign!(whoami::v3::Response::new(body.identity.sender_user().to_owned(), false), {
|
||||||
}))
|
device_id: body.identity.sender_device().map(ToOwned::to_owned),
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/account/deactivate`
|
/// # `POST /_matrix/client/r0/account/deactivate`
|
||||||
@@ -265,15 +252,24 @@ pub(crate) async fn deactivate_route(
|
|||||||
) -> Result<deactivate::v3::Response> {
|
) -> Result<deactivate::v3::Response> {
|
||||||
// Authentication for this endpoint is technically optional,
|
// Authentication for this endpoint is technically optional,
|
||||||
// but we require the user to be logged in
|
// but we require the user to be logged in
|
||||||
let sender_user = body
|
let identity = body
|
||||||
.sender_user
|
.identity
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
|
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
|
||||||
|
|
||||||
|
let sender_user = identity.sender_user();
|
||||||
|
|
||||||
|
if !services.config.allow_deactivation {
|
||||||
|
return Err!(Request(Unauthorized(
|
||||||
|
"You may not deactivate your own account. Contact your server's administrator for \
|
||||||
|
assistance."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
// Prompt the user to confirm with their password using UIAA
|
// Prompt the user to confirm with their password using UIAA
|
||||||
let _ = services
|
let _ = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
.authenticate_password(&body.auth, sender_user, identity.sender_device(), None)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Remove profile pictures and display name
|
// Remove profile pictures and display name
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
use std::{collections::HashMap, fmt::Write};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::ClientIp;
|
use axum_client_ip::ClientIp;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Result, debug_info, error, info,
|
Err, Result, debug_info, info,
|
||||||
utils::{self},
|
utils::{self},
|
||||||
warn,
|
|
||||||
};
|
};
|
||||||
use conduwuit_service::Services;
|
use conduwuit_service::Services;
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::StreamExt;
|
||||||
use lettre::{Address, message::Mailbox};
|
use lettre::{Address, message::Mailbox};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedUserId, UserId,
|
|
||||||
api::client::{
|
api::client::{
|
||||||
account::{
|
account::{
|
||||||
register::{self, LoginType, RegistrationKind},
|
register::{self, LoginType, RegistrationKind},
|
||||||
@@ -20,11 +18,6 @@ use ruma::{
|
|||||||
uiaa::{AuthFlow, AuthType},
|
uiaa::{AuthFlow, AuthType},
|
||||||
},
|
},
|
||||||
assign,
|
assign,
|
||||||
events::{
|
|
||||||
GlobalAccountDataEventType, push_rules::PushRulesEvent,
|
|
||||||
room::message::RoomMessageEventContent,
|
|
||||||
},
|
|
||||||
push,
|
|
||||||
};
|
};
|
||||||
use serde_json::value::RawValue;
|
use serde_json::value::RawValue;
|
||||||
use service::{mailer::messages, users::HashedPassword};
|
use service::{mailer::messages, users::HashedPassword};
|
||||||
@@ -32,8 +25,6 @@ use service::{mailer::messages, users::HashedPassword};
|
|||||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
|
|
||||||
const RANDOM_USER_ID_LENGTH: usize = 10;
|
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/register`
|
/// # `POST /_matrix/client/v3/register`
|
||||||
///
|
///
|
||||||
/// Register an account on this homeserver.
|
/// Register an account on this homeserver.
|
||||||
@@ -52,14 +43,12 @@ pub(crate) async fn register_route(
|
|||||||
return Err!(Request(GuestAccessForbidden("Guests may not register on this server.")));
|
return Err!(Request(GuestAccessForbidden("Guests may not register on this server.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let emergency_mode_enabled = services.config.emergency_password.is_some();
|
|
||||||
|
|
||||||
// Allow registration if it's enabled in the config file or if this is the first
|
// Allow registration if it's enabled in the config file or if this is the first
|
||||||
// run (so the first user account can be created)
|
// run (so the first user account can be created)
|
||||||
let allow_registration =
|
let allow_registration =
|
||||||
services.config.allow_registration || services.firstrun.is_first_run();
|
services.config.allow_registration || services.firstrun.is_first_run();
|
||||||
|
|
||||||
if !allow_registration && body.appservice_info.is_none() {
|
if !allow_registration && body.identity.is_none() {
|
||||||
info!(
|
info!(
|
||||||
?body.username,
|
?body.username,
|
||||||
?body.initial_device_display_name,
|
?body.initial_device_display_name,
|
||||||
@@ -71,101 +60,59 @@ pub(crate) async fn register_route(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let identity = if body.appservice_info.is_some() {
|
let user_id = if body.body.login_type == Some(LoginType::ApplicationService) {
|
||||||
// Appservices can skip auth
|
let Some(appservice_info) = &body.identity else {
|
||||||
None
|
return Err!(Request(Forbidden(
|
||||||
|
"Only appservices can use the appservice login type."
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_id = services
|
||||||
|
.users
|
||||||
|
.determine_registration_user_id(body.username.clone(), None, Some(appservice_info))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
services.users.create(&user_id, None).await?;
|
||||||
|
|
||||||
|
user_id
|
||||||
} else {
|
} else {
|
||||||
// Perform UIAA to determine the user's identity
|
// Perform UIAA to determine the user's identity
|
||||||
let (flows, params) = create_registration_uiaa_session(&services).await?;
|
let (flows, params) = create_registration_uiaa_session(&services).await?;
|
||||||
|
|
||||||
Some(
|
let identity = services
|
||||||
services
|
.uiaa
|
||||||
.uiaa
|
.authenticate(&body.auth, flows, params, None)
|
||||||
.authenticate(&body.auth, flows, params, None)
|
|
||||||
.await?,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the user didn't supply a username but did supply an email, use
|
|
||||||
// the email's user as their initial localpart to avoid falling back to
|
|
||||||
// a randomly generated localpart
|
|
||||||
let supplied_username = body.username.clone().or_else(|| {
|
|
||||||
if let Some(identity) = &identity
|
|
||||||
&& let Some(email) = &identity.email
|
|
||||||
{
|
|
||||||
Some(email.user().to_owned())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let user_id =
|
|
||||||
determine_registration_user_id(&services, supplied_username, emergency_mode_enabled)
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if body.body.login_type == Some(LoginType::ApplicationService) {
|
let password = if let Some(password) = &body.password {
|
||||||
// For appservice logins, make sure that the user ID is in the appservice's
|
HashedPassword::new(password)?
|
||||||
// namespace
|
} else {
|
||||||
|
return Err!(Request(InvalidParam("A password must be provided.")));
|
||||||
|
};
|
||||||
|
|
||||||
match body.appservice_info {
|
let user_id = services
|
||||||
| Some(ref info) =>
|
.users
|
||||||
if !info.is_user_match(&user_id) && !emergency_mode_enabled {
|
.determine_registration_user_id(body.username.clone(), identity.email.as_ref(), None)
|
||||||
return Err!(Request(Exclusive(
|
.await?;
|
||||||
"Username is not in an appservice namespace."
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
| _ => {
|
|
||||||
return Err!(Request(MissingToken("Missing appservice token.")));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else if services.appservice.is_exclusive_user_id(&user_id).await && !emergency_mode_enabled
|
|
||||||
{
|
|
||||||
// For non-appservice logins, ban user IDs which are in an appservice's
|
|
||||||
// namespace (unless emergency mode is enabled)
|
|
||||||
return Err!(Request(Exclusive("Username is reserved by an appservice.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
let password = if body.appservice_info.is_some() {
|
services
|
||||||
None
|
.users
|
||||||
} else if let Some(password) = body.password.as_deref() {
|
.create_local_account(&user_id, password, identity.email)
|
||||||
Some(HashedPassword::new(password)?)
|
.await;
|
||||||
} else {
|
services.users.join_auto_join_rooms(&user_id).await;
|
||||||
return Err!(Request(InvalidParam("A password must be provided")));
|
user_id
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create user
|
|
||||||
services.users.create(&user_id, password).await?;
|
|
||||||
|
|
||||||
// Set an initial display name
|
|
||||||
let mut displayname = user_id.localpart().to_owned();
|
|
||||||
|
|
||||||
// Apply the new user displayname suffix, if it's set
|
|
||||||
if !services.globals.new_user_displayname_suffix().is_empty()
|
|
||||||
&& body.appservice_info.is_none()
|
|
||||||
{
|
|
||||||
write!(displayname, " {}", services.server.config.new_user_displayname_suffix)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
services
|
|
||||||
.users
|
|
||||||
.set_displayname(&user_id, Some(displayname.clone()));
|
|
||||||
|
|
||||||
// Initial account data
|
|
||||||
services
|
|
||||||
.account_data
|
|
||||||
.update(
|
|
||||||
None,
|
|
||||||
&user_id,
|
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
|
||||||
&serde_json::to_value(PushRulesEvent::new(
|
|
||||||
push::Ruleset::server_default(&user_id).into(),
|
|
||||||
))
|
|
||||||
.expect("should be able to serialize push rules"),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Generate new device id if the user didn't specify one
|
|
||||||
let (token, device) = if !body.inhibit_login {
|
let (token, device) = if !body.inhibit_login {
|
||||||
|
// If UIAA is disabled, we can't create a device. In that case only appservices
|
||||||
|
// can reach this point in the first place, so we return an error for them.
|
||||||
|
if !services.config.oauth.compatibility_mode.uiaa_available() {
|
||||||
|
return Err!(Request(AppserviceLoginUnsupported(
|
||||||
|
"User-interactive appservice registration is not available on this server."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate new device id if the user didn't specify one
|
||||||
let device_id = body
|
let device_id = body
|
||||||
.device_id
|
.device_id
|
||||||
.clone()
|
.clone()
|
||||||
@@ -181,6 +128,7 @@ pub(crate) async fn register_route(
|
|||||||
&user_id,
|
&user_id,
|
||||||
&device_id,
|
&device_id,
|
||||||
&new_token,
|
&new_token,
|
||||||
|
None,
|
||||||
body.initial_device_display_name.clone(),
|
body.initial_device_display_name.clone(),
|
||||||
Some(client.to_string()),
|
Some(client.to_string()),
|
||||||
)
|
)
|
||||||
@@ -191,118 +139,7 @@ pub(crate) async fn register_route(
|
|||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
debug_info!(%user_id, ?device, "User account was created");
|
debug_info!(%user_id, ?device, "New account created via legacy registration");
|
||||||
|
|
||||||
// If the user registered with an email, associate it with their account.
|
|
||||||
if let Some(identity) = identity
|
|
||||||
&& let Some(email) = identity.email
|
|
||||||
{
|
|
||||||
// This may fail if the email is already in use, but we already check for that
|
|
||||||
// in `/requestToken`, so ignoring the error is acceptable here in the rare case
|
|
||||||
// that an email is sniped by another user between the `/requestToken` request
|
|
||||||
// and the `/register` request.
|
|
||||||
let _ = services
|
|
||||||
.threepid
|
|
||||||
.associate_localpart_email(user_id.localpart(), &email)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
let device_display_name = body.initial_device_display_name.as_deref().unwrap_or("");
|
|
||||||
|
|
||||||
if body.appservice_info.is_none() {
|
|
||||||
if !device_display_name.is_empty() {
|
|
||||||
let notice = format!(
|
|
||||||
"New user \"{user_id}\" registered on this server from IP {client} and device \
|
|
||||||
display name \"{device_display_name}\""
|
|
||||||
);
|
|
||||||
|
|
||||||
info!("{notice}");
|
|
||||||
if services.server.config.admin_room_notices {
|
|
||||||
services.admin.notice(¬ice).await;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let notice = format!("New user \"{user_id}\" registered on this server.");
|
|
||||||
|
|
||||||
info!("{notice}");
|
|
||||||
if services.server.config.admin_room_notices {
|
|
||||||
services.admin.notice(¬ice).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the first user to register an administrator and disable first-run mode.
|
|
||||||
let was_first_user = services.firstrun.empower_first_user(&user_id).await?;
|
|
||||||
|
|
||||||
// If the registering user was not the first and we're suspending users on
|
|
||||||
// register, suspend them.
|
|
||||||
if !was_first_user && services.config.suspend_on_register {
|
|
||||||
// Note that we can still do auto joins for suspended users
|
|
||||||
services
|
|
||||||
.users
|
|
||||||
.suspend_account(&user_id, &services.globals.server_user)
|
|
||||||
.await;
|
|
||||||
// And send an @room notice to the admin room, to prompt admins to review the
|
|
||||||
// new user and ideally unsuspend them if deemed appropriate.
|
|
||||||
if services.server.config.admin_room_notices {
|
|
||||||
services
|
|
||||||
.admin
|
|
||||||
.send_loud_message(RoomMessageEventContent::text_plain(format!(
|
|
||||||
"User {user_id} has been suspended as they are not the first user on this \
|
|
||||||
server. Please review and unsuspend them if appropriate."
|
|
||||||
)))
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if body.appservice_info.is_none() && !services.server.config.auto_join_rooms.is_empty() {
|
|
||||||
for room in &services.server.config.auto_join_rooms {
|
|
||||||
let Ok(room_id) = services.rooms.alias.resolve(room).await else {
|
|
||||||
error!(
|
|
||||||
"Failed to resolve room alias to room ID when attempting to auto join \
|
|
||||||
{room}, skipping"
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
if !services
|
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.server_in_room(services.globals.server_name(), &room_id)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
warn!(
|
|
||||||
"Skipping room {room} to automatically join as we have never joined before."
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(room_server_name) = room.server_name() {
|
|
||||||
match services
|
|
||||||
.rooms
|
|
||||||
.membership
|
|
||||||
.join_room(
|
|
||||||
&user_id,
|
|
||||||
&room_id,
|
|
||||||
Some("Automatically joining this room upon registration".to_owned()),
|
|
||||||
&[services.globals.server_name().to_owned(), room_server_name.to_owned()],
|
|
||||||
)
|
|
||||||
.boxed()
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
| Err(e) => {
|
|
||||||
// don't return this error so we don't fail registrations
|
|
||||||
error!(
|
|
||||||
"Failed to automatically join room {room} for user {user_id}: {e}"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
| _ => {
|
|
||||||
info!("Automatically joined room {room} for user {user_id}");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(assign!(register::v3::Response::new(user_id), {
|
Ok(assign!(register::v3::Response::new(user_id), {
|
||||||
access_token: token,
|
access_token: token,
|
||||||
@@ -374,21 +211,21 @@ async fn create_registration_uiaa_session(
|
|||||||
|
|
||||||
// Require all users to agree to the terms and conditions, if configured
|
// Require all users to agree to the terms and conditions, if configured
|
||||||
let terms = &services.config.registration_terms;
|
let terms = &services.config.registration_terms;
|
||||||
if !terms.is_empty() {
|
if !terms.documents.is_empty() {
|
||||||
let mut terms =
|
let mut terms_map = HashMap::new();
|
||||||
serde_json::to_value(terms.clone()).expect("failed to serialize terms");
|
|
||||||
|
|
||||||
// Insert a dummy `version` field
|
for (id, document) in &terms.documents {
|
||||||
for (_, documents) in terms.as_object_mut().unwrap() {
|
terms_map.insert(id.to_owned(), serde_json::json!({
|
||||||
let documents = documents.as_object_mut().unwrap();
|
terms.language.clone(): serde_json::to_value(document).expect("should be able to serialize document")
|
||||||
|
}));
|
||||||
documents.insert("version".to_owned(), "latest".into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
terms_map.insert("version".to_owned(), "latest".into());
|
||||||
|
|
||||||
params.insert(
|
params.insert(
|
||||||
AuthType::Terms.as_str().to_owned(),
|
AuthType::Terms.as_str().to_owned(),
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"policies": terms,
|
"policies": terms_map,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -421,81 +258,6 @@ async fn create_registration_uiaa_session(
|
|||||||
Ok((flows, params))
|
Ok((flows, params))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn determine_registration_user_id(
|
|
||||||
services: &Services,
|
|
||||||
supplied_username: Option<String>,
|
|
||||||
emergency_mode_enabled: bool,
|
|
||||||
) -> Result<OwnedUserId> {
|
|
||||||
if let Some(supplied_username) = supplied_username {
|
|
||||||
// The user gets to pick their username. Do some validation to make sure it's
|
|
||||||
// acceptable.
|
|
||||||
|
|
||||||
// Don't allow registration with forbidden usernames.
|
|
||||||
if services
|
|
||||||
.globals
|
|
||||||
.forbidden_usernames()
|
|
||||||
.is_match(&supplied_username)
|
|
||||||
&& !emergency_mode_enabled
|
|
||||||
{
|
|
||||||
return Err!(Request(Forbidden("Username is forbidden")));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and validate the user ID
|
|
||||||
let user_id = match UserId::parse_with_server_name(
|
|
||||||
&supplied_username,
|
|
||||||
services.globals.server_name(),
|
|
||||||
) {
|
|
||||||
| Ok(user_id) => {
|
|
||||||
if let Err(e) = user_id.validate_strict() {
|
|
||||||
// Unless we are in emergency mode, we should follow synapse's behaviour on
|
|
||||||
// not allowing things like spaces and UTF-8 characters in usernames
|
|
||||||
if !emergency_mode_enabled {
|
|
||||||
return Err!(Request(InvalidUsername(debug_warn!(
|
|
||||||
"Username {supplied_username} contains disallowed characters or \
|
|
||||||
spaces: {e}"
|
|
||||||
))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow registration with user IDs that aren't local
|
|
||||||
if !services.globals.user_is_local(&user_id) {
|
|
||||||
return Err!(Request(InvalidUsername(
|
|
||||||
"Username {supplied_username} is not local to this server"
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
user_id
|
|
||||||
},
|
|
||||||
| Err(e) => {
|
|
||||||
return Err!(Request(InvalidUsername(debug_warn!(
|
|
||||||
"Username {supplied_username} is not valid: {e}"
|
|
||||||
))));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if services.users.exists(&user_id).await {
|
|
||||||
return Err!(Request(UserInUse("User ID is not available.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(user_id)
|
|
||||||
} else {
|
|
||||||
// The user didn't specify a username. Generate a username for
|
|
||||||
// them.
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let user_id = UserId::parse_with_server_name(
|
|
||||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
|
||||||
services.globals.server_name(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if !services.users.exists(&user_id).await {
|
|
||||||
break Ok(user_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/register/email/requestToken`
|
/// # `POST /_matrix/client/v3/register/email/requestToken`
|
||||||
///
|
///
|
||||||
/// Requests a validation email for the purpose of registering a new account.
|
/// Requests a validation email for the purpose of registering a new account.
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
thirdparty::{Medium, ThirdPartyIdentifierInit},
|
thirdparty::{Medium, ThirdPartyIdentifierInit},
|
||||||
};
|
};
|
||||||
use service::{mailer::messages, uiaa::Identity};
|
use service::mailer::messages;
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::{Ruma, router::ClientIdentity};
|
||||||
|
|
||||||
/// # `GET _matrix/client/v3/account/3pid`
|
/// # `GET _matrix/client/v3/account/3pid`
|
||||||
///
|
///
|
||||||
@@ -22,7 +22,7 @@ pub(crate) async fn third_party_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_3pids::v3::Request>,
|
body: Ruma<get_3pids::v3::Request>,
|
||||||
) -> Result<get_3pids::v3::Response> {
|
) -> Result<get_3pids::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let mut threepids = vec![];
|
let mut threepids = vec![];
|
||||||
|
|
||||||
if let Some(email) = services
|
if let Some(email) = services
|
||||||
@@ -53,6 +53,14 @@ pub(crate) async fn request_3pid_management_token_via_email_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<request_3pid_management_token_via_email::v3::Request>,
|
body: Ruma<request_3pid_management_token_via_email::v3::Request>,
|
||||||
) -> Result<request_3pid_management_token_via_email::v3::Response> {
|
) -> Result<request_3pid_management_token_via_email::v3::Response> {
|
||||||
|
// Authentication for this endpoint is technically optional,
|
||||||
|
// but we require the user to be logged in
|
||||||
|
let sender_user = body
|
||||||
|
.identity
|
||||||
|
.as_ref()
|
||||||
|
.map(ClientIdentity::sender_user)
|
||||||
|
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
|
||||||
|
|
||||||
if !services.threepid.email_requirement().may_change() {
|
if !services.threepid.email_requirement().may_change() {
|
||||||
return Err!(Request(Forbidden("You may not change your email address.")));
|
return Err!(Request(Forbidden("You may not change your email address.")));
|
||||||
}
|
}
|
||||||
@@ -76,7 +84,7 @@ pub(crate) async fn request_3pid_management_token_via_email_route(
|
|||||||
Mailbox::new(None, email),
|
Mailbox::new(None, email),
|
||||||
|verification_link| messages::ChangeEmail {
|
|verification_link| messages::ChangeEmail {
|
||||||
server_name: services.config.server_name.as_str(),
|
server_name: services.config.server_name.as_str(),
|
||||||
user_id: body.sender_user.as_deref(),
|
user_id: Some(sender_user),
|
||||||
verification_link,
|
verification_link,
|
||||||
},
|
},
|
||||||
&body.client_secret,
|
&body.client_secret,
|
||||||
@@ -107,8 +115,6 @@ pub(crate) async fn add_3pid_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<add_3pid::v3::Request>,
|
body: Ruma<add_3pid::v3::Request>,
|
||||||
) -> Result<add_3pid::v3::Response> {
|
) -> Result<add_3pid::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
|
||||||
|
|
||||||
if !services.threepid.email_requirement().may_change() {
|
if !services.threepid.email_requirement().may_change() {
|
||||||
return Err!(Request(Forbidden("You may not change your email address.")));
|
return Err!(Request(Forbidden("You may not change your email address.")));
|
||||||
}
|
}
|
||||||
@@ -116,18 +122,24 @@ pub(crate) async fn add_3pid_route(
|
|||||||
// Require password auth to add an email
|
// Require password auth to add an email
|
||||||
let _ = services
|
let _ = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
.authenticate_password(
|
||||||
|
&body.auth,
|
||||||
|
body.identity.sender_user(),
|
||||||
|
body.identity.sender_device(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let email = services
|
let email = services
|
||||||
.threepid
|
.threepid
|
||||||
.consume_valid_session(&body.sid, &body.client_secret)
|
.get_valid_session(&body.sid, &body.client_secret)
|
||||||
.await
|
.await
|
||||||
.map_err(|message| err!(Request(ThreepidAuthFailed("{message}"))))?;
|
.map_err(|message| err!(Request(ThreepidAuthFailed("{message}"))))?
|
||||||
|
.consume();
|
||||||
|
|
||||||
services
|
services
|
||||||
.threepid
|
.threepid
|
||||||
.associate_localpart_email(sender_user.localpart(), &email)
|
.associate_localpart_email(body.identity.sender_user().localpart(), &email)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(add_3pid::v3::Response::new())
|
Ok(add_3pid::v3::Response::new())
|
||||||
@@ -138,8 +150,6 @@ pub(crate) async fn delete_3pid_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<delete_3pid::v3::Request>,
|
body: Ruma<delete_3pid::v3::Request>,
|
||||||
) -> Result<delete_3pid::v3::Response> {
|
) -> Result<delete_3pid::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
|
||||||
|
|
||||||
if body.medium != Medium::Email {
|
if body.medium != Medium::Email {
|
||||||
return Ok(delete_3pid::v3::Response::new(ThirdPartyIdRemovalStatus::NoSupport));
|
return Ok(delete_3pid::v3::Response::new(ThirdPartyIdRemovalStatus::NoSupport));
|
||||||
}
|
}
|
||||||
@@ -150,7 +160,7 @@ pub(crate) async fn delete_3pid_route(
|
|||||||
|
|
||||||
if services
|
if services
|
||||||
.threepid
|
.threepid
|
||||||
.disassociate_localpart_email(sender_user.localpart())
|
.disassociate_localpart_email(body.identity.sender_user().localpart())
|
||||||
.await
|
.await
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ pub(crate) async fn set_global_account_data_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<set_global_account_data::v3::Request>,
|
body: Ruma<set_global_account_data::v3::Request>,
|
||||||
) -> Result<set_global_account_data::v3::Response> {
|
) -> Result<set_global_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if sender_user != body.user_id && body.appservice_info.is_none() {
|
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||||
return Err!(Request(Forbidden("You cannot set account data for other users.")));
|
return Err!(Request(Forbidden("You cannot set account data for other users.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,9 +47,9 @@ pub(crate) async fn set_room_account_data_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<set_room_account_data::v3::Request>,
|
body: Ruma<set_room_account_data::v3::Request>,
|
||||||
) -> Result<set_room_account_data::v3::Response> {
|
) -> Result<set_room_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if sender_user != body.user_id && body.appservice_info.is_none() {
|
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||||
return Err!(Request(Forbidden("You cannot set account data for other users.")));
|
return Err!(Request(Forbidden("You cannot set account data for other users.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +72,9 @@ pub(crate) async fn get_global_account_data_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_global_account_data::v3::Request>,
|
body: Ruma<get_global_account_data::v3::Request>,
|
||||||
) -> Result<get_global_account_data::v3::Response> {
|
) -> Result<get_global_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if sender_user != body.user_id && body.appservice_info.is_none() {
|
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||||
return Err!(Request(Forbidden("You cannot get account data of other users.")));
|
return Err!(Request(Forbidden("You cannot get account data of other users.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,9 +94,9 @@ pub(crate) async fn get_room_account_data_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_room_account_data::v3::Request>,
|
body: Ruma<get_room_account_data::v3::Request>,
|
||||||
) -> Result<get_room_account_data::v3::Response> {
|
) -> Result<get_room_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if sender_user != body.user_id && body.appservice_info.is_none() {
|
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||||
return Err!(Request(Forbidden("You cannot get account data of other users.")));
|
return Err!(Request(Forbidden("You cannot get account data of other users.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use conduwuit::Err;
|
||||||
|
use futures::future::{join, join3};
|
||||||
|
use ruma::api::client::admin::{is_user_locked, lock_user};
|
||||||
|
|
||||||
|
use crate::router::Ruma;
|
||||||
|
|
||||||
|
/// # `GET /_matrix/client/v1/admin/lock/{userId}`
|
||||||
|
///
|
||||||
|
/// Check the account lock status of a target user
|
||||||
|
pub(crate) async fn get_locked_status(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<is_user_locked::v1::Request>,
|
||||||
|
) -> conduwuit::Result<is_user_locked::v1::Response> {
|
||||||
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
let (admin, active) =
|
||||||
|
join(services.users.is_admin(sender_user), services.users.is_active(&body.user_id)).await;
|
||||||
|
if !admin {
|
||||||
|
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||||
|
}
|
||||||
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
|
return Err!(Request(InvalidParam("Can only check the lock status of local users")));
|
||||||
|
}
|
||||||
|
if !active {
|
||||||
|
return Err!(Request(NotFound("Unknown user")));
|
||||||
|
}
|
||||||
|
Ok(is_user_locked::v1::Response::new(
|
||||||
|
services.users.is_locked(&body.user_id).await?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # `PUT /_matrix/client/v1/admin/lock/{userId}`
|
||||||
|
///
|
||||||
|
/// Set the account lock status of a target user
|
||||||
|
pub(crate) async fn put_locked_status(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<lock_user::v1::Request>,
|
||||||
|
) -> conduwuit::Result<lock_user::v1::Response> {
|
||||||
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
let (sender_admin, active, target_admin) = join3(
|
||||||
|
services.users.is_admin(sender_user),
|
||||||
|
services.users.is_active(&body.user_id),
|
||||||
|
services.users.is_admin(&body.user_id),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if !sender_admin {
|
||||||
|
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||||
|
}
|
||||||
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
|
return Err!(Request(InvalidParam("Can only set the locked status of local users")));
|
||||||
|
}
|
||||||
|
if !active {
|
||||||
|
return Err!(Request(NotFound("Unknown user")));
|
||||||
|
}
|
||||||
|
if body.user_id == *sender_user {
|
||||||
|
return Err!(Request(Forbidden("You cannot lock yourself")));
|
||||||
|
}
|
||||||
|
if target_admin {
|
||||||
|
return Err!(Request(Forbidden("You cannot lock another server administrator")));
|
||||||
|
}
|
||||||
|
if services.users.is_locked(&body.user_id).await? == body.locked {
|
||||||
|
// No change
|
||||||
|
return Ok(lock_user::v1::Response::new(body.locked));
|
||||||
|
}
|
||||||
|
|
||||||
|
let action = if body.locked {
|
||||||
|
services
|
||||||
|
.users
|
||||||
|
.lock_account(&body.user_id, sender_user)
|
||||||
|
.await;
|
||||||
|
"suspended"
|
||||||
|
} else {
|
||||||
|
services.users.unlock_account(&body.user_id).await;
|
||||||
|
"unsuspended"
|
||||||
|
};
|
||||||
|
|
||||||
|
if services.config.admin_room_notices {
|
||||||
|
// Notify the admin room that an account has been un/suspended
|
||||||
|
services
|
||||||
|
.admin
|
||||||
|
.send_text(&format!("{} has been {} by {}.", body.user_id, action, sender_user))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(lock_user::v1::Response::new(body.locked))
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
mod lock;
|
||||||
|
pub(crate) mod site;
|
||||||
mod suspend;
|
mod suspend;
|
||||||
|
|
||||||
pub(crate) use self::suspend::*;
|
pub(crate) use self::{lock::*, suspend::*};
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub(crate) mod rooms;
|
||||||
|
pub(crate) mod users;
|
||||||
@@ -6,14 +6,14 @@ use ruminuwuity::admin::continuwuity::rooms;
|
|||||||
|
|
||||||
use crate::{Ruma, client::leave_room};
|
use crate::{Ruma, client::leave_room};
|
||||||
|
|
||||||
/// # `PUT /_continuwuity/admin/rooms/{roomID}/ban`
|
/// # `PUT /_continuwuity/admin/v1/rooms/{roomID}/ban`
|
||||||
///
|
///
|
||||||
/// Bans or unbans a room.
|
/// Bans or unbans a room.
|
||||||
pub(crate) async fn ban_room(
|
pub(crate) async fn ban_room(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<rooms::ban::v1::Request>,
|
body: Ruma<rooms::ban::v1::Request>,
|
||||||
) -> Result<rooms::ban::v1::Response> {
|
) -> Result<rooms::ban::v1::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
if !services.users.is_admin(sender_user).await {
|
if !services.users.is_admin(sender_user).await {
|
||||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use conduwuit::{
|
||||||
|
Err, Event, Result,
|
||||||
|
utils::stream::{BroadbandExt, WidebandExt},
|
||||||
|
};
|
||||||
|
use futures::StreamExt;
|
||||||
|
use ruma::{
|
||||||
|
OwnedRoomId,
|
||||||
|
events::{
|
||||||
|
StateEventType,
|
||||||
|
room::{
|
||||||
|
create::RoomCreateEventContent,
|
||||||
|
encryption::PossiblyRedactedRoomEncryptionEventContent,
|
||||||
|
tombstone::PossiblyRedactedRoomTombstoneEventContent,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use ruminuwuity::admin::continuwuity::rooms;
|
||||||
|
use tokio::join;
|
||||||
|
|
||||||
|
use crate::Ruma;
|
||||||
|
|
||||||
|
/// # `GET /_continuwuity/admin/rooms`
|
||||||
|
///
|
||||||
|
/// Lists all room IDs known to this server, excluding banned ones.
|
||||||
|
///
|
||||||
|
/// This is the legacy version of the endpoint, which does not support
|
||||||
|
/// pagination or including banned rooms. It is recommended to use the
|
||||||
|
/// `/v1/rooms` endpoint instead. This endpoint may be removed in a future
|
||||||
|
/// release.
|
||||||
|
pub(crate) async fn legacy_list_rooms_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<rooms::list::unstable::Request>,
|
||||||
|
) -> Result<rooms::list::unstable::Response> {
|
||||||
|
let sender_user = body.identity.sender_user();
|
||||||
|
if !services.users.is_admin(sender_user).await {
|
||||||
|
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rooms: Vec<OwnedRoomId> = services
|
||||||
|
.rooms
|
||||||
|
.metadata
|
||||||
|
.iter_ids()
|
||||||
|
.filter_map(|room_id| async move {
|
||||||
|
if !services.rooms.metadata.is_banned(&room_id).await {
|
||||||
|
Some(room_id.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
.await;
|
||||||
|
rooms.sort();
|
||||||
|
Ok(rooms::list::unstable::Response::new(rooms))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # `GET /_continuwuity/admin/v1/rooms`
|
||||||
|
///
|
||||||
|
/// Lists rooms known to this server.
|
||||||
|
pub(crate) async fn list_rooms_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<rooms::list::v1::Request>,
|
||||||
|
) -> Result<rooms::list::v1::Response> {
|
||||||
|
let sender_user = body.sender_user();
|
||||||
|
if !services.users.is_admin(sender_user).await {
|
||||||
|
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||||
|
}
|
||||||
|
|
||||||
|
let include_banned_rooms = body.include_banned_rooms;
|
||||||
|
let rooms = services
|
||||||
|
.rooms
|
||||||
|
.metadata
|
||||||
|
.iter_ids()
|
||||||
|
.wide_filter_map(|room_id| async move {
|
||||||
|
if include_banned_rooms || !services.rooms.metadata.is_banned(&room_id).await {
|
||||||
|
Some(room_id.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.skip(body.offset.unwrap_or_default())
|
||||||
|
.take(body.limit.unwrap_or(100).min(100))
|
||||||
|
.broad_filter_map(|room_id| async move {
|
||||||
|
let (
|
||||||
|
banned,
|
||||||
|
disabled,
|
||||||
|
member_count,
|
||||||
|
local_member_count,
|
||||||
|
resident_server_count,
|
||||||
|
published,
|
||||||
|
create_event,
|
||||||
|
encryption_event,
|
||||||
|
name_event,
|
||||||
|
topic_event,
|
||||||
|
canonical_alias_event,
|
||||||
|
join_rules_event,
|
||||||
|
history_visibility_event,
|
||||||
|
tombstone_event,
|
||||||
|
) = join!(
|
||||||
|
services.rooms.metadata.is_banned(&room_id),
|
||||||
|
services.rooms.metadata.is_disabled(&room_id),
|
||||||
|
services.rooms.state_cache.room_joined_count(&room_id),
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.active_local_users_in_room(&room_id)
|
||||||
|
.count(),
|
||||||
|
services.rooms.state_cache.room_servers(&room_id).count(),
|
||||||
|
services.rooms.directory.is_public_room(&room_id),
|
||||||
|
services.rooms.state_accessor.room_state_get(
|
||||||
|
&room_id,
|
||||||
|
&StateEventType::RoomCreate,
|
||||||
|
""
|
||||||
|
),
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.room_state_get_content::<PossiblyRedactedRoomEncryptionEventContent>(
|
||||||
|
&room_id,
|
||||||
|
&StateEventType::RoomEncryption,
|
||||||
|
""
|
||||||
|
),
|
||||||
|
services.rooms.state_accessor.room_state_get_content(
|
||||||
|
&room_id,
|
||||||
|
&StateEventType::RoomName,
|
||||||
|
""
|
||||||
|
),
|
||||||
|
services.rooms.state_accessor.room_state_get_content(
|
||||||
|
&room_id,
|
||||||
|
&StateEventType::RoomTopic,
|
||||||
|
""
|
||||||
|
),
|
||||||
|
services.rooms.state_accessor.room_state_get_content(
|
||||||
|
&room_id,
|
||||||
|
&StateEventType::RoomCanonicalAlias,
|
||||||
|
""
|
||||||
|
),
|
||||||
|
services.rooms.state_accessor.room_state_get_content(
|
||||||
|
&room_id,
|
||||||
|
&StateEventType::RoomJoinRules,
|
||||||
|
""
|
||||||
|
),
|
||||||
|
services.rooms.state_accessor.room_state_get_content(
|
||||||
|
&room_id,
|
||||||
|
&StateEventType::RoomHistoryVisibility,
|
||||||
|
""
|
||||||
|
),
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.room_state_get_content::<PossiblyRedactedRoomTombstoneEventContent>(
|
||||||
|
&room_id,
|
||||||
|
&StateEventType::RoomTombstone,
|
||||||
|
""
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let Ok(create_event) = create_event else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let create_content = create_event
|
||||||
|
.get_content::<RoomCreateEventContent>()
|
||||||
|
.expect("m.room.create content must be valid");
|
||||||
|
Some(rooms::list::v1::MinimalRoomInfo {
|
||||||
|
room_id,
|
||||||
|
banned,
|
||||||
|
disabled,
|
||||||
|
member_count: usize::try_from(member_count.unwrap_or_default())
|
||||||
|
.expect("u64 should fit in usize"),
|
||||||
|
local_member_count,
|
||||||
|
resident_server_count,
|
||||||
|
creators: vec![create_event.sender],
|
||||||
|
encrypted: encryption_event.is_ok_and(|c| c.algorithm.is_some()),
|
||||||
|
federated: create_content.federate,
|
||||||
|
published,
|
||||||
|
version: create_content.room_version,
|
||||||
|
name: name_event.unwrap_or(None),
|
||||||
|
topic: topic_event.unwrap_or(None),
|
||||||
|
canonical_alias: canonical_alias_event.unwrap_or(None),
|
||||||
|
join_rules: join_rules_event.unwrap_or(None),
|
||||||
|
history_visibility: history_visibility_event.unwrap_or(None),
|
||||||
|
predecessor: create_content.predecessor.map(|c| c.room_id),
|
||||||
|
successor: tombstone_event.map_or(None, |c| c.replacement_room),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
.await;
|
||||||
|
Ok(rooms::list::v1::Response::new(rooms))
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
mod ban;
|
||||||
|
mod list;
|
||||||
|
|
||||||
|
pub(crate) use ban::ban_room;
|
||||||
|
pub(crate) use list::*;
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use conduwuit::{
|
||||||
|
Err, err, error, info,
|
||||||
|
utils::{IterStream, stream::BroadbandExt},
|
||||||
|
warn,
|
||||||
|
};
|
||||||
|
use futures::{FutureExt, StreamExt};
|
||||||
|
use ruma::UserId;
|
||||||
|
use ruminuwuity::admin::continuwuity::users;
|
||||||
|
use service::users::HashedPassword;
|
||||||
|
|
||||||
|
use crate::router::Ruma;
|
||||||
|
|
||||||
|
/// # `POST /_continuwuity/admin/v1/users/create`
|
||||||
|
///
|
||||||
|
/// Creates a new user.
|
||||||
|
pub(crate) async fn create_user_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<users::create::v1::Request>,
|
||||||
|
) -> conduwuit::Result<users::create::v1::Response> {
|
||||||
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
if !services.users.is_admin(sender_user).await {
|
||||||
|
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||||
|
}
|
||||||
|
let user_id =
|
||||||
|
&UserId::parse_with_server_name(&body.localpart, services.globals.server_name())?;
|
||||||
|
if services.users.is_active_local(user_id).await {
|
||||||
|
return Err!(Conflict("A user with this username already exists"));
|
||||||
|
}
|
||||||
|
|
||||||
|
services
|
||||||
|
.users
|
||||||
|
.create_local_account(
|
||||||
|
user_id,
|
||||||
|
HashedPassword::new(&body.password)?,
|
||||||
|
body.email
|
||||||
|
.clone()
|
||||||
|
.map(lettre::Address::try_from)
|
||||||
|
.transpose()
|
||||||
|
.map_err(|e| err!(Request(BadJson("Invalid email address: {e}"))))?,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if body.suspended {
|
||||||
|
services.users.suspend_account(user_id, sender_user).await;
|
||||||
|
}
|
||||||
|
if body.locked {
|
||||||
|
services.users.lock_account(user_id, sender_user).await;
|
||||||
|
}
|
||||||
|
if body.login_disabled {
|
||||||
|
services.users.disable_login(user_id);
|
||||||
|
}
|
||||||
|
if let Some(ref value) = body.display_name {
|
||||||
|
services.users.set_profile_key(
|
||||||
|
user_id,
|
||||||
|
"displayname",
|
||||||
|
Some(serde_json::to_value(value)?),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(ref value) = body.avatar_url {
|
||||||
|
services
|
||||||
|
.users
|
||||||
|
.set_profile_key(user_id, "avatar_url", Some(serde_json::to_value(value)?));
|
||||||
|
}
|
||||||
|
if body.admin {
|
||||||
|
services
|
||||||
|
.admin
|
||||||
|
.make_user_admin(user_id)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| error!("failed to make new user {user_id} an admin: {e}"))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
if !body.skip_auto_join {
|
||||||
|
services.users.join_auto_join_rooms(user_id).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.auto_join_rooms
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.stream()
|
||||||
|
.broad_filter_map(|room| async move {
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.alias
|
||||||
|
.resolve_with_servers(&room, None)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
warn!(
|
||||||
|
"Failed to resolve room alias to room ID when attempting to auto join \
|
||||||
|
{room}: {e}"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.for_each_concurrent(None, |(room_id, servers)| async move {
|
||||||
|
match services
|
||||||
|
.rooms
|
||||||
|
.membership
|
||||||
|
.join_room(
|
||||||
|
user_id,
|
||||||
|
&room_id,
|
||||||
|
Some("Automatically joining this room upon registration".to_owned()),
|
||||||
|
servers.as_ref(),
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
| Err(e) => {
|
||||||
|
warn!("Failed to automatically join {user_id} to {room_id}: {e}");
|
||||||
|
},
|
||||||
|
| _ => {
|
||||||
|
info!("Automatically joined room {user_id} to {room_id}");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(users::create::v1::Response::new(user_id.to_owned()))
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use conduwuit::{Err, utils::stream::WidebandExt};
|
||||||
|
use futures::StreamExt;
|
||||||
|
use ruminuwuity::admin::continuwuity::users;
|
||||||
|
use tokio::join;
|
||||||
|
|
||||||
|
use crate::router::Ruma;
|
||||||
|
|
||||||
|
/// # `GET /_continuwuity/admin/v1/users`
|
||||||
|
///
|
||||||
|
/// Lists all users on this homeserver.
|
||||||
|
pub(crate) async fn list_users_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<users::list::v1::Request>,
|
||||||
|
) -> conduwuit::Result<users::list::v1::Response> {
|
||||||
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
if !services.users.is_admin(sender_user).await {
|
||||||
|
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||||
|
}
|
||||||
|
|
||||||
|
let users = services
|
||||||
|
.users
|
||||||
|
.list_local_users()
|
||||||
|
.skip(body.offset.unwrap_or_default())
|
||||||
|
.take(body.limit.unwrap_or(100).min(100))
|
||||||
|
.wide_filter_map(|user_id| async move {
|
||||||
|
let (deactivated, suspended, locked, admin, login_disabled) = join!(
|
||||||
|
services.users.is_deactivated(&user_id),
|
||||||
|
services.users.is_suspended(&user_id),
|
||||||
|
services.users.is_locked(&user_id),
|
||||||
|
services.users.is_admin(&user_id),
|
||||||
|
services.users.is_login_disabled(&user_id),
|
||||||
|
);
|
||||||
|
Some(users::list::v1::User {
|
||||||
|
user_id: user_id.clone(),
|
||||||
|
deactivated: deactivated.unwrap_or_default(),
|
||||||
|
suspended: suspended.unwrap_or_default(),
|
||||||
|
locked: locked.unwrap_or_default(),
|
||||||
|
admin,
|
||||||
|
login_disabled,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(users::list::v1::Response::new(users))
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
mod create;
|
||||||
|
mod list;
|
||||||
|
|
||||||
|
pub(crate) use create::*;
|
||||||
|
pub(crate) use list::*;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Err, Result};
|
use conduwuit::{Err, Result};
|
||||||
use futures::future::{join, join3};
|
use futures::future::{join, join3};
|
||||||
use ruminuwuity::admin::{get_suspended, set_suspended};
|
use ruma::api::client::admin::{is_user_suspended, suspend_user};
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
|
|
||||||
@@ -10,12 +10,13 @@ use crate::Ruma;
|
|||||||
/// Check the suspension status of a target user
|
/// Check the suspension status of a target user
|
||||||
pub(crate) async fn get_suspended_status(
|
pub(crate) async fn get_suspended_status(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_suspended::v1::Request>,
|
body: Ruma<is_user_suspended::v1::Request>,
|
||||||
) -> Result<get_suspended::v1::Response> {
|
) -> Result<is_user_suspended::v1::Response> {
|
||||||
let sender_user = body.sender_user();
|
let (admin, active) = join(
|
||||||
|
services.users.is_admin(body.identity.sender_user()),
|
||||||
let (admin, active) =
|
services.users.is_active(&body.user_id),
|
||||||
join(services.users.is_admin(sender_user), services.users.is_active(&body.user_id)).await;
|
)
|
||||||
|
.await;
|
||||||
if !admin {
|
if !admin {
|
||||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||||
}
|
}
|
||||||
@@ -25,7 +26,7 @@ pub(crate) async fn get_suspended_status(
|
|||||||
if !active {
|
if !active {
|
||||||
return Err!(Request(NotFound("Unknown user")));
|
return Err!(Request(NotFound("Unknown user")));
|
||||||
}
|
}
|
||||||
Ok(get_suspended::v1::Response::new(
|
Ok(is_user_suspended::v1::Response::new(
|
||||||
services.users.is_suspended(&body.user_id).await?,
|
services.users.is_suspended(&body.user_id).await?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -35,9 +36,9 @@ pub(crate) async fn get_suspended_status(
|
|||||||
/// Set the suspension status of a target user
|
/// Set the suspension status of a target user
|
||||||
pub(crate) async fn put_suspended_status(
|
pub(crate) async fn put_suspended_status(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<set_suspended::v1::Request>,
|
body: Ruma<suspend_user::v1::Request>,
|
||||||
) -> Result<set_suspended::v1::Response> {
|
) -> Result<suspend_user::v1::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let (sender_admin, active, target_admin) = join3(
|
let (sender_admin, active, target_admin) = join3(
|
||||||
services.users.is_admin(sender_user),
|
services.users.is_admin(sender_user),
|
||||||
@@ -63,7 +64,7 @@ pub(crate) async fn put_suspended_status(
|
|||||||
}
|
}
|
||||||
if services.users.is_suspended(&body.user_id).await? == body.suspended {
|
if services.users.is_suspended(&body.user_id).await? == body.suspended {
|
||||||
// No change
|
// No change
|
||||||
return Ok(set_suspended::v1::Response::new(body.suspended));
|
return Ok(suspend_user::v1::Response::new(body.suspended));
|
||||||
}
|
}
|
||||||
|
|
||||||
let action = if body.suspended {
|
let action = if body.suspended {
|
||||||
@@ -85,5 +86,5 @@ pub(crate) async fn put_suspended_status(
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(set_suspended::v1::Response::new(body.suspended))
|
Ok(suspend_user::v1::Response::new(body.suspended))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub(crate) async fn create_alias_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<create_alias::v3::Request>,
|
body: Ruma<create_alias::v3::Request>,
|
||||||
) -> Result<create_alias::v3::Response> {
|
) -> Result<create_alias::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ pub(crate) async fn create_alias_route(
|
|||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.appservice_checks(&body.room_alias, &body.appservice_info)
|
.appservice_checks(&body.room_alias, body.identity.appservice_info())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// this isn't apart of alias_checks or delete alias route because we should
|
// this isn't apart of alias_checks or delete alias route because we should
|
||||||
@@ -59,7 +59,7 @@ pub(crate) async fn delete_alias_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<delete_alias::v3::Request>,
|
body: Ruma<delete_alias::v3::Request>,
|
||||||
) -> Result<delete_alias::v3::Response> {
|
) -> Result<delete_alias::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ pub(crate) async fn delete_alias_route(
|
|||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.appservice_checks(&body.room_alias, &body.appservice_info)
|
.appservice_checks(&body.room_alias, body.identity.appservice_info())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
services
|
services
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Err, Result, err};
|
use conduwuit::{Err, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{appservice::ping, client::appservice::request_ping},
|
api::{appservice::ping, client::appservice::request_ping},
|
||||||
assign,
|
assign,
|
||||||
@@ -15,9 +15,7 @@ pub(crate) async fn appservice_ping(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<request_ping::v1::Request>,
|
body: Ruma<request_ping::v1::Request>,
|
||||||
) -> Result<request_ping::v1::Response> {
|
) -> Result<request_ping::v1::Response> {
|
||||||
let appservice_info = body.appservice_info.as_ref().ok_or_else(|| {
|
let appservice_info = &body.identity;
|
||||||
err!(Request(Forbidden("This endpoint can only be called by appservices.")))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if body.appservice_id != appservice_info.registration.id {
|
if body.appservice_id != appservice_info.registration.id {
|
||||||
return Err!(Request(Forbidden(
|
return Err!(Request(Forbidden(
|
||||||
|
|||||||
+50
-26
@@ -25,7 +25,7 @@ pub(crate) async fn create_backup_version_route(
|
|||||||
) -> Result<create_backup_version::v3::Response> {
|
) -> Result<create_backup_version::v3::Response> {
|
||||||
let version = services
|
let version = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.create_backup(body.sender_user(), &body.algorithm)?;
|
.create_backup(body.identity.sender_user(), &body.algorithm)?;
|
||||||
|
|
||||||
Ok(create_backup_version::v3::Response::new(version))
|
Ok(create_backup_version::v3::Response::new(version))
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ pub(crate) async fn update_backup_version_route(
|
|||||||
) -> Result<update_backup_version::v3::Response> {
|
) -> Result<update_backup_version::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.update_backup(body.sender_user(), &body.version, &body.algorithm)
|
.update_backup(body.identity.sender_user(), &body.version, &body.algorithm)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(update_backup_version::v3::Response::new())
|
Ok(update_backup_version::v3::Response::new())
|
||||||
@@ -55,11 +55,11 @@ pub(crate) async fn get_latest_backup_info_route(
|
|||||||
) -> Result<get_latest_backup_info::v3::Response> {
|
) -> Result<get_latest_backup_info::v3::Response> {
|
||||||
let (version, algorithm) = services
|
let (version, algorithm) = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_latest_backup(body.sender_user())
|
.get_latest_backup(body.identity.sender_user())
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Request(NotFound("Key backup does not exist."))))?;
|
.map_err(|_| err!(Request(NotFound("Key backup does not exist."))))?;
|
||||||
|
|
||||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &version).await;
|
let (count, etag) = get_count_etag(&services, body.identity.sender_user(), &version).await;
|
||||||
|
|
||||||
Ok(get_latest_backup_info::v3::Response::new(algorithm, count, etag, version))
|
Ok(get_latest_backup_info::v3::Response::new(algorithm, count, etag, version))
|
||||||
}
|
}
|
||||||
@@ -73,13 +73,14 @@ pub(crate) async fn get_backup_info_route(
|
|||||||
) -> Result<get_backup_info::v3::Response> {
|
) -> Result<get_backup_info::v3::Response> {
|
||||||
let algorithm = services
|
let algorithm = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_backup(body.sender_user(), &body.version)
|
.get_backup(body.identity.sender_user(), &body.version)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
err!(Request(NotFound("Key backup does not exist at version {:?}", body.version)))
|
err!(Request(NotFound("Key backup does not exist at version {:?}", body.version)))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
let (count, etag) =
|
||||||
|
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
|
||||||
|
|
||||||
Ok(get_backup_info::v3::Response::new(algorithm, count, etag, body.version.clone()))
|
Ok(get_backup_info::v3::Response::new(algorithm, count, etag, body.version.clone()))
|
||||||
}
|
}
|
||||||
@@ -96,7 +97,7 @@ pub(crate) async fn delete_backup_version_route(
|
|||||||
) -> Result<delete_backup_version::v3::Response> {
|
) -> Result<delete_backup_version::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.delete_backup(body.sender_user(), &body.version)
|
.delete_backup(body.identity.sender_user(), &body.version)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(delete_backup_version::v3::Response::new())
|
Ok(delete_backup_version::v3::Response::new())
|
||||||
@@ -116,7 +117,7 @@ pub(crate) async fn add_backup_keys_route(
|
|||||||
) -> Result<add_backup_keys::v3::Response> {
|
) -> Result<add_backup_keys::v3::Response> {
|
||||||
if services
|
if services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_latest_backup_version(body.sender_user())
|
.get_latest_backup_version(body.identity.sender_user())
|
||||||
.await
|
.await
|
||||||
.is_ok_and(|version| version != body.version)
|
.is_ok_and(|version| version != body.version)
|
||||||
{
|
{
|
||||||
@@ -129,12 +130,19 @@ pub(crate) async fn add_backup_keys_route(
|
|||||||
for (session_id, key_data) in &room.sessions {
|
for (session_id, key_data) in &room.sessions {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.add_key(body.sender_user(), &body.version, room_id, session_id, key_data)
|
.add_key(
|
||||||
|
body.identity.sender_user(),
|
||||||
|
&body.version,
|
||||||
|
room_id,
|
||||||
|
session_id,
|
||||||
|
key_data,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
let (count, etag) =
|
||||||
|
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
|
||||||
|
|
||||||
Ok(add_backup_keys::v3::Response::new(etag, count))
|
Ok(add_backup_keys::v3::Response::new(etag, count))
|
||||||
}
|
}
|
||||||
@@ -153,7 +161,7 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
|||||||
) -> Result<add_backup_keys_for_room::v3::Response> {
|
) -> Result<add_backup_keys_for_room::v3::Response> {
|
||||||
if services
|
if services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_latest_backup_version(body.sender_user())
|
.get_latest_backup_version(body.identity.sender_user())
|
||||||
.await
|
.await
|
||||||
.is_ok_and(|version| version != body.version)
|
.is_ok_and(|version| version != body.version)
|
||||||
{
|
{
|
||||||
@@ -165,11 +173,18 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
|||||||
for (session_id, key_data) in &body.sessions {
|
for (session_id, key_data) in &body.sessions {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.add_key(body.sender_user(), &body.version, &body.room_id, session_id, key_data)
|
.add_key(
|
||||||
|
body.identity.sender_user(),
|
||||||
|
&body.version,
|
||||||
|
&body.room_id,
|
||||||
|
session_id,
|
||||||
|
key_data,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
let (count, etag) =
|
||||||
|
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
|
||||||
|
|
||||||
Ok(add_backup_keys_for_room::v3::Response::new(etag, count))
|
Ok(add_backup_keys_for_room::v3::Response::new(etag, count))
|
||||||
}
|
}
|
||||||
@@ -188,7 +203,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
|||||||
) -> Result<add_backup_keys_for_session::v3::Response> {
|
) -> Result<add_backup_keys_for_session::v3::Response> {
|
||||||
if services
|
if services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_latest_backup_version(body.sender_user())
|
.get_latest_backup_version(body.identity.sender_user())
|
||||||
.await
|
.await
|
||||||
.is_ok_and(|version| version != body.version)
|
.is_ok_and(|version| version != body.version)
|
||||||
{
|
{
|
||||||
@@ -201,7 +216,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
|||||||
let mut ok_to_replace = true;
|
let mut ok_to_replace = true;
|
||||||
if let Some(old_key) = &services
|
if let Some(old_key) = &services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_session(body.sender_user(), &body.version, &body.room_id, &body.session_id)
|
.get_session(body.identity.sender_user(), &body.version, &body.room_id, &body.session_id)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
{
|
{
|
||||||
@@ -260,7 +275,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
|||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.add_key(
|
.add_key(
|
||||||
body.sender_user(),
|
body.identity.sender_user(),
|
||||||
&body.version,
|
&body.version,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.session_id,
|
&body.session_id,
|
||||||
@@ -269,7 +284,8 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
let (count, etag) =
|
||||||
|
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
|
||||||
|
|
||||||
Ok(add_backup_keys_for_session::v3::Response::new(etag, count))
|
Ok(add_backup_keys_for_session::v3::Response::new(etag, count))
|
||||||
}
|
}
|
||||||
@@ -283,7 +299,7 @@ pub(crate) async fn get_backup_keys_route(
|
|||||||
) -> Result<get_backup_keys::v3::Response> {
|
) -> Result<get_backup_keys::v3::Response> {
|
||||||
let rooms = services
|
let rooms = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_all(body.sender_user(), &body.version)
|
.get_all(body.identity.sender_user(), &body.version)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(get_backup_keys::v3::Response::new(rooms))
|
Ok(get_backup_keys::v3::Response::new(rooms))
|
||||||
@@ -298,7 +314,7 @@ pub(crate) async fn get_backup_keys_for_room_route(
|
|||||||
) -> Result<get_backup_keys_for_room::v3::Response> {
|
) -> Result<get_backup_keys_for_room::v3::Response> {
|
||||||
let sessions = services
|
let sessions = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_room(body.sender_user(), &body.version, &body.room_id)
|
.get_room(body.identity.sender_user(), &body.version, &body.room_id)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(get_backup_keys_for_room::v3::Response::new(sessions))
|
Ok(get_backup_keys_for_room::v3::Response::new(sessions))
|
||||||
@@ -313,7 +329,7 @@ pub(crate) async fn get_backup_keys_for_session_route(
|
|||||||
) -> Result<get_backup_keys_for_session::v3::Response> {
|
) -> Result<get_backup_keys_for_session::v3::Response> {
|
||||||
let key_data = services
|
let key_data = services
|
||||||
.key_backups
|
.key_backups
|
||||||
.get_session(body.sender_user(), &body.version, &body.room_id, &body.session_id)
|
.get_session(body.identity.sender_user(), &body.version, &body.room_id, &body.session_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
err!(Request(NotFound(debug_error!("Backup key not found for this user's session."))))
|
err!(Request(NotFound(debug_error!("Backup key not found for this user's session."))))
|
||||||
@@ -331,10 +347,11 @@ pub(crate) async fn delete_backup_keys_route(
|
|||||||
) -> Result<delete_backup_keys::v3::Response> {
|
) -> Result<delete_backup_keys::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.delete_all_keys(body.sender_user(), &body.version)
|
.delete_all_keys(body.identity.sender_user(), &body.version)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
let (count, etag) =
|
||||||
|
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
|
||||||
|
|
||||||
Ok(delete_backup_keys::v3::Response::new(etag, count))
|
Ok(delete_backup_keys::v3::Response::new(etag, count))
|
||||||
}
|
}
|
||||||
@@ -348,10 +365,11 @@ pub(crate) async fn delete_backup_keys_for_room_route(
|
|||||||
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.delete_room_keys(body.sender_user(), &body.version, &body.room_id)
|
.delete_room_keys(body.identity.sender_user(), &body.version, &body.room_id)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
let (count, etag) =
|
||||||
|
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
|
||||||
|
|
||||||
Ok(delete_backup_keys_for_room::v3::Response::new(etag, count))
|
Ok(delete_backup_keys_for_room::v3::Response::new(etag, count))
|
||||||
}
|
}
|
||||||
@@ -365,10 +383,16 @@ pub(crate) async fn delete_backup_keys_for_session_route(
|
|||||||
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
||||||
services
|
services
|
||||||
.key_backups
|
.key_backups
|
||||||
.delete_room_key(body.sender_user(), &body.version, &body.room_id, &body.session_id)
|
.delete_room_key(
|
||||||
|
body.identity.sender_user(),
|
||||||
|
&body.version,
|
||||||
|
&body.room_id,
|
||||||
|
&body.session_id,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await;
|
let (count, etag) =
|
||||||
|
get_count_etag(&services, body.identity.sender_user(), &body.version).await;
|
||||||
|
|
||||||
Ok(delete_backup_keys_for_session::v3::Response::new(etag, count))
|
Ok(delete_backup_keys_for_session::v3::Response::new(etag, count))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,11 +48,7 @@ pub(crate) async fn get_capabilities_route(
|
|||||||
json!({"enabled": services.config.forget_forced_upon_leave}),
|
json!({"enabled": services.config.forget_forced_upon_leave}),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if services
|
if services.users.is_admin(body.identity.sender_user()).await {
|
||||||
.users
|
|
||||||
.is_admin(body.sender_user.as_ref().unwrap())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
// Advertise suspension API
|
// Advertise suspension API
|
||||||
capabilities.set("uk.timedout.msc4323", json!({"suspend": true, "lock": false}))?;
|
capabilities.set("uk.timedout.msc4323", json!({"suspend": true, "lock": false}))?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ pub(crate) async fn get_context_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_context::v3::Request>,
|
body: Ruma<get_context::v3::Request>,
|
||||||
) -> Result<get_context::v3::Response> {
|
) -> Result<get_context::v3::Response> {
|
||||||
let sender = body.sender();
|
let sender_user = body.identity.sender_user();
|
||||||
let (sender_user, sender_device) = sender;
|
let sender_device = body.identity.sender_device();
|
||||||
let room_id = &body.room_id;
|
let room_id = &body.room_id;
|
||||||
let event_id = &body.event_id;
|
let event_id = &body.event_id;
|
||||||
let filter = &body.filter;
|
let filter = &body.filter;
|
||||||
@@ -143,7 +143,7 @@ pub(crate) async fn get_context_route(
|
|||||||
|
|
||||||
let lazy_loading_context = lazy_loading::Context {
|
let lazy_loading_context = lazy_loading::Context {
|
||||||
user_id: sender_user,
|
user_id: sender_user,
|
||||||
device_id: Some(sender_device),
|
device_id: sender_device,
|
||||||
room_id,
|
room_id,
|
||||||
token: Some(base_count.into_unsigned()),
|
token: Some(base_count.into_unsigned()),
|
||||||
options: Some(&filter.lazy_load_options),
|
options: Some(&filter.lazy_load_options),
|
||||||
|
|||||||
@@ -25,16 +25,11 @@ pub(crate) async fn put_dehydrated_device_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<put_dehydrated_device::Request>,
|
body: Ruma<put_dehydrated_device::Request>,
|
||||||
) -> Result<put_dehydrated_device::Response> {
|
) -> Result<put_dehydrated_device::Response> {
|
||||||
let sender_user = body
|
let device_id = body.device_id.clone();
|
||||||
.sender_user
|
|
||||||
.as_deref()
|
|
||||||
.expect("AccessToken authentication required");
|
|
||||||
|
|
||||||
let device_id = body.body.device_id.clone();
|
|
||||||
|
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.set_dehydrated_device(sender_user, body.body)
|
.set_dehydrated_device(body.identity.sender_user(), body.body)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(put_dehydrated_device::Response::new(device_id))
|
Ok(put_dehydrated_device::Response::new(device_id))
|
||||||
@@ -49,7 +44,7 @@ pub(crate) async fn delete_dehydrated_device_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<delete_dehydrated_device::Request>,
|
body: Ruma<delete_dehydrated_device::Request>,
|
||||||
) -> Result<delete_dehydrated_device::Response> {
|
) -> Result<delete_dehydrated_device::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let device_id = services.users.get_dehydrated_device_id(sender_user).await?;
|
let device_id = services.users.get_dehydrated_device_id(sender_user).await?;
|
||||||
|
|
||||||
@@ -67,7 +62,7 @@ pub(crate) async fn get_dehydrated_device_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<get_dehydrated_device::Request>,
|
body: Ruma<get_dehydrated_device::Request>,
|
||||||
) -> Result<get_dehydrated_device::Response> {
|
) -> Result<get_dehydrated_device::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let device = services.users.get_dehydrated_device(sender_user).await?;
|
let device = services.users.get_dehydrated_device(sender_user).await?;
|
||||||
|
|
||||||
@@ -83,7 +78,7 @@ pub(crate) async fn get_dehydrated_events_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<get_events::Request>,
|
body: Ruma<get_events::Request>,
|
||||||
) -> Result<get_events::Response> {
|
) -> Result<get_events::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let device_id = &body.body.device_id;
|
let device_id = &body.body.device_id;
|
||||||
let existing_id = services.users.get_dehydrated_device_id(sender_user).await;
|
let existing_id = services.users.get_dehydrated_device_id(sender_user).await;
|
||||||
|
|||||||
+11
-13
@@ -8,7 +8,6 @@ use ruma::{
|
|||||||
self, delete_device, delete_devices, get_device, get_devices, update_device,
|
self, delete_device, delete_devices, get_device, get_devices, update_device,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use service::uiaa::Identity;
|
|
||||||
|
|
||||||
use crate::{Ruma, client::DEVICE_ID_LENGTH};
|
use crate::{Ruma, client::DEVICE_ID_LENGTH};
|
||||||
|
|
||||||
@@ -21,7 +20,7 @@ pub(crate) async fn get_devices_route(
|
|||||||
) -> Result<get_devices::v3::Response> {
|
) -> Result<get_devices::v3::Response> {
|
||||||
let devices: Vec<device::Device> = services
|
let devices: Vec<device::Device> = services
|
||||||
.users
|
.users
|
||||||
.all_devices_metadata(body.sender_user())
|
.all_devices_metadata(body.identity.sender_user())
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ pub(crate) async fn get_device_route(
|
|||||||
) -> Result<get_device::v3::Response> {
|
) -> Result<get_device::v3::Response> {
|
||||||
let device = services
|
let device = services
|
||||||
.users
|
.users
|
||||||
.get_device_metadata(body.sender_user(), &body.body.device_id)
|
.get_device_metadata(body.identity.sender_user(), &body.body.device_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
|
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
|
||||||
|
|
||||||
@@ -53,8 +52,8 @@ pub(crate) async fn update_device_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<update_device::v3::Request>,
|
body: Ruma<update_device::v3::Request>,
|
||||||
) -> Result<update_device::v3::Response> {
|
) -> Result<update_device::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let appservice = body.appservice_info.as_ref();
|
let appservice = body.identity.appservice_info();
|
||||||
|
|
||||||
match services
|
match services
|
||||||
.users
|
.users
|
||||||
@@ -95,6 +94,7 @@ pub(crate) async fn update_device_route(
|
|||||||
&device_id,
|
&device_id,
|
||||||
&appservice.registration.as_token,
|
&appservice.registration.as_token,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
Some(client.to_string()),
|
Some(client.to_string()),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -118,15 +118,14 @@ pub(crate) async fn delete_device_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<delete_device::v3::Request>,
|
body: Ruma<delete_device::v3::Request>,
|
||||||
) -> Result<delete_device::v3::Response> {
|
) -> Result<delete_device::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let appservice = body.appservice_info.as_ref();
|
|
||||||
|
|
||||||
// Appservices get to skip UIAA for this endpoint
|
// Appservices get to skip UIAA for this endpoint
|
||||||
if appservice.is_none() {
|
if let Some(sender_device) = body.identity.sender_device() {
|
||||||
// Prompt the user to confirm with their password using UIAA
|
// Prompt the user to confirm with their password using UIAA
|
||||||
let _ = services
|
let _ = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
.authenticate_password(&body.auth, sender_user, Some(sender_device), None)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,15 +153,14 @@ pub(crate) async fn delete_devices_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<delete_devices::v3::Request>,
|
body: Ruma<delete_devices::v3::Request>,
|
||||||
) -> Result<delete_devices::v3::Response> {
|
) -> Result<delete_devices::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let appservice = body.appservice_info.as_ref();
|
|
||||||
|
|
||||||
// Appservices get to skip UIAA for this endpoint
|
// Appservices get to skip UIAA for this endpoint
|
||||||
if appservice.is_none() {
|
if let Some(sender_device) = body.identity.sender_device() {
|
||||||
// Prompt the user to confirm with their password using UIAA
|
// Prompt the user to confirm with their password using UIAA
|
||||||
let _ = services
|
let _ = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
.authenticate_password(&body.auth, sender_user, Some(sender_device), None)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ pub(crate) async fn set_room_visibility_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<set_room_visibility::v3::Request>,
|
body: Ruma<set_room_visibility::v3::Request>,
|
||||||
) -> Result<set_room_visibility::v3::Response> {
|
) -> Result<set_room_visibility::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if !services.rooms.metadata.exists(&body.room_id).await {
|
if !services.rooms.metadata.exists(&body.room_id).await {
|
||||||
// Return 404 if the room doesn't exist
|
// Return 404 if the room doesn't exist
|
||||||
@@ -130,7 +130,7 @@ pub(crate) async fn set_room_visibility_route(
|
|||||||
| room::Visibility::Public => {
|
| room::Visibility::Public => {
|
||||||
if services.server.config.lockdown_public_room_directory
|
if services.server.config.lockdown_public_room_directory
|
||||||
&& !services.users.is_admin(sender_user).await
|
&& !services.users.is_admin(sender_user).await
|
||||||
&& body.appservice_info.is_none()
|
&& !body.identity.is_appservice()
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
"Non-admin user {sender_user} tried to publish {0} to the room directory \
|
"Non-admin user {sender_user} tried to publish {0} to the room directory \
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub(crate) async fn get_filter_route(
|
|||||||
) -> Result<get_filter::v3::Response> {
|
) -> Result<get_filter::v3::Response> {
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.get_filter(body.sender_user(), &body.filter_id)
|
.get_filter(body.identity.sender_user(), &body.filter_id)
|
||||||
.await
|
.await
|
||||||
.map(get_filter::v3::Response::new)
|
.map(get_filter::v3::Response::new)
|
||||||
.map_err(|_| err!(Request(NotFound("Filter not found."))))
|
.map_err(|_| err!(Request(NotFound("Filter not found."))))
|
||||||
@@ -30,7 +30,7 @@ pub(crate) async fn create_filter_route(
|
|||||||
) -> Result<create_filter::v3::Response> {
|
) -> Result<create_filter::v3::Response> {
|
||||||
let filter_id = services
|
let filter_id = services
|
||||||
.users
|
.users
|
||||||
.create_filter(body.sender_user(), &body.filter);
|
.create_filter(body.identity.sender_user(), &body.filter);
|
||||||
|
|
||||||
Ok(create_filter::v3::Response::new(filter_id))
|
Ok(create_filter::v3::Response::new(filter_id))
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-7
@@ -26,7 +26,7 @@ use ruma::{
|
|||||||
serde::Raw,
|
serde::Raw,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use service::uiaa::Identity;
|
use service::oauth::OAuthTicket;
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
|
|
||||||
@@ -41,7 +41,8 @@ pub(crate) async fn upload_keys_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<upload_keys::v3::Request>,
|
body: Ruma<upload_keys::v3::Request>,
|
||||||
) -> Result<upload_keys::v3::Response> {
|
) -> Result<upload_keys::v3::Response> {
|
||||||
let (sender_user, sender_device) = body.sender();
|
let sender_user = body.identity.sender_user();
|
||||||
|
let sender_device = body.identity.expect_sender_device()?;
|
||||||
|
|
||||||
for (key_id, one_time_key) in &body.one_time_keys {
|
for (key_id, one_time_key) in &body.one_time_keys {
|
||||||
if one_time_key
|
if one_time_key
|
||||||
@@ -154,7 +155,7 @@ pub(crate) async fn get_keys_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_keys::v3::Request>,
|
body: Ruma<get_keys::v3::Request>,
|
||||||
) -> Result<get_keys::v3::Response> {
|
) -> Result<get_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
get_keys_helper(
|
get_keys_helper(
|
||||||
&services,
|
&services,
|
||||||
@@ -191,7 +192,7 @@ pub(crate) async fn upload_signing_keys_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<upload_signing_keys::v3::Request>,
|
body: Ruma<upload_signing_keys::v3::Request>,
|
||||||
) -> Result<upload_signing_keys::v3::Response> {
|
) -> Result<upload_signing_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if uiaa_needed_to_upload_keys(
|
if uiaa_needed_to_upload_keys(
|
||||||
services,
|
services,
|
||||||
@@ -204,7 +205,12 @@ pub(crate) async fn upload_signing_keys_route(
|
|||||||
{
|
{
|
||||||
let _ = services
|
let _ = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
.authenticate_password(
|
||||||
|
&body.auth,
|
||||||
|
sender_user,
|
||||||
|
body.identity.sender_device(),
|
||||||
|
Some(OAuthTicket::CrossSigningReset),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +293,7 @@ pub(crate) async fn upload_signatures_route(
|
|||||||
return Ok(upload_signatures::v3::Response::new());
|
return Ok(upload_signatures::v3::Response::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
for (user_id, keys) in &body.signed_keys {
|
for (user_id, keys) in &body.signed_keys {
|
||||||
for (key_id, key) in keys {
|
for (key_id, key) in keys {
|
||||||
@@ -340,7 +346,7 @@ pub(crate) async fn get_key_changes_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_key_changes::v3::Request>,
|
body: Ruma<get_key_changes::v3::Request>,
|
||||||
) -> Result<get_key_changes::v3::Response> {
|
) -> Result<get_key_changes::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let mut device_list_updates = HashSet::new();
|
let mut device_list_updates = HashSet::new();
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ pub(crate) async fn create_content_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<create_content::v3::Request>,
|
body: Ruma<create_content::v3::Request>,
|
||||||
) -> Result<create_content::v3::Response> {
|
) -> Result<create_content::v3::Response> {
|
||||||
let user = body.sender_user();
|
let user = body.identity.sender_user();
|
||||||
if services.users.is_suspended(user).await? {
|
if services.users.is_suspended(user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<get_content_thumbnail::v1::Request>,
|
body: Ruma<get_content_thumbnail::v1::Request>,
|
||||||
) -> Result<get_content_thumbnail::v1::Response> {
|
) -> Result<get_content_thumbnail::v1::Response> {
|
||||||
let user = body.sender_user();
|
let user = body.identity.sender_user();
|
||||||
|
|
||||||
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
||||||
let mxc = Mxc {
|
let mxc = Mxc {
|
||||||
@@ -142,7 +142,7 @@ pub(crate) async fn get_content_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<get_content::v1::Request>,
|
body: Ruma<get_content::v1::Request>,
|
||||||
) -> Result<get_content::v1::Response> {
|
) -> Result<get_content::v1::Response> {
|
||||||
let user = body.sender_user();
|
let user = body.identity.sender_user();
|
||||||
|
|
||||||
let mxc = Mxc {
|
let mxc = Mxc {
|
||||||
server_name: &body.server_name,
|
server_name: &body.server_name,
|
||||||
@@ -189,7 +189,7 @@ pub(crate) async fn get_content_as_filename_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<get_content_as_filename::v1::Request>,
|
body: Ruma<get_content_as_filename::v1::Request>,
|
||||||
) -> Result<get_content_as_filename::v1::Response> {
|
) -> Result<get_content_as_filename::v1::Response> {
|
||||||
let user = body.sender_user();
|
let user = body.identity.sender_user();
|
||||||
|
|
||||||
let mxc = Mxc {
|
let mxc = Mxc {
|
||||||
server_name: &body.server_name,
|
server_name: &body.server_name,
|
||||||
@@ -240,7 +240,7 @@ pub(crate) async fn get_media_preview_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<get_media_preview::v1::Request>,
|
body: Ruma<get_media_preview::v1::Request>,
|
||||||
) -> Result<get_media_preview::v1::Response> {
|
) -> Result<get_media_preview::v1::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let url = &body.url;
|
let url = &body.url;
|
||||||
let url = Url::parse(&body.url).map_err(|e| {
|
let url = Url::parse(&body.url).map_err(|e| {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ pub(crate) async fn get_media_preview_legacy_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<get_media_preview::v3::Request>,
|
body: Ruma<get_media_preview::v3::Request>,
|
||||||
) -> Result<get_media_preview::v3::Response> {
|
) -> Result<get_media_preview::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let url = &body.url;
|
let url = &body.url;
|
||||||
let url = Url::parse(&body.url).map_err(|e| {
|
let url = Url::parse(&body.url).map_err(|e| {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub(crate) async fn ban_user_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<ban_user::v3::Request>,
|
body: Ruma<ban_user::v3::Request>,
|
||||||
) -> Result<ban_user::v3::Response> {
|
) -> Result<ban_user::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if sender_user == body.user_id {
|
if sender_user == body.user_id {
|
||||||
return Err!(Request(Forbidden("You cannot ban yourself.")));
|
return Err!(Request(Forbidden("You cannot ban yourself.")));
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub(crate) async fn forget_room_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<forget_room::v3::Request>,
|
body: Ruma<forget_room::v3::Request>,
|
||||||
) -> Result<forget_room::v3::Response> {
|
) -> Result<forget_room::v3::Response> {
|
||||||
let user_id = body.sender_user();
|
let user_id = body.identity.sender_user();
|
||||||
let room_id = &body.room_id;
|
let room_id = &body.room_id;
|
||||||
|
|
||||||
let joined = services.rooms.state_cache.is_joined(user_id, room_id);
|
let joined = services.rooms.state_cache.is_joined(user_id, room_id);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ pub(crate) async fn invite_user_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<invite_user::v3::Request>,
|
body: Ruma<invite_user::v3::Request>,
|
||||||
) -> Result<invite_user::v3::Response> {
|
) -> Result<invite_user::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ pub(crate) async fn join_room_by_id_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<join_room_by_id::v3::Request>,
|
body: Ruma<join_room_by_id::v3::Request>,
|
||||||
) -> Result<join_room_by_id::v3::Response> {
|
) -> Result<join_room_by_id::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
||||||
) -> Result<join_room_by_id_or_alias::v3::Response> {
|
) -> Result<join_room_by_id_or_alias::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let body = &body.body;
|
let body = &body.body;
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub(crate) async fn kick_user_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<kick_user::v3::Request>,
|
body: Ruma<kick_user::v3::Request>,
|
||||||
) -> Result<kick_user::v3::Response> {
|
) -> Result<kick_user::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ pub(crate) async fn knock_room_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<knock_room::v3::Request>,
|
body: Ruma<knock_room::v3::Request>,
|
||||||
) -> Result<knock_room::v3::Response> {
|
) -> Result<knock_room::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let body = &body.body;
|
let body = &body.body;
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ pub(crate) async fn leave_room_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<leave_room::v3::Request>,
|
body: Ruma<leave_room::v3::Request>,
|
||||||
) -> Result<leave_room::v3::Response> {
|
) -> Result<leave_room::v3::Response> {
|
||||||
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone())
|
leave_room(&services, body.identity.sender_user(), &body.room_id, body.reason.clone())
|
||||||
.boxed()
|
.boxed()
|
||||||
.await
|
.await
|
||||||
.map(|()| leave_room::v3::Response::new())
|
.map(|()| leave_room::v3::Response::new())
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ pub(crate) async fn get_member_events_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_member_events::v3::Request>,
|
body: Ruma<get_member_events::v3::Request>,
|
||||||
) -> Result<get_member_events::v3::Response> {
|
) -> Result<get_member_events::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let membership = body.membership.as_ref();
|
let membership = body.membership.as_ref();
|
||||||
let not_membership = body.not_membership.as_ref();
|
let not_membership = body.not_membership.as_ref();
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ pub(crate) async fn joined_members_route(
|
|||||||
if !services
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(body.sender_user(), &body.room_id)
|
.user_can_see_state_events(body.identity.sender_user(), &body.room_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub(crate) async fn joined_rooms_route(
|
|||||||
let joined_rooms = services
|
let joined_rooms = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(body.sender_user())
|
.rooms_joined(body.identity.sender_user())
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ pub(crate) async fn unban_user_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<unban_user::v3::Request>,
|
body: Ruma<unban_user::v3::Request>,
|
||||||
) -> Result<unban_user::v3::Response> {
|
) -> Result<unban_user::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use conduwuit_service::{
|
|||||||
};
|
};
|
||||||
use futures::{FutureExt, StreamExt, TryFutureExt, future::OptionFuture, pin_mut};
|
use futures::{FutureExt, StreamExt, TryFutureExt, future::OptionFuture, pin_mut};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
DeviceId, RoomId, UserId,
|
RoomId, UserId,
|
||||||
api::{
|
api::{
|
||||||
Direction,
|
Direction,
|
||||||
client::{filter::RoomEventFilter, message::get_message_events},
|
client::{filter::RoomEventFilter, message::get_message_events},
|
||||||
@@ -37,7 +37,6 @@ use ruma::{
|
|||||||
serde::Raw,
|
serde::Raw,
|
||||||
};
|
};
|
||||||
use ruminuwuity::invite_permission_config::FilterLevel;
|
use ruminuwuity::invite_permission_config::FilterLevel;
|
||||||
use tracing::warn;
|
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
|
|
||||||
@@ -76,8 +75,8 @@ pub(crate) async fn get_message_events_route(
|
|||||||
ClientIp(client_ip): ClientIp,
|
ClientIp(client_ip): ClientIp,
|
||||||
body: Ruma<get_message_events::v3::Request>,
|
body: Ruma<get_message_events::v3::Request>,
|
||||||
) -> Result<get_message_events::v3::Response> {
|
) -> Result<get_message_events::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let sender_device = body.sender_device.as_deref();
|
let sender_device = body.identity.sender_device();
|
||||||
let room_id = &body.room_id;
|
let room_id = &body.room_id;
|
||||||
let filter = &body.filter;
|
let filter = &body.filter;
|
||||||
|
|
||||||
@@ -158,17 +157,7 @@ pub(crate) async fn get_message_events_route(
|
|||||||
|
|
||||||
let lazy_loading_context = lazy_loading::Context {
|
let lazy_loading_context = lazy_loading::Context {
|
||||||
user_id: sender_user,
|
user_id: sender_user,
|
||||||
device_id: sender_device.or_else(|| {
|
device_id: sender_device,
|
||||||
if let Some(registration) = body.appservice_info.as_ref() {
|
|
||||||
Some(<&DeviceId>::from(registration.registration.id.as_str()))
|
|
||||||
} else {
|
|
||||||
warn!(
|
|
||||||
"No device_id provided and no appservice registration found, this should be \
|
|
||||||
unreachable"
|
|
||||||
);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
room_id,
|
room_id,
|
||||||
token: Some(from.into_unsigned()),
|
token: Some(from.into_unsigned()),
|
||||||
options: Some(&filter.lazy_load_options),
|
options: Some(&filter.lazy_load_options),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub(super) mod media_legacy;
|
|||||||
pub(super) mod membership;
|
pub(super) mod membership;
|
||||||
pub(super) mod message;
|
pub(super) mod message;
|
||||||
pub(super) mod mutual_rooms;
|
pub(super) mod mutual_rooms;
|
||||||
|
pub(super) mod oauth;
|
||||||
pub(super) mod openid;
|
pub(super) mod openid;
|
||||||
pub(super) mod presence;
|
pub(super) mod presence;
|
||||||
pub(super) mod profile;
|
pub(super) mod profile;
|
||||||
@@ -61,6 +62,7 @@ pub(super) use membership::*;
|
|||||||
pub use membership::{leave_all_rooms, leave_room, remote_leave_room};
|
pub use membership::{leave_all_rooms, leave_room, remote_leave_room};
|
||||||
pub(super) use message::*;
|
pub(super) use message::*;
|
||||||
pub(super) use mutual_rooms::*;
|
pub(super) use mutual_rooms::*;
|
||||||
|
pub(super) use oauth::*;
|
||||||
pub(super) use openid::*;
|
pub(super) use openid::*;
|
||||||
pub(super) use presence::*;
|
pub(super) use presence::*;
|
||||||
pub(super) use profile::*;
|
pub(super) use profile::*;
|
||||||
@@ -73,6 +75,7 @@ pub(super) use report::*;
|
|||||||
pub(super) use room::*;
|
pub(super) use room::*;
|
||||||
pub(super) use search::*;
|
pub(super) use search::*;
|
||||||
pub(super) use send::*;
|
pub(super) use send::*;
|
||||||
|
pub use session::handle_login;
|
||||||
pub(super) use session::*;
|
pub(super) use session::*;
|
||||||
pub(super) use space::*;
|
pub(super) use space::*;
|
||||||
pub(super) use state::*;
|
pub(super) use state::*;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub(crate) async fn get_mutual_rooms_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<mutual_rooms::unstable::Request>,
|
body: Ruma<mutual_rooms::unstable::Request>,
|
||||||
) -> Result<mutual_rooms::unstable::Response> {
|
) -> Result<mutual_rooms::unstable::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if sender_user == body.user_id {
|
if sender_user == body.user_id {
|
||||||
return Err!(Request(Unknown("You cannot request rooms in common with yourself.")));
|
return Err!(Request(Unknown("You cannot request rooms in common with yourself.")));
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
use axum::{
|
||||||
|
Json, Router,
|
||||||
|
extract::{Request, State},
|
||||||
|
middleware::{self, Next},
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
routing::method_routing::{get, post},
|
||||||
|
};
|
||||||
|
use const_str::concat;
|
||||||
|
use http::StatusCode;
|
||||||
|
use serde_json::json;
|
||||||
|
pub(crate) use server_metadata::*;
|
||||||
|
|
||||||
|
mod register_client;
|
||||||
|
mod server_metadata;
|
||||||
|
mod token;
|
||||||
|
|
||||||
|
const BASE_PATH: &str = concat!(conduwuit_core::ROUTE_PREFIX, "/oauth2/");
|
||||||
|
const AUTH_CODE_PATH: &str = "grant/authorization_code";
|
||||||
|
const JWKS_URI_PATH: &str = "client/keys.json";
|
||||||
|
const CLIENT_REGISTER_PATH: &str = "client/register";
|
||||||
|
const TOKEN_REVOKE_PATH: &str = "client/revoke";
|
||||||
|
const TOKEN_PATH: &str = "grant/token";
|
||||||
|
const ACCOUNT_MANAGEMENT_PATH: &str = concat!(conduwuit_core::ROUTE_PREFIX, "/account/deeplink");
|
||||||
|
|
||||||
|
pub(crate) fn router(state: crate::State) -> Router<crate::State> {
|
||||||
|
Router::new()
|
||||||
|
.nest(BASE_PATH, oauth_router())
|
||||||
|
.route(
|
||||||
|
"/.well-known/openid-configuration",
|
||||||
|
get(
|
||||||
|
// TODO(unspecced): used by old versions of the matrix-js-sdk
|
||||||
|
async |State(services): State<crate::State>| {
|
||||||
|
Json(authorization_server_metadata(&services).await)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.layer(middleware::from_fn_with_state(
|
||||||
|
state,
|
||||||
|
async |State(state): State<crate::State>, request: Request, next: Next| -> Response {
|
||||||
|
if state.config.oauth.compatibility_mode.oauth_available() {
|
||||||
|
next.run(request).await
|
||||||
|
} else {
|
||||||
|
(StatusCode::NOT_FOUND, "OAuth is unavailable on this server").into_response()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn oauth_router() -> Router<crate::State> {
|
||||||
|
Router::new()
|
||||||
|
.route(concat!("/", CLIENT_REGISTER_PATH), post(register_client::register_client_route))
|
||||||
|
// TODO(unspecced): used by old versions of the matrix-js-sdk
|
||||||
|
.route(concat!("/", JWKS_URI_PATH), get(async || Json(json!({"keys": []}))))
|
||||||
|
.route(concat!("/", TOKEN_PATH), post(token::token_route))
|
||||||
|
.route(concat!("/", TOKEN_REVOKE_PATH), post(token::revoke_token_route))
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
extract::State,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
use http::StatusCode;
|
||||||
|
use serde::Serialize;
|
||||||
|
use service::oauth::client_metadata::ClientMetadata;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct RegisteredClient {
|
||||||
|
client_id: String,
|
||||||
|
#[serde(flatten)]
|
||||||
|
metadata: ClientMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn register_client_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
Json(metadata): Json<ClientMetadata>,
|
||||||
|
) -> Result<Response, Response> {
|
||||||
|
let client_id = services
|
||||||
|
.oauth
|
||||||
|
.register_client(&metadata)
|
||||||
|
.await
|
||||||
|
.map_err(|err| (StatusCode::BAD_REQUEST, err.to_owned()).into_response())?;
|
||||||
|
|
||||||
|
Ok(Json(RegisteredClient { client_id, metadata }).into_response())
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use conduwuit::{Err, Result};
|
||||||
|
use ruma::{
|
||||||
|
api::client::discovery::get_authorization_server_metadata::{
|
||||||
|
self, v1::AccountManagementAction,
|
||||||
|
},
|
||||||
|
serde::Raw,
|
||||||
|
};
|
||||||
|
use serde_json::{Value, json};
|
||||||
|
use service::Services;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Ruma,
|
||||||
|
client::oauth::{
|
||||||
|
ACCOUNT_MANAGEMENT_PATH, AUTH_CODE_PATH, CLIENT_REGISTER_PATH, JWKS_URI_PATH, TOKEN_PATH,
|
||||||
|
TOKEN_REVOKE_PATH,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) async fn get_authorization_server_metadata_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
_body: Ruma<get_authorization_server_metadata::v1::Request>,
|
||||||
|
) -> Result<get_authorization_server_metadata::v1::Response> {
|
||||||
|
if !services.config.oauth.compatibility_mode.oauth_available() {
|
||||||
|
return Err!(Request(Unrecognized("OAuth is unavailable on this server")));
|
||||||
|
}
|
||||||
|
|
||||||
|
let metadata = Raw::new(&authorization_server_metadata(&services).await).unwrap();
|
||||||
|
|
||||||
|
Ok(get_authorization_server_metadata::v1::Response::new(metadata.cast_unchecked()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn authorization_server_metadata(services: &Services) -> Value {
|
||||||
|
let endpoint_base = services
|
||||||
|
.config
|
||||||
|
.get_client_domain()
|
||||||
|
.join(super::BASE_PATH)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
json!({
|
||||||
|
"account_management_uri": endpoint_base.join(ACCOUNT_MANAGEMENT_PATH).unwrap(),
|
||||||
|
"account_management_actions_supported": [
|
||||||
|
AccountManagementAction::AccountDeactivate,
|
||||||
|
AccountManagementAction::CrossSigningReset,
|
||||||
|
AccountManagementAction::DeviceDelete,
|
||||||
|
AccountManagementAction::DeviceView,
|
||||||
|
AccountManagementAction::DevicesList,
|
||||||
|
AccountManagementAction::Profile,
|
||||||
|
],
|
||||||
|
"authorization_endpoint": endpoint_base.join(AUTH_CODE_PATH).unwrap(),
|
||||||
|
"code_challenge_methods_supported": ["S256"],
|
||||||
|
"grant_types_supported": ["authorization_code", "refresh_token"],
|
||||||
|
"issuer": services.config.get_client_domain(),
|
||||||
|
"jwks_uri": endpoint_base.join(JWKS_URI_PATH).unwrap(),
|
||||||
|
"prompt_values_supported": ["create"],
|
||||||
|
"registration_endpoint": endpoint_base.join(CLIENT_REGISTER_PATH).unwrap(),
|
||||||
|
"response_modes_supported": ["query", "fragment"],
|
||||||
|
"response_types_supported": ["code"],
|
||||||
|
"revocation_endpoint": endpoint_base.join(TOKEN_REVOKE_PATH).unwrap(),
|
||||||
|
"token_endpoint": endpoint_base.join(TOKEN_PATH).unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
use axum::{Form, Json, extract::State, response::IntoResponse};
|
||||||
|
use http::StatusCode;
|
||||||
|
use service::oauth::grant::{RevokeTokenRequest, TokenRequest};
|
||||||
|
|
||||||
|
pub(crate) async fn token_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
Form(request): Form<TokenRequest>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
match services.oauth.issue_token(request).await {
|
||||||
|
| Ok(response) => Ok(Json(response)),
|
||||||
|
| Err(err) => Err((StatusCode::BAD_REQUEST, err.message())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn revoke_token_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
Form(request): Form<RevokeTokenRequest>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
match services.oauth.revoke_token(request.token).await {
|
||||||
|
| Ok(()) => Ok(StatusCode::OK),
|
||||||
|
| Err(err) => Err((StatusCode::BAD_REQUEST, err.message())),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ pub(crate) async fn create_openid_token_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<account::request_openid_token::v3::Request>,
|
body: Ruma<account::request_openid_token::v3::Request>,
|
||||||
) -> Result<account::request_openid_token::v3::Response> {
|
) -> Result<account::request_openid_token::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if sender_user != body.user_id {
|
if sender_user != body.user_id {
|
||||||
return Err!(Request(InvalidParam(
|
return Err!(Request(InvalidParam(
|
||||||
|
|||||||
@@ -20,13 +20,19 @@ pub(crate) async fn set_presence_route(
|
|||||||
return Err!(Request(Forbidden("Presence is disabled on this server")));
|
return Err!(Request(Forbidden("Presence is disabled on this server")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.sender_user() != body.user_id && body.appservice_info.is_none() {
|
if body.identity.sender_user() != body.user_id && !body.identity.is_appservice() {
|
||||||
return Err!(Request(InvalidParam("Not allowed to set presence of other users")));
|
return Err!(Request(InvalidParam("Not allowed to set presence of other users")));
|
||||||
}
|
}
|
||||||
|
|
||||||
services
|
services
|
||||||
.presence
|
.presence
|
||||||
.set_presence(body.sender_user(), &body.presence, None, None, body.status_msg.clone())
|
.set_presence(
|
||||||
|
body.identity.sender_user(),
|
||||||
|
&body.presence,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
body.status_msg.clone(),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(set_presence::v3::Response::new())
|
Ok(set_presence::v3::Response::new())
|
||||||
@@ -49,7 +55,7 @@ pub(crate) async fn get_presence_route(
|
|||||||
let has_shared_rooms = services
|
let has_shared_rooms = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.user_sees_user(body.sender_user(), &body.user_id)
|
.user_sees_user(body.identity.sender_user(), &body.user_id)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if has_shared_rooms {
|
if has_shared_rooms {
|
||||||
|
|||||||
+59
-129
@@ -8,12 +8,12 @@ use ruma::{
|
|||||||
UserId,
|
UserId,
|
||||||
api::{
|
api::{
|
||||||
client::profile::{
|
client::profile::{
|
||||||
PropagateTo, delete_profile_field, get_profile, get_profile_field, set_profile_field,
|
delete_profile_field, get_profile, get_profile_field, set_profile_field,
|
||||||
},
|
},
|
||||||
federation,
|
federation,
|
||||||
},
|
},
|
||||||
assign,
|
assign,
|
||||||
events::room::member::MembershipState,
|
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||||
presence::PresenceState,
|
presence::PresenceState,
|
||||||
profile::{ProfileFieldName, ProfileFieldValue},
|
profile::{ProfileFieldName, ProfileFieldValue},
|
||||||
};
|
};
|
||||||
@@ -51,9 +51,12 @@ pub(crate) async fn set_profile_field_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<set_profile_field::v3::Request>,
|
body: Ruma<set_profile_field::v3::Request>,
|
||||||
) -> Result<set_profile_field::v3::Response> {
|
) -> Result<set_profile_field::v3::Response> {
|
||||||
if body.user_id != body.sender_user()
|
if body.user_id != body.identity.sender_user()
|
||||||
&& !(body.appservice_info.is_some()
|
&& !(body.identity.is_appservice()
|
||||||
|| services.admin.user_is_admin(body.sender_user()).await)
|
|| services
|
||||||
|
.admin
|
||||||
|
.user_is_admin(body.identity.sender_user())
|
||||||
|
.await)
|
||||||
{
|
{
|
||||||
return Err!(Request(Forbidden("You may not change other users' profile data.")));
|
return Err!(Request(Forbidden("You may not change other users' profile data.")));
|
||||||
}
|
}
|
||||||
@@ -62,13 +65,8 @@ pub(crate) async fn set_profile_field_route(
|
|||||||
return Err!(Request(InvalidParam("You may not change a remote user's profile data.")));
|
return Err!(Request(InvalidParam("You may not change a remote user's profile data.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
set_profile_field(
|
set_profile_field(&services, &body.user_id, ProfileFieldChange::Set(body.value.clone()))
|
||||||
&services,
|
.await?;
|
||||||
&body.user_id,
|
|
||||||
ProfileFieldChange::Set(body.value.clone()),
|
|
||||||
body.propagate_to.clone(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(set_profile_field::v3::Response::new())
|
Ok(set_profile_field::v3::Response::new())
|
||||||
}
|
}
|
||||||
@@ -77,9 +75,12 @@ pub(crate) async fn delete_profile_field_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<delete_profile_field::v3::Request>,
|
body: Ruma<delete_profile_field::v3::Request>,
|
||||||
) -> Result<delete_profile_field::v3::Response> {
|
) -> Result<delete_profile_field::v3::Response> {
|
||||||
if body.user_id != body.sender_user()
|
if body.user_id != body.identity.sender_user()
|
||||||
&& !(body.appservice_info.is_some()
|
&& !(body.identity.is_appservice()
|
||||||
|| services.admin.user_is_admin(body.sender_user()).await)
|
|| services
|
||||||
|
.admin
|
||||||
|
.user_is_admin(body.identity.sender_user())
|
||||||
|
.await)
|
||||||
{
|
{
|
||||||
return Err!(Request(Forbidden("You may not change other users' profile data.")));
|
return Err!(Request(Forbidden("You may not change other users' profile data.")));
|
||||||
}
|
}
|
||||||
@@ -88,13 +89,8 @@ pub(crate) async fn delete_profile_field_route(
|
|||||||
return Err!(Request(InvalidParam("You may not change a remote user's profile data.")));
|
return Err!(Request(InvalidParam("You may not change a remote user's profile data.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
set_profile_field(
|
set_profile_field(&services, &body.user_id, ProfileFieldChange::Delete(body.field.clone()))
|
||||||
&services,
|
.await?;
|
||||||
&body.user_id,
|
|
||||||
ProfileFieldChange::Delete(body.field.clone()),
|
|
||||||
body.propagate_to.clone(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(delete_profile_field::v3::Response::new())
|
Ok(delete_profile_field::v3::Response::new())
|
||||||
}
|
}
|
||||||
@@ -129,13 +125,7 @@ async fn fetch_full_profile(
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = set_profile_field(
|
let _ = set_profile_field(services, user_id, ProfileFieldChange::Set(value)).await;
|
||||||
services,
|
|
||||||
user_id,
|
|
||||||
ProfileFieldChange::Set(value),
|
|
||||||
PropagateTo::None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(BTreeMap::from_iter(response))
|
Some(BTreeMap::from_iter(response))
|
||||||
@@ -169,13 +159,8 @@ async fn fetch_profile_field(
|
|||||||
|
|
||||||
if let Some(value) = response.get(field.as_str()).map(ToOwned::to_owned) {
|
if let Some(value) = response.get(field.as_str()).map(ToOwned::to_owned) {
|
||||||
if let Ok(value) = ProfileFieldValue::new(field.as_str(), value) {
|
if let Ok(value) = ProfileFieldValue::new(field.as_str(), value) {
|
||||||
let _ = set_profile_field(
|
let _ = set_profile_field(services, user_id, ProfileFieldChange::Set(value.clone()))
|
||||||
services,
|
.await;
|
||||||
user_id,
|
|
||||||
ProfileFieldChange::Set(value.clone()),
|
|
||||||
PropagateTo::None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(Some(value))
|
Ok(Some(value))
|
||||||
} else {
|
} else {
|
||||||
@@ -184,13 +169,7 @@ async fn fetch_profile_field(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = set_profile_field(
|
let _ = set_profile_field(services, user_id, ProfileFieldChange::Delete(field)).await;
|
||||||
services,
|
|
||||||
user_id,
|
|
||||||
ProfileFieldChange::Delete(field),
|
|
||||||
PropagateTo::None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@@ -283,7 +262,6 @@ async fn set_profile_field(
|
|||||||
services: &Services,
|
services: &Services,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
change: ProfileFieldChange,
|
change: ProfileFieldChange,
|
||||||
propagate_to: PropagateTo,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
const MAX_KEY_LENGTH_BYTES: usize = 255;
|
const MAX_KEY_LENGTH_BYTES: usize = 255;
|
||||||
const MAX_PROFILE_LENGTH_BYTES: usize = 65536;
|
const MAX_PROFILE_LENGTH_BYTES: usize = 65536;
|
||||||
@@ -331,91 +309,6 @@ async fn set_profile_field(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user is local and changed their displayname or avatar_url, update it
|
|
||||||
// in all their joined rooms. This is done before updating their profile data
|
|
||||||
// so we can check the old value of the field if `propagate_to` is `unchanged`.
|
|
||||||
if matches!(field_name, ProfileFieldName::AvatarUrl | ProfileFieldName::DisplayName)
|
|
||||||
&& matches!(propagate_to, PropagateTo::All | PropagateTo::Unchanged)
|
|
||||||
&& services.globals.user_is_local(user_id)
|
|
||||||
{
|
|
||||||
let current_displayname = services.users.displayname(user_id).await.ok();
|
|
||||||
let current_avatar_url = services.users.avatar_url(user_id).await.ok();
|
|
||||||
|
|
||||||
let mut all_joined_rooms = services.rooms.state_cache.rooms_joined(user_id);
|
|
||||||
|
|
||||||
while let Some(room_id) = all_joined_rooms.next().await {
|
|
||||||
// TODO: this clobbers any custom fields on the event content
|
|
||||||
let mut current_membership = services
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.get_member(&room_id, user_id)
|
|
||||||
.await
|
|
||||||
.expect("should be able to fetch membership event for joined room");
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
current_membership.membership,
|
|
||||||
MembershipState::Join,
|
|
||||||
"user should be joined"
|
|
||||||
);
|
|
||||||
|
|
||||||
// If `propagate_to` is `unchanged`, and the current value of the field we're
|
|
||||||
// updating was changed from its global value in this room, skip it.
|
|
||||||
if matches!(propagate_to, PropagateTo::Unchanged) {
|
|
||||||
let field_changed_from_global = match field_name {
|
|
||||||
| ProfileFieldName::AvatarUrl =>
|
|
||||||
current_membership.avatar_url.as_ref() != current_avatar_url.as_ref(),
|
|
||||||
| ProfileFieldName::DisplayName =>
|
|
||||||
current_membership.displayname.as_ref() != current_displayname.as_ref(),
|
|
||||||
| _ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if field_changed_from_global {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let state_lock = services.rooms.state.mutex.lock(room_id.as_str()).await;
|
|
||||||
|
|
||||||
// Preserve keys in accordance with the key copying rules
|
|
||||||
current_membership.reason = None;
|
|
||||||
current_membership.join_authorized_via_users_server = None;
|
|
||||||
match &change {
|
|
||||||
| ProfileFieldChange::Set(ProfileFieldValue::AvatarUrl(avatar_url)) => {
|
|
||||||
current_membership.avatar_url = Some(avatar_url.clone());
|
|
||||||
},
|
|
||||||
| ProfileFieldChange::Set(ProfileFieldValue::DisplayName(displayname)) => {
|
|
||||||
current_membership.displayname = Some(displayname.clone());
|
|
||||||
},
|
|
||||||
| ProfileFieldChange::Delete(ProfileFieldName::AvatarUrl) => {
|
|
||||||
current_membership.avatar_url = None;
|
|
||||||
},
|
|
||||||
| ProfileFieldChange::Delete(ProfileFieldName::DisplayName) => {
|
|
||||||
current_membership.displayname = None;
|
|
||||||
},
|
|
||||||
| _ => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = services
|
|
||||||
.rooms
|
|
||||||
.timeline
|
|
||||||
.build_and_append_pdu(
|
|
||||||
PartialPdu::state(user_id.to_string(), ¤t_membership),
|
|
||||||
user_id,
|
|
||||||
Some(&room_id),
|
|
||||||
&state_lock,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
if services.config.allow_local_presence {
|
|
||||||
// Send a presence EDU to indicate the profile changed
|
|
||||||
let _ = services
|
|
||||||
.presence
|
|
||||||
.ping_presence(user_id, &PresenceState::Online)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match change {
|
match change {
|
||||||
| ProfileFieldChange::Set(ProfileFieldValue::DisplayName(displayname)) => {
|
| ProfileFieldChange::Set(ProfileFieldValue::DisplayName(displayname)) => {
|
||||||
services
|
services
|
||||||
@@ -439,5 +332,42 @@ async fn set_profile_field(
|
|||||||
.set_profile_key(user_id, other.field_name().as_str(), other.value()),
|
.set_profile_key(user_id, other.field_name().as_str(), other.value()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the user is local and changed their displayname or avatar_url, update it
|
||||||
|
// in all their joined rooms
|
||||||
|
if matches!(field_name, ProfileFieldName::AvatarUrl | ProfileFieldName::DisplayName)
|
||||||
|
&& services.globals.user_is_local(user_id)
|
||||||
|
{
|
||||||
|
let displayname = services.users.displayname(user_id).await.ok();
|
||||||
|
let avatar_url = services.users.avatar_url(user_id).await.ok();
|
||||||
|
let membership_content = assign!(
|
||||||
|
RoomMemberEventContent::new(MembershipState::Join), { displayname, avatar_url }
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut all_joined_rooms = services.rooms.state_cache.rooms_joined(user_id);
|
||||||
|
|
||||||
|
while let Some(room_id) = all_joined_rooms.next().await {
|
||||||
|
let state_lock = services.rooms.state.mutex.lock(room_id.as_str()).await;
|
||||||
|
|
||||||
|
let _ = services
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.build_and_append_pdu(
|
||||||
|
PartialPdu::state(user_id.to_string(), &membership_content),
|
||||||
|
user_id,
|
||||||
|
Some(&room_id),
|
||||||
|
&state_lock,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if services.config.allow_local_presence {
|
||||||
|
// Send a presence EDU to indicate the profile changed
|
||||||
|
let _ = services
|
||||||
|
.presence
|
||||||
|
.ping_presence(user_id, &PresenceState::Online)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-12
@@ -30,7 +30,7 @@ pub(crate) async fn get_pushrules_all_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_pushrules_all::v3::Request>,
|
body: Ruma<get_pushrules_all::v3::Request>,
|
||||||
) -> Result<get_pushrules_all::v3::Response> {
|
) -> Result<get_pushrules_all::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let Some(content_value) = services
|
let Some(content_value) = services
|
||||||
.account_data
|
.account_data
|
||||||
@@ -101,7 +101,7 @@ pub(crate) async fn get_pushrules_global_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_pushrules_global_scope::v3::Request>,
|
body: Ruma<get_pushrules_global_scope::v3::Request>,
|
||||||
) -> Result<get_pushrules_global_scope::v3::Response> {
|
) -> Result<get_pushrules_global_scope::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let Some(content_value) = services
|
let Some(content_value) = services
|
||||||
.account_data
|
.account_data
|
||||||
@@ -189,7 +189,7 @@ pub(crate) async fn get_pushrule_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_pushrule::v3::Request>,
|
body: Ruma<get_pushrule::v3::Request>,
|
||||||
) -> Result<get_pushrule::v3::Response> {
|
) -> Result<get_pushrule::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
// remove old deprecated mentions push rules as per MSC4210
|
// remove old deprecated mentions push rules as per MSC4210
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
@@ -226,7 +226,7 @@ pub(crate) async fn set_pushrule_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<set_pushrule::v3::Request>,
|
body: Ruma<set_pushrule::v3::Request>,
|
||||||
) -> Result<set_pushrule::v3::Response> {
|
) -> Result<set_pushrule::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let body = &body.body;
|
let body = &body.body;
|
||||||
let mut account_data: PushRulesEvent = services
|
let mut account_data: PushRulesEvent = services
|
||||||
.account_data
|
.account_data
|
||||||
@@ -282,7 +282,7 @@ pub(crate) async fn get_pushrule_actions_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_pushrule_actions::v3::Request>,
|
body: Ruma<get_pushrule_actions::v3::Request>,
|
||||||
) -> Result<get_pushrule_actions::v3::Response> {
|
) -> Result<get_pushrule_actions::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
// remove old deprecated mentions push rules as per MSC4210
|
// remove old deprecated mentions push rules as per MSC4210
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
@@ -316,7 +316,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<set_pushrule_actions::v3::Request>,
|
body: Ruma<set_pushrule_actions::v3::Request>,
|
||||||
) -> Result<set_pushrule_actions::v3::Response> {
|
) -> Result<set_pushrule_actions::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let mut account_data: PushRulesEvent = services
|
let mut account_data: PushRulesEvent = services
|
||||||
.account_data
|
.account_data
|
||||||
@@ -349,7 +349,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_pushrule_enabled::v3::Request>,
|
body: Ruma<get_pushrule_enabled::v3::Request>,
|
||||||
) -> Result<get_pushrule_enabled::v3::Response> {
|
) -> Result<get_pushrule_enabled::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
// remove old deprecated mentions push rules as per MSC4210
|
// remove old deprecated mentions push rules as per MSC4210
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
@@ -383,7 +383,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<set_pushrule_enabled::v3::Request>,
|
body: Ruma<set_pushrule_enabled::v3::Request>,
|
||||||
) -> Result<set_pushrule_enabled::v3::Response> {
|
) -> Result<set_pushrule_enabled::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let mut account_data: PushRulesEvent = services
|
let mut account_data: PushRulesEvent = services
|
||||||
.account_data
|
.account_data
|
||||||
@@ -416,7 +416,7 @@ pub(crate) async fn delete_pushrule_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<delete_pushrule::v3::Request>,
|
body: Ruma<delete_pushrule::v3::Request>,
|
||||||
) -> Result<delete_pushrule::v3::Response> {
|
) -> Result<delete_pushrule::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let mut account_data: PushRulesEvent = services
|
let mut account_data: PushRulesEvent = services
|
||||||
.account_data
|
.account_data
|
||||||
@@ -458,7 +458,7 @@ pub(crate) async fn get_pushers_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_pushers::v3::Request>,
|
body: Ruma<get_pushers::v3::Request>,
|
||||||
) -> Result<get_pushers::v3::Response> {
|
) -> Result<get_pushers::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
Ok(get_pushers::v3::Response::new(services.pusher.get_pushers(sender_user).await))
|
Ok(get_pushers::v3::Response::new(services.pusher.get_pushers(sender_user).await))
|
||||||
}
|
}
|
||||||
@@ -472,11 +472,12 @@ pub(crate) async fn set_pushers_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<set_pusher::v3::Request>,
|
body: Ruma<set_pusher::v3::Request>,
|
||||||
) -> Result<set_pusher::v3::Response> {
|
) -> Result<set_pusher::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
let sender_device = body.identity.expect_sender_device()?;
|
||||||
|
|
||||||
services
|
services
|
||||||
.pusher
|
.pusher
|
||||||
.set_pusher(sender_user, body.sender_device(), &body.action)
|
.set_pusher(sender_user, sender_device, &body.action)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(set_pusher::v3::Response::new())
|
Ok(set_pusher::v3::Response::new())
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub(crate) async fn set_read_marker_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<set_read_marker::v3::Request>,
|
body: Ruma<set_read_marker::v3::Request>,
|
||||||
) -> Result<set_read_marker::v3::Response> {
|
) -> Result<set_read_marker::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if let Some(event) = &body.fully_read {
|
if let Some(event) = &body.fully_read {
|
||||||
let fully_read_event = FullyReadEvent::new(FullyReadEventContent::new(event.to_owned()));
|
let fully_read_event = FullyReadEvent::new(FullyReadEventContent::new(event.to_owned()));
|
||||||
@@ -118,10 +118,10 @@ pub(crate) async fn create_receipt_route(
|
|||||||
ClientIp(client_ip): ClientIp,
|
ClientIp(client_ip): ClientIp,
|
||||||
body: Ruma<create_receipt::v3::Request>,
|
body: Ruma<create_receipt::v3::Request>,
|
||||||
) -> Result<create_receipt::v3::Response> {
|
) -> Result<create_receipt::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.update_device_last_seen(sender_user, body.sender_device.as_deref(), client_ip)
|
.update_device_last_seen(sender_user, body.identity.sender_device(), client_ip)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ pub(crate) async fn redact_event_route(
|
|||||||
ClientIp(client_ip): ClientIp,
|
ClientIp(client_ip): ClientIp,
|
||||||
body: Ruma<redact_event::v3::Request>,
|
body: Ruma<redact_event::v3::Request>,
|
||||||
) -> Result<redact_event::v3::Response> {
|
) -> Result<redact_event::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.update_device_last_seen(sender_user, body.sender_device.as_deref(), client_ip)
|
.update_device_last_seen(sender_user, body.identity.sender_device(), client_ip)
|
||||||
.await;
|
.await;
|
||||||
let body = &body.body;
|
let body = &body.body;
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
|||||||
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
|
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
|
||||||
paginate_relations_with_filter(
|
paginate_relations_with_filter(
|
||||||
&services,
|
&services,
|
||||||
body.sender_user(),
|
body.identity.sender_user(),
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
body.event_type.clone().into(),
|
body.event_type.clone().into(),
|
||||||
@@ -56,7 +56,7 @@ pub(crate) async fn get_relating_events_with_rel_type_route(
|
|||||||
) -> Result<get_relating_events_with_rel_type::v1::Response> {
|
) -> Result<get_relating_events_with_rel_type::v1::Response> {
|
||||||
paginate_relations_with_filter(
|
paginate_relations_with_filter(
|
||||||
&services,
|
&services,
|
||||||
body.sender_user(),
|
body.identity.sender_user(),
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
None,
|
None,
|
||||||
@@ -84,7 +84,7 @@ pub(crate) async fn get_relating_events_route(
|
|||||||
) -> Result<get_relating_events::v1::Response> {
|
) -> Result<get_relating_events::v1::Response> {
|
||||||
paginate_relations_with_filter(
|
paginate_relations_with_filter(
|
||||||
&services,
|
&services,
|
||||||
body.sender_user(),
|
body.identity.sender_user(),
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&body.event_id,
|
&body.event_id,
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ pub(crate) async fn report_room_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<report_room::v3::Request>,
|
body: Ruma<report_room::v3::Request>,
|
||||||
) -> Result<report_room::v3::Response> {
|
) -> Result<report_room::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ pub(crate) async fn report_event_route(
|
|||||||
body: Ruma<report_content::v3::Request>,
|
body: Ruma<report_content::v3::Request>,
|
||||||
) -> Result<report_content::v3::Response> {
|
) -> Result<report_content::v3::Response> {
|
||||||
// user authentication
|
// user authentication
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
@@ -135,8 +135,8 @@ pub(crate) async fn report_user_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<report_user::v3::Request>,
|
body: Ruma<report_user::v3::Request>,
|
||||||
) -> Result<report_user::v3::Response> {
|
) -> Result<report_user::v3::Response> {
|
||||||
// user authentication
|
let sender_user = body.identity.sender_user();
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub(crate) async fn get_room_aliases_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<aliases::v3::Request>,
|
body: Ruma<aliases::v3::Request>,
|
||||||
) -> Result<aliases::v3::Response> {
|
) -> Result<aliases::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if !services
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
|
|||||||
@@ -61,10 +61,10 @@ pub(crate) async fn create_room_route(
|
|||||||
) -> Result<create_room::v3::Response> {
|
) -> Result<create_room::v3::Response> {
|
||||||
use create_room::v3::RoomPreset;
|
use create_room::v3::RoomPreset;
|
||||||
|
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if !services.globals.allow_room_creation()
|
if !services.globals.allow_room_creation()
|
||||||
&& body.appservice_info.is_none()
|
&& !body.identity.is_appservice()
|
||||||
&& !services.users.is_admin(sender_user).await
|
&& !services.users.is_admin(sender_user).await
|
||||||
{
|
{
|
||||||
return Err!(Request(Forbidden("Room creation has been disabled.",)));
|
return Err!(Request(Forbidden("Room creation has been disabled.",)));
|
||||||
@@ -130,7 +130,7 @@ pub(crate) async fn create_room_route(
|
|||||||
if body.visibility == room::Visibility::Public
|
if body.visibility == room::Visibility::Public
|
||||||
&& services.server.config.lockdown_public_room_directory
|
&& services.server.config.lockdown_public_room_directory
|
||||||
&& !services.users.is_admin(sender_user).await
|
&& !services.users.is_admin(sender_user).await
|
||||||
&& body.appservice_info.is_none()
|
&& !body.identity.is_appservice()
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Non-admin user {sender_user} tried to publish {room_id:?} to the room directory \
|
"Non-admin user {sender_user} tried to publish {room_id:?} to the room directory \
|
||||||
@@ -186,7 +186,7 @@ pub(crate) async fn create_room_route(
|
|||||||
|
|
||||||
let alias: Option<OwnedRoomAliasId> = match body.room_alias_name.as_ref() {
|
let alias: Option<OwnedRoomAliasId> = match body.room_alias_name.as_ref() {
|
||||||
| Some(alias) =>
|
| Some(alias) =>
|
||||||
Some(room_alias_check(&services, alias, body.appservice_info.as_ref()).await?),
|
Some(room_alias_check(&services, alias, body.identity.appservice_info()).await?),
|
||||||
| _ => None,
|
| _ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,25 +24,25 @@ pub(crate) async fn get_room_event_route(
|
|||||||
let visible = services
|
let visible = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(body.sender_user(), room_id, event_id)
|
.user_can_see_event(body.identity.sender_user(), room_id, event_id)
|
||||||
.map(Ok);
|
.map(Ok);
|
||||||
|
|
||||||
let (mut event, visible) = try_join(event, visible).await?;
|
let (mut event, visible) = try_join(event, visible).await?;
|
||||||
|
|
||||||
if !visible || is_ignored_pdu(services, &event, body.sender_user()).await? {
|
if !visible || is_ignored_pdu(services, &event, body.identity.sender_user()).await? {
|
||||||
return Err!(Request(Forbidden("You don't have permission to view this event.")));
|
return Err!(Request(Forbidden("You don't have permission to view this event.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = services
|
if let Err(e) = services
|
||||||
.rooms
|
.rooms
|
||||||
.pdu_metadata
|
.pdu_metadata
|
||||||
.add_bundled_aggregations_to_pdu(body.sender_user(), &mut event)
|
.add_bundled_aggregations_to_pdu(body.identity.sender_user(), &mut event)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
debug_warn!("Failed to add bundled aggregations to event: {e}");
|
debug_warn!("Failed to add bundled aggregations to event: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
event.set_unsigned(body.sender_user.as_deref());
|
event.set_unsigned(Some(body.identity.sender_user()));
|
||||||
|
|
||||||
Ok(get_room_event::v3::Response::new(event.into_format()))
|
Ok(get_room_event::v3::Response::new(event.into_format()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub(crate) async fn room_initial_sync_route(
|
|||||||
if !services
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(body.sender_user(), room_id)
|
.user_can_see_state_events(body.identity.sender_user(), room_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err!(Request(Forbidden("No room preview available.")));
|
return Err!(Request(Forbidden("No room preview available.")));
|
||||||
@@ -31,7 +31,7 @@ pub(crate) async fn room_initial_sync_route(
|
|||||||
let membership = services
|
let membership = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.user_membership(body.sender_user(), room_id)
|
.user_membership(body.identity.sender_user(), room_id)
|
||||||
.map(Ok);
|
.map(Ok);
|
||||||
|
|
||||||
let visibility = services.rooms.directory.visibility(room_id).map(Ok);
|
let visibility = services.rooms.directory.visibility(room_id).map(Ok);
|
||||||
@@ -52,16 +52,14 @@ pub(crate) async fn room_initial_sync_route(
|
|||||||
.pdus_rev(room_id, None)
|
.pdus_rev(room_id, None)
|
||||||
.try_take(limit)
|
.try_take(limit)
|
||||||
.and_then(async |mut pdu| {
|
.and_then(async |mut pdu| {
|
||||||
pdu.1.set_unsigned(body.sender_user.as_deref());
|
pdu.1.set_unsigned(Some(body.identity.sender_user()));
|
||||||
if let Some(sender_user) = body.sender_user.as_deref() {
|
if let Err(e) = services
|
||||||
if let Err(e) = services
|
.rooms
|
||||||
.rooms
|
.pdu_metadata
|
||||||
.pdu_metadata
|
.add_bundled_aggregations_to_pdu(body.identity.sender_user(), &mut pdu.1)
|
||||||
.add_bundled_aggregations_to_pdu(sender_user, &mut pdu.1)
|
.await
|
||||||
.await
|
{
|
||||||
{
|
debug_warn!("Failed to add bundled aggregations: {e}");
|
||||||
debug_warn!("Failed to add bundled aggregations: {e}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(pdu)
|
Ok(pdu)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use conduwuit::{Err, Result};
|
|||||||
use ruma::api::client::room::get_summary;
|
use ruma::api::client::room::get_summary;
|
||||||
use service::rooms::summary::Accessibility;
|
use service::rooms::summary::Accessibility;
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::{Ruma, router::ClientIdentity};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/v1/room_summary/{roomIdOrAlias}`
|
/// # `GET /_matrix/client/v1/room_summary/{roomIdOrAlias}`
|
||||||
///
|
///
|
||||||
@@ -28,7 +28,11 @@ pub(crate) async fn get_room_summary(
|
|||||||
let summary = services
|
let summary = services
|
||||||
.rooms
|
.rooms
|
||||||
.summary
|
.summary
|
||||||
.get_room_summary_for_user(body.sender_user.as_deref(), &room_id, &servers)
|
.get_room_summary_for_user(
|
||||||
|
body.identity.as_ref().map(ClientIdentity::sender_user),
|
||||||
|
&room_id,
|
||||||
|
&servers,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
match summary {
|
match summary {
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ pub(crate) async fn upgrade_room_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<upgrade_room::v3::Request>,
|
body: Ruma<upgrade_room::v3::Request>,
|
||||||
) -> Result<upgrade_room::v3::Response> {
|
) -> Result<upgrade_room::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let (supported, forbid_unstable, is_unstable) = (
|
let (supported, forbid_unstable, is_unstable) = (
|
||||||
services.server.supported_room_version(&body.new_version),
|
services.server.supported_room_version(&body.new_version),
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ pub(crate) async fn search_events_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<Request>,
|
body: Ruma<Request>,
|
||||||
) -> Result<Response> {
|
) -> Result<Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let next_batch = body.next_batch.as_deref();
|
let next_batch = body.next_batch.as_deref();
|
||||||
|
|
||||||
let mut result_categories = ResultCategories::new();
|
let mut result_categories = ResultCategories::new();
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ pub(crate) async fn send_message_event_route(
|
|||||||
ClientIp(client_ip): ClientIp,
|
ClientIp(client_ip): ClientIp,
|
||||||
body: Ruma<send_message_event::v3::Request>,
|
body: Ruma<send_message_event::v3::Request>,
|
||||||
) -> Result<send_message_event::v3::Response> {
|
) -> Result<send_message_event::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let sender_device = body.sender_device.as_deref();
|
let sender_device = body.identity.sender_device();
|
||||||
let appservice_info = body.appservice_info.as_ref();
|
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.update_device_last_seen(sender_user, body.sender_device.as_deref(), client_ip)
|
.update_device_last_seen(sender_user, sender_device, client_ip)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Forbid m.room.encrypted if encryption is disabled
|
// Forbid m.room.encrypted if encryption is disabled
|
||||||
@@ -83,7 +83,11 @@ pub(crate) async fn send_message_event_route(
|
|||||||
event_type: body.event_type.clone().into(),
|
event_type: body.event_type.clone().into(),
|
||||||
content,
|
content,
|
||||||
unsigned: Some(unsigned),
|
unsigned: Some(unsigned),
|
||||||
timestamp: appservice_info.and(body.timestamp),
|
timestamp: if body.identity.is_appservice() {
|
||||||
|
body.timestamp
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
|
|||||||
+37
-24
@@ -21,7 +21,7 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
login::{
|
login::{
|
||||||
self,
|
self,
|
||||||
v3::{DiscoveryInfo, HomeserverInfo},
|
v3::{DiscoveryInfo, HomeserverInfo, LoginInfo},
|
||||||
},
|
},
|
||||||
logout, logout_all,
|
logout, logout_all,
|
||||||
},
|
},
|
||||||
@@ -29,7 +29,6 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
assign,
|
assign,
|
||||||
};
|
};
|
||||||
use service::uiaa::Identity;
|
|
||||||
|
|
||||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
@@ -44,6 +43,12 @@ pub(crate) async fn get_login_types_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
_body: Ruma<get_login_types::v3::Request>,
|
_body: Ruma<get_login_types::v3::Request>,
|
||||||
) -> Result<get_login_types::v3::Response> {
|
) -> Result<get_login_types::v3::Response> {
|
||||||
|
if !services.config.oauth.compatibility_mode.uiaa_available() {
|
||||||
|
return Err!(Request(Unrecognized(
|
||||||
|
"User-interactive authentication is not available on this server."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(get_login_types::v3::Response::new(vec![
|
Ok(get_login_types::v3::Response::new(vec![
|
||||||
get_login_types::v3::LoginType::Password(PasswordLoginType::default()),
|
get_login_types::v3::LoginType::Password(PasswordLoginType::default()),
|
||||||
get_login_types::v3::LoginType::ApplicationService(ApplicationServiceLoginType::default()),
|
get_login_types::v3::LoginType::ApplicationService(ApplicationServiceLoginType::default()),
|
||||||
@@ -53,7 +58,7 @@ pub(crate) async fn get_login_types_route(
|
|||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_login(
|
pub async fn handle_login(
|
||||||
services: &Services,
|
services: &Services,
|
||||||
identifier: Option<&UserIdentifier>,
|
identifier: Option<&UserIdentifier>,
|
||||||
password: &str,
|
password: &str,
|
||||||
@@ -87,10 +92,6 @@ pub(crate) async fn handle_login(
|
|||||||
return Err!(Request(InvalidParam("User ID does not belong to this homeserver")));
|
return Err!(Request(InvalidParam("User ID does not belong to this homeserver")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if services.users.is_locked(&user_id).await? {
|
|
||||||
return Err!(Request(UserLocked("This account has been locked.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if services.users.is_login_disabled(&user_id).await {
|
if services.users.is_login_disabled(&user_id).await {
|
||||||
warn!(%user_id, "user attempted to log in with a login-disabled account");
|
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.")));
|
return Err!(Request(Forbidden("This account is not permitted to log in.")));
|
||||||
@@ -119,19 +120,29 @@ pub(crate) async fn login_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<login::v3::Request>,
|
body: Ruma<login::v3::Request>,
|
||||||
) -> Result<login::v3::Response> {
|
) -> Result<login::v3::Response> {
|
||||||
|
if !services.config.oauth.compatibility_mode.uiaa_available() {
|
||||||
|
return match body.login_info {
|
||||||
|
| LoginInfo::ApplicationService(_) => {
|
||||||
|
Err!(Request(AppserviceLoginUnsupported(
|
||||||
|
"User-interactive appservice login is not available on this server."
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
| _ => {
|
||||||
|
Err!(Request(Unrecognized(
|
||||||
|
"User-interactive authentication is not available on this server."
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let emergency_mode_enabled = services.config.emergency_password.is_some();
|
let emergency_mode_enabled = services.config.emergency_password.is_some();
|
||||||
|
|
||||||
// Validate login method
|
// Validate login method
|
||||||
// TODO: Other login methods
|
|
||||||
let user_id = match &body.login_info {
|
let user_id = match &body.login_info {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
| login::v3::LoginInfo::Password(login::v3::Password {
|
| LoginInfo::Password(login::v3::Password { identifier, password, user, .. }) =>
|
||||||
identifier,
|
handle_login(&services, identifier.as_ref(), password, user.as_ref()).await?,
|
||||||
password,
|
| LoginInfo::Token(login::v3::Token { token, .. }) => {
|
||||||
user,
|
|
||||||
..
|
|
||||||
}) => handle_login(&services, identifier.as_ref(), password, user.as_ref()).await?,
|
|
||||||
| login::v3::LoginInfo::Token(login::v3::Token { token, .. }) => {
|
|
||||||
debug!("Got token login type");
|
debug!("Got token login type");
|
||||||
if !services.server.config.login_via_existing_session {
|
if !services.server.config.login_via_existing_session {
|
||||||
return Err!(Request(Unknown("Token login is not enabled.")));
|
return Err!(Request(Unknown("Token login is not enabled.")));
|
||||||
@@ -139,14 +150,14 @@ pub(crate) async fn login_route(
|
|||||||
services.users.find_from_login_token(token).await?
|
services.users.find_from_login_token(token).await?
|
||||||
},
|
},
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
| login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService {
|
| LoginInfo::ApplicationService(login::v3::ApplicationService {
|
||||||
identifier,
|
identifier,
|
||||||
user,
|
user,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
debug!("Got appservice login type");
|
debug!("Got appservice login type");
|
||||||
|
|
||||||
let Some(ref info) = body.appservice_info else {
|
let Some(ref info) = body.identity else {
|
||||||
return Err!(Request(MissingToken("Missing appservice token.")));
|
return Err!(Request(MissingToken("Missing appservice token.")));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -173,7 +184,6 @@ pub(crate) async fn login_route(
|
|||||||
user_id
|
user_id
|
||||||
},
|
},
|
||||||
| _ => {
|
| _ => {
|
||||||
debug!("/login json_body: {:?}", &body.json_body);
|
|
||||||
return Err!(Request(Unknown(
|
return Err!(Request(Unknown(
|
||||||
debug_warn!(?body.login_info, "Invalid or unsupported login type")
|
debug_warn!(?body.login_info, "Invalid or unsupported login type")
|
||||||
)));
|
)));
|
||||||
@@ -203,7 +213,7 @@ pub(crate) async fn login_route(
|
|||||||
if device_exists {
|
if device_exists {
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.set_token(&user_id, &device_id, &token)
|
.set_token(&user_id, &device_id, &token, None)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
services
|
services
|
||||||
@@ -212,6 +222,7 @@ pub(crate) async fn login_route(
|
|||||||
&user_id,
|
&user_id,
|
||||||
&device_id,
|
&device_id,
|
||||||
&token,
|
&token,
|
||||||
|
None,
|
||||||
body.initial_device_display_name.clone(),
|
body.initial_device_display_name.clone(),
|
||||||
Some(client.to_string()),
|
Some(client.to_string()),
|
||||||
)
|
)
|
||||||
@@ -250,16 +261,16 @@ pub(crate) async fn login_token_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<get_login_token::v1::Request>,
|
body: Ruma<get_login_token::v1::Request>,
|
||||||
) -> Result<get_login_token::v1::Response> {
|
) -> Result<get_login_token::v1::Response> {
|
||||||
if !services.server.config.login_via_existing_session {
|
if !services.config.login_via_existing_session {
|
||||||
return Err!(Request(Forbidden("Login via an existing session is not enabled")));
|
return Err!(Request(Forbidden("Login via an existing session is not enabled")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
// Prompt the user to confirm with their password using UIAA
|
// Prompt the user to confirm with their password using UIAA
|
||||||
let _ = services
|
let _ = services
|
||||||
.uiaa
|
.uiaa
|
||||||
.authenticate_password(&body.auth, Some(Identity::from_user_id(sender_user)))
|
.authenticate_password(&body.auth, sender_user, body.identity.sender_device(), None)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let login_token = utils::random_string(TOKEN_LENGTH);
|
let login_token = utils::random_string(TOKEN_LENGTH);
|
||||||
@@ -286,7 +297,9 @@ pub(crate) async fn logout_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<logout::v3::Request>,
|
body: Ruma<logout::v3::Request>,
|
||||||
) -> Result<logout::v3::Response> {
|
) -> Result<logout::v3::Response> {
|
||||||
let (sender_user, sender_device) = body.sender();
|
let sender_user = body.identity.sender_user();
|
||||||
|
let sender_device = body.identity.expect_sender_device()?;
|
||||||
|
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.remove_device(sender_user, sender_device)
|
.remove_device(sender_user, sender_device)
|
||||||
@@ -332,7 +345,7 @@ pub(crate) async fn logout_all_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<logout_all::v3::Request>,
|
body: Ruma<logout_all::v3::Request>,
|
||||||
) -> Result<logout_all::v3::Response> {
|
) -> Result<logout_all::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.all_device_ids(sender_user)
|
.all_device_ids(sender_user)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pub(crate) async fn get_hierarchy_route(
|
|||||||
.rooms
|
.rooms
|
||||||
.summary
|
.summary
|
||||||
.get_room_hierarchy_for_user(
|
.get_room_hierarchy_for_user(
|
||||||
body.sender_user(),
|
body.identity.sender_user(),
|
||||||
body.room_id.clone(),
|
body.room_id.clone(),
|
||||||
max_depth,
|
max_depth,
|
||||||
body.suggested_only,
|
body.suggested_only,
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ pub(crate) async fn send_state_event_for_key_route(
|
|||||||
ClientIp(ip): ClientIp,
|
ClientIp(ip): ClientIp,
|
||||||
body: Ruma<send_state_event::v3::Request>,
|
body: Ruma<send_state_event::v3::Request>,
|
||||||
) -> Result<send_state_event::v3::Response> {
|
) -> Result<send_state_event::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.update_device_last_seen(sender_user, body.sender_device.as_deref(), ip)
|
.update_device_last_seen(sender_user, body.identity.sender_device(), ip)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if services.users.is_suspended(sender_user).await? {
|
if services.users.is_suspended(sender_user).await? {
|
||||||
@@ -55,7 +55,7 @@ pub(crate) async fn send_state_event_for_key_route(
|
|||||||
&body.event_type,
|
&body.event_type,
|
||||||
&body.body.body,
|
&body.body.body,
|
||||||
&body.state_key,
|
&body.state_key,
|
||||||
if body.appservice_info.is_some() {
|
if body.identity.is_appservice() {
|
||||||
body.timestamp
|
body.timestamp
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -91,7 +91,7 @@ pub(crate) async fn get_state_events_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_state_events::v3::Request>,
|
body: Ruma<get_state_events::v3::Request>,
|
||||||
) -> Result<get_state_events::v3::Response> {
|
) -> Result<get_state_events::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if !services
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
@@ -125,7 +125,7 @@ pub(crate) async fn get_state_event_for_key_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_state_event_for_key::v3::Request>,
|
body: Ruma<get_state_event_for_key::v3::Request>,
|
||||||
) -> Result<get_state_event_for_key::v3::Response> {
|
) -> Result<get_state_event_for_key::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
if !services
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
|
|||||||
@@ -181,7 +181,8 @@ pub(crate) async fn sync_events_route(
|
|||||||
ClientIp(client_ip): ClientIp,
|
ClientIp(client_ip): ClientIp,
|
||||||
body: Ruma<sync_events::v3::Request>,
|
body: Ruma<sync_events::v3::Request>,
|
||||||
) -> Result<sync_events::v3::Response> {
|
) -> Result<sync_events::v3::Response> {
|
||||||
let (sender_user, sender_device) = body.sender();
|
let sender_user = body.identity.sender_user();
|
||||||
|
let sender_device = body.identity.expect_sender_device()?;
|
||||||
|
|
||||||
// Presence update
|
// Presence update
|
||||||
if services.config.allow_local_presence {
|
if services.config.allow_local_presence {
|
||||||
@@ -225,7 +226,8 @@ pub(crate) async fn build_sync_events(
|
|||||||
services: &Services,
|
services: &Services,
|
||||||
body: &Ruma<sync_events::v3::Request>,
|
body: &Ruma<sync_events::v3::Request>,
|
||||||
) -> Result<sync_events::v3::Response> {
|
) -> Result<sync_events::v3::Response> {
|
||||||
let (syncing_user, syncing_device) = body.sender();
|
let syncing_user = body.identity.sender_user();
|
||||||
|
let syncing_device = body.identity.sender_device().expect("should have a device");
|
||||||
|
|
||||||
let current_count = services.globals.current_count()?;
|
let current_count = services.globals.current_count()?;
|
||||||
|
|
||||||
|
|||||||
@@ -69,9 +69,8 @@ pub(crate) async fn sync_events_v5_route(
|
|||||||
ClientIp(client_ip): ClientIp,
|
ClientIp(client_ip): ClientIp,
|
||||||
body: Ruma<sync_events::v5::Request>,
|
body: Ruma<sync_events::v5::Request>,
|
||||||
) -> Result<sync_events::v5::Response> {
|
) -> Result<sync_events::v5::Response> {
|
||||||
debug_assert!(DEFAULT_BUMP_TYPES.is_sorted(), "DEFAULT_BUMP_TYPES is not sorted");
|
let sender_user = body.identity.sender_user();
|
||||||
let ref sender_user = body.sender_user().to_owned();
|
let sender_device = body.identity.expect_sender_device()?;
|
||||||
let ref sender_device = body.sender_device().to_owned();
|
|
||||||
|
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
@@ -93,7 +92,7 @@ pub(crate) async fn sync_events_v5_route(
|
|||||||
.and_then(|string| string.parse().ok())
|
.and_then(|string| string.parse().ok())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let snake_key = into_snake_key(sender_user.as_ref(), sender_device.as_str(), conn_id);
|
let snake_key = into_snake_key(sender_user, sender_device.as_str(), conn_id);
|
||||||
|
|
||||||
if globalsince != 0 && !services.sync.snake_connection_cached(&snake_key) {
|
if globalsince != 0 && !services.sync.snake_connection_cached(&snake_key) {
|
||||||
return Err!(Request(UnknownPos(
|
return Err!(Request(UnknownPos(
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub(crate) async fn update_tag_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<create_tag::v3::Request>,
|
body: Ruma<create_tag::v3::Request>,
|
||||||
) -> Result<create_tag::v3::Response> {
|
) -> Result<create_tag::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let mut tags_event = services
|
let mut tags_event = services
|
||||||
.account_data
|
.account_data
|
||||||
@@ -56,7 +56,7 @@ pub(crate) async fn delete_tag_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<delete_tag::v3::Request>,
|
body: Ruma<delete_tag::v3::Request>,
|
||||||
) -> Result<delete_tag::v3::Response> {
|
) -> Result<delete_tag::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let mut tags_event = services
|
let mut tags_event = services
|
||||||
.account_data
|
.account_data
|
||||||
@@ -88,7 +88,7 @@ pub(crate) async fn get_tags_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_tags::v3::Request>,
|
body: Ruma<get_tags::v3::Request>,
|
||||||
) -> Result<get_tags::v3::Response> {
|
) -> Result<get_tags::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
|
|
||||||
let tags_event = services
|
let tags_event = services
|
||||||
.account_data
|
.account_data
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ pub(crate) async fn get_threads_route(
|
|||||||
let threads: Vec<(PduCount, PduEvent)> = services
|
let threads: Vec<(PduCount, PduEvent)> = services
|
||||||
.rooms
|
.rooms
|
||||||
.threads
|
.threads
|
||||||
.threads_until(body.sender_user(), &body.room_id, from, &body.include)
|
.threads_until(body.identity.sender_user(), &body.room_id, from, &body.include)
|
||||||
.await?
|
.await?
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.filter_map(|(count, pdu)| async move {
|
.filter_map(|(count, pdu)| async move {
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_event(body.sender_user(), &body.room_id, &pdu.event_id)
|
.user_can_see_event(body.identity.sender_user(), &body.room_id, &pdu.event_id)
|
||||||
.await
|
.await
|
||||||
.then_some((count, pdu))
|
.then_some((count, pdu))
|
||||||
})
|
})
|
||||||
@@ -49,7 +49,7 @@ pub(crate) async fn get_threads_route(
|
|||||||
if let Err(e) = services
|
if let Err(e) = services
|
||||||
.rooms
|
.rooms
|
||||||
.pdu_metadata
|
.pdu_metadata
|
||||||
.add_bundled_aggregations_to_pdu(body.sender_user(), &mut pdu)
|
.add_bundled_aggregations_to_pdu(body.identity.sender_user(), &mut pdu)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
debug_warn!("Failed to add bundled aggregations to thread: {e}");
|
debug_warn!("Failed to add bundled aggregations to thread: {e}");
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ pub(crate) async fn send_event_to_device_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<send_event_to_device::v3::Request>,
|
body: Ruma<send_event_to_device::v3::Request>,
|
||||||
) -> Result<send_event_to_device::v3::Response> {
|
) -> Result<send_event_to_device::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let sender_device = body.sender_device.as_deref();
|
let sender_device = body.identity.sender_device();
|
||||||
|
|
||||||
// Check if this is a new transaction id
|
// Check if this is a new transaction id
|
||||||
if services
|
if services
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ pub(crate) async fn create_typing_event_route(
|
|||||||
body: Ruma<create_typing_event::v3::Request>,
|
body: Ruma<create_typing_event::v3::Request>,
|
||||||
) -> Result<create_typing_event::v3::Response> {
|
) -> Result<create_typing_event::v3::Response> {
|
||||||
use create_typing_event::v3::Typing;
|
use create_typing_event::v3::Typing;
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.update_device_last_seen(sender_user, body.sender_device.as_deref(), ip)
|
.update_device_last_seen(sender_user, body.identity.sender_device(), ip)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if sender_user != body.user_id && body.appservice_info.is_none() {
|
if sender_user != body.user_id && !body.identity.is_appservice() {
|
||||||
return Err!(Request(Forbidden("You cannot update typing status of other users.")));
|
return Err!(Request(Forbidden("You cannot update typing status of other users.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ pub(crate) async fn get_supported_versions_route(
|
|||||||
/// `/_matrix/federation/v1/version`
|
/// `/_matrix/federation/v1/version`
|
||||||
pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
|
pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
|
||||||
Ok(Json(serde_json::json!({
|
Ok(Json(serde_json::json!({
|
||||||
"name": conduwuit::version::name(),
|
"name": conduwuit::BRANDING,
|
||||||
"version": conduwuit::version::version(),
|
"version": conduwuit::version(),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub(crate) async fn search_users_route(
|
|||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<search_users::v3::Request>,
|
body: Ruma<search_users::v3::Request>,
|
||||||
) -> Result<search_users::v3::Response> {
|
) -> Result<search_users::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.identity.sender_user();
|
||||||
let limit = usize::try_from(body.limit)
|
let limit = usize::try_from(body.limit)
|
||||||
.map_or(LIMIT_DEFAULT, usize::from)
|
.map_or(LIMIT_DEFAULT, usize::from)
|
||||||
.min(LIMIT_MAX);
|
.min(LIMIT_MAX);
|
||||||
|
|||||||
+3
-13
@@ -2,15 +2,13 @@ use std::time::{Duration, SystemTime};
|
|||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
use conduwuit::{Err, Result, utils};
|
use conduwuit::{Err, Result};
|
||||||
use hmac::{Hmac, KeyInit, Mac};
|
use hmac::{Hmac, KeyInit, Mac};
|
||||||
use ruma::{SecondsSinceUnixEpoch, UserId, api::client::voip::get_turn_server_info};
|
use ruma::{SecondsSinceUnixEpoch, api::client::voip::get_turn_server_info};
|
||||||
use sha1::Sha1;
|
use sha1::Sha1;
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
|
|
||||||
const RANDOM_USER_ID_LENGTH: usize = 10;
|
|
||||||
|
|
||||||
type HmacSha1 = Hmac<Sha1>;
|
type HmacSha1 = Hmac<Sha1>;
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/voip/turnServer`
|
/// # `GET /_matrix/client/r0/voip/turnServer`
|
||||||
@@ -35,15 +33,7 @@ pub(crate) async fn turn_server_route(
|
|||||||
)
|
)
|
||||||
.expect("time is valid");
|
.expect("time is valid");
|
||||||
|
|
||||||
let user = body.sender_user.unwrap_or_else(|| {
|
let username: String = format!("{}:{}", expiry.get(), body.identity.sender_user());
|
||||||
UserId::parse_with_server_name(
|
|
||||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
|
||||||
&services.server.name,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
let username: String = format!("{}:{}", expiry.get(), user);
|
|
||||||
|
|
||||||
let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes())
|
let mut mac = HmacSha1::new_from_slice(turn_secret.as_bytes())
|
||||||
.expect("HMAC can take key of any size");
|
.expect("HMAC can take key of any size");
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ use conduwuit::{Err, Result};
|
|||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::discovery::{
|
api::client::discovery::{
|
||||||
discover_homeserver::{self, HomeserverInfo},
|
discover_homeserver::{self, HomeserverInfo},
|
||||||
discover_policy_server,
|
discover_policy_server, discover_support,
|
||||||
discover_support::{self, Contact, ContactRole},
|
|
||||||
},
|
},
|
||||||
assign,
|
assign,
|
||||||
};
|
};
|
||||||
@@ -67,46 +66,7 @@ pub(crate) async fn well_known_support(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(ToString::to_string);
|
.map(ToString::to_string);
|
||||||
|
|
||||||
let email_address = services.config.well_known.support_email.clone();
|
let contacts = services.admin.get_support_contacts().await;
|
||||||
let matrix_id = services.config.well_known.support_mxid.clone();
|
|
||||||
let pgp_key = services.config.well_known.support_pgp_key.clone();
|
|
||||||
|
|
||||||
// TODO: support defining multiple contacts in the config
|
|
||||||
let mut contacts: Vec<Contact> = vec![];
|
|
||||||
|
|
||||||
let role = services
|
|
||||||
.config
|
|
||||||
.well_known
|
|
||||||
.support_role
|
|
||||||
.clone()
|
|
||||||
.unwrap_or(ContactRole::Admin);
|
|
||||||
|
|
||||||
// Add configured contact if at least one contact method is specified
|
|
||||||
let configured_contact = match (matrix_id, email_address) {
|
|
||||||
| (Some(matrix_id), email_address) =>
|
|
||||||
Some(assign!(Contact::with_matrix_id(role, matrix_id), { email_address })),
|
|
||||||
| (None, Some(email_address)) => Some(Contact::with_email_address(role, email_address)),
|
|
||||||
| (None, None) => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(mut configured_contact) = configured_contact {
|
|
||||||
configured_contact.pgp_key = pgp_key;
|
|
||||||
|
|
||||||
contacts.push(configured_contact);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to add admin users as contacts if no contacts are configured
|
|
||||||
if contacts.is_empty() {
|
|
||||||
let admin_users = services.admin.get_admins().await;
|
|
||||||
|
|
||||||
for user_id in &admin_users {
|
|
||||||
if *user_id == services.globals.server_user {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
contacts.push(Contact::with_matrix_id(ContactRole::Admin, user_id.to_owned()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if contacts.is_empty() && support_page.is_none() {
|
if contacts.is_empty() && support_page.is_none() {
|
||||||
// No admin room, no configured contacts, and no support page
|
// No admin room, no configured contacts, and no support page
|
||||||
|
|||||||
+1
-2
@@ -1,4 +1,5 @@
|
|||||||
#![type_length_limit = "16384"] //TODO: reduce me
|
#![type_length_limit = "16384"] //TODO: reduce me
|
||||||
|
#![recursion_limit = "256"] // My Giant Async Function
|
||||||
#![allow(clippy::toplevel_ref_arg)]
|
#![allow(clippy::toplevel_ref_arg)]
|
||||||
|
|
||||||
extern crate conduwuit_core as conduwuit;
|
extern crate conduwuit_core as conduwuit;
|
||||||
@@ -10,8 +11,6 @@ pub mod client;
|
|||||||
pub mod router;
|
pub mod router;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
pub mod admin;
|
|
||||||
|
|
||||||
pub(crate) use self::router::{Ruma, RumaResponse, State};
|
pub(crate) use self::router::{Ruma, RumaResponse, State};
|
||||||
|
|
||||||
conduwuit::mod_ctor! {}
|
conduwuit::mod_ctor! {}
|
||||||
|
|||||||
+22
-8
@@ -10,16 +10,18 @@ use axum::{
|
|||||||
response::{IntoResponse, Redirect},
|
response::{IntoResponse, Redirect},
|
||||||
routing::{any, get, post},
|
routing::{any, get, post},
|
||||||
};
|
};
|
||||||
use conduwuit::{Server, err};
|
use conduwuit::err;
|
||||||
pub(super) use conduwuit_service::state::State;
|
pub(super) use conduwuit_service::state::State;
|
||||||
use http::{Uri, uri};
|
use http::{Uri, uri};
|
||||||
|
|
||||||
use self::handler::RouterExt;
|
use self::handler::RouterExt;
|
||||||
pub(super) use self::{args::Args as Ruma, response::RumaResponse};
|
pub(super) use self::{args::Args as Ruma, auth::ClientIdentity, response::RumaResponse};
|
||||||
use crate::{admin, client, server};
|
#[cfg(feature = "admin_api")]
|
||||||
|
use crate::client::admin::site as admin_api;
|
||||||
|
use crate::{client, server};
|
||||||
|
|
||||||
pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
pub fn build(router: Router<State>, state: State) -> Router<State> {
|
||||||
let config = &server.config;
|
let config = &state.server.config;
|
||||||
let mut router = router
|
let mut router = router
|
||||||
.ruma_route(&client::appservice_ping)
|
.ruma_route(&client::appservice_ping)
|
||||||
.ruma_route(&client::get_supported_versions_route)
|
.ruma_route(&client::get_supported_versions_route)
|
||||||
@@ -181,15 +183,17 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
|||||||
.ruma_route(&client::get_room_summary)
|
.ruma_route(&client::get_room_summary)
|
||||||
.ruma_route(&client::get_suspended_status)
|
.ruma_route(&client::get_suspended_status)
|
||||||
.ruma_route(&client::put_suspended_status)
|
.ruma_route(&client::put_suspended_status)
|
||||||
|
.ruma_route(&client::get_locked_status)
|
||||||
|
.ruma_route(&client::put_locked_status)
|
||||||
.ruma_route(&client::well_known_support)
|
.ruma_route(&client::well_known_support)
|
||||||
.ruma_route(&client::well_known_client)
|
.ruma_route(&client::well_known_client)
|
||||||
.ruma_route(&client::well_known_policy_server)
|
.ruma_route(&client::well_known_policy_server)
|
||||||
.ruma_route(&client::get_rtc_transports)
|
.ruma_route(&client::get_rtc_transports)
|
||||||
.ruma_route(&client::room_initial_sync_route)
|
.ruma_route(&client::room_initial_sync_route)
|
||||||
|
.ruma_route(&client::get_authorization_server_metadata_route)
|
||||||
|
.merge(client::oauth::router(state))
|
||||||
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
|
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
|
||||||
.route("/_continuwuity/server_version", get(client::conduwuit_server_version))
|
.route("/_continuwuity/server_version", get(client::conduwuit_server_version));
|
||||||
.ruma_route(&admin::rooms::ban::ban_room)
|
|
||||||
.ruma_route(&admin::rooms::list::list_rooms);
|
|
||||||
|
|
||||||
if config.allow_federation {
|
if config.allow_federation {
|
||||||
router = router
|
router = router
|
||||||
@@ -275,6 +279,16 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
|||||||
.route("/_matrix/media/r0/preview_url", any(redirect_legacy_preview));
|
.route("/_matrix/media/r0/preview_url", any(redirect_legacy_preview));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "admin_api")]
|
||||||
|
{
|
||||||
|
router = router
|
||||||
|
.ruma_route(&admin_api::users::list_users_route)
|
||||||
|
.ruma_route(&admin_api::users::create_user_route)
|
||||||
|
.ruma_route(&admin_api::rooms::ban_room)
|
||||||
|
.ruma_route(&admin_api::rooms::legacy_list_rooms_route)
|
||||||
|
.ruma_route(&admin_api::rooms::list_rooms_route);
|
||||||
|
};
|
||||||
|
|
||||||
router
|
router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-68
@@ -6,17 +6,14 @@ use axum::{
|
|||||||
extract::{FromRequest, Path, Query},
|
extract::{FromRequest, Path, Query},
|
||||||
};
|
};
|
||||||
use conduwuit::{Error, Result, err};
|
use conduwuit::{Error, Result, err};
|
||||||
use ruma::{
|
use ruma::{CanonicalJsonObject, api::IncomingRequest};
|
||||||
CanonicalJsonObject, DeviceId, OwnedDeviceId, OwnedServerName, OwnedUserId, ServerName,
|
|
||||||
UserId, api::IncomingRequest,
|
|
||||||
};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{State, router::auth::CheckAuth, service::appservice::RegistrationInfo};
|
use crate::{State, router::auth::CheckAuth};
|
||||||
|
|
||||||
/// Query parameters needed to authenticate requests
|
/// Query parameters needed to authenticate requests
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub(super) struct AuthQueryParams {
|
pub(crate) struct AuthQueryParams {
|
||||||
pub(super) user_id: Option<String>,
|
pub(super) user_id: Option<String>,
|
||||||
/// Device ID for appservice device masquerading (MSC3202/MSC4190).
|
/// Device ID for appservice device masquerading (MSC3202/MSC4190).
|
||||||
/// Can be provided as `device_id` or `org.matrix.msc3202.device_id`.
|
/// Can be provided as `device_id` or `org.matrix.msc3202.device_id`.
|
||||||
@@ -25,67 +22,22 @@ pub(super) struct AuthQueryParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extractor for Ruma request structs
|
/// Extractor for Ruma request structs
|
||||||
pub(crate) struct Args<T> {
|
pub(crate) struct Args<R: IncomingRequest<Authentication: CheckAuth> + Send + Sync + 'static> {
|
||||||
/// Request struct body
|
/// Request struct body
|
||||||
pub(crate) body: T,
|
pub(crate) body: R,
|
||||||
|
|
||||||
/// Federation server authentication: X-Matrix origin
|
/// Parsed JSON body. None when body is not JSON.
|
||||||
/// None when not a federation server.
|
|
||||||
pub(crate) origin: Option<OwnedServerName>,
|
|
||||||
|
|
||||||
/// Local user authentication: user_id.
|
|
||||||
/// None when not an authenticated local user.
|
|
||||||
pub(crate) sender_user: Option<OwnedUserId>,
|
|
||||||
|
|
||||||
/// Local user authentication: device_id.
|
|
||||||
/// None when not an authenticated local user or no device.
|
|
||||||
pub(crate) sender_device: Option<OwnedDeviceId>,
|
|
||||||
|
|
||||||
/// Appservice authentication; registration info.
|
|
||||||
/// None when not an appservice.
|
|
||||||
pub(crate) appservice_info: Option<RegistrationInfo>,
|
|
||||||
|
|
||||||
/// Parsed JSON content.
|
|
||||||
/// None when body is not a valid string
|
|
||||||
pub(crate) json_body: Option<CanonicalJsonObject>,
|
pub(crate) json_body: Option<CanonicalJsonObject>,
|
||||||
|
|
||||||
|
/// Identity of the requesting entity
|
||||||
|
pub(crate) identity: <R::Authentication as CheckAuth>::Identity,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Args<T>
|
impl<R> Deref for Args<R>
|
||||||
where
|
where
|
||||||
T: IncomingRequest + Send + Sync + 'static,
|
R: IncomingRequest<Authentication: CheckAuth> + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
#[inline]
|
type Target = R;
|
||||||
pub(crate) fn sender(&self) -> (&UserId, &DeviceId) {
|
|
||||||
(self.sender_user(), self.sender_device())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn sender_user(&self) -> &UserId {
|
|
||||||
self.sender_user
|
|
||||||
.as_deref()
|
|
||||||
.expect("user must be authenticated for this handler")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn sender_device(&self) -> &DeviceId {
|
|
||||||
self.sender_device
|
|
||||||
.as_deref()
|
|
||||||
.expect("user must be authenticated and device identified")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn origin(&self) -> &ServerName {
|
|
||||||
self.origin
|
|
||||||
.as_deref()
|
|
||||||
.expect("server must be authenticated for this handler")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deref for Args<T>
|
|
||||||
where
|
|
||||||
T: IncomingRequest + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.body }
|
fn deref(&self) -> &Self::Target { &self.body }
|
||||||
}
|
}
|
||||||
@@ -145,13 +97,6 @@ where
|
|||||||
let body = R::try_from_http_request(request, &path)
|
let body = R::try_from_http_request(request, &path)
|
||||||
.map_err(|e| err!(Request(BadJson(debug_warn!("{e}")))))?;
|
.map_err(|e| err!(Request(BadJson(debug_warn!("{e}")))))?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self { body, json_body, identity: auth })
|
||||||
body,
|
|
||||||
origin: auth.origin,
|
|
||||||
sender_user: auth.sender_user,
|
|
||||||
sender_device: auth.sender_device,
|
|
||||||
appservice_info: auth.appservice_info,
|
|
||||||
json_body,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+157
-98
@@ -1,8 +1,9 @@
|
|||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
|
|
||||||
use conduwuit::{Err, Result, err};
|
use conduwuit::{Err, Error, Result, err};
|
||||||
|
use http::StatusCode;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
DeviceId, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
||||||
api::{
|
api::{
|
||||||
IncomingRequest,
|
IncomingRequest,
|
||||||
auth_scheme::{
|
auth_scheme::{
|
||||||
@@ -10,30 +11,70 @@ use ruma::{
|
|||||||
AuthScheme, NoAccessToken, NoAuthentication,
|
AuthScheme, NoAccessToken, NoAuthentication,
|
||||||
},
|
},
|
||||||
client,
|
client,
|
||||||
|
error::{ErrorKind, UnknownTokenErrorData},
|
||||||
federation::authentication::ServerSignatures,
|
federation::authentication::ServerSignatures,
|
||||||
},
|
},
|
||||||
|
assign,
|
||||||
};
|
};
|
||||||
use service::{
|
use service::{
|
||||||
Services,
|
Services,
|
||||||
server_keys::{PubKeyMap, PubKeys},
|
server_keys::{PubKeyMap, PubKeys},
|
||||||
|
users::AccessTokenStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{router::args::AuthQueryParams, service::appservice::RegistrationInfo};
|
use crate::{router::args::AuthQueryParams, service::appservice::RegistrationInfo};
|
||||||
|
|
||||||
#[derive(Default)]
|
pub(crate) enum ClientIdentity {
|
||||||
pub(super) struct Auth {
|
User {
|
||||||
pub(super) origin: Option<OwnedServerName>,
|
sender_user: OwnedUserId,
|
||||||
pub(super) sender_user: Option<OwnedUserId>,
|
sender_device: OwnedDeviceId,
|
||||||
pub(super) sender_device: Option<OwnedDeviceId>,
|
},
|
||||||
pub(super) appservice_info: Option<RegistrationInfo>,
|
Appservice {
|
||||||
|
sender_user: OwnedUserId,
|
||||||
|
sender_device: Option<OwnedDeviceId>,
|
||||||
|
appservice_info: Box<RegistrationInfo>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) trait CheckAuth: AuthScheme {
|
impl ClientIdentity {
|
||||||
|
pub(crate) fn sender_user(&self) -> &UserId {
|
||||||
|
match self {
|
||||||
|
| Self::User { sender_user, .. } | Self::Appservice { sender_user, .. } =>
|
||||||
|
sender_user,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sender_device(&self) -> Option<&DeviceId> {
|
||||||
|
match self {
|
||||||
|
| Self::User { sender_device, .. } => Some(sender_device),
|
||||||
|
| Self::Appservice { sender_device, .. } => sender_device.as_deref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn expect_sender_device(&self) -> Result<&DeviceId> {
|
||||||
|
self.sender_device().ok_or_else(|| {
|
||||||
|
err!(Request(Forbidden("Appservices must masquerade to use this endpoint.")))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn appservice_info(&self) -> Option<&RegistrationInfo> {
|
||||||
|
match self {
|
||||||
|
| Self::User { .. } => None,
|
||||||
|
| Self::Appservice { appservice_info, .. } => Some(appservice_info),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_appservice(&self) -> bool { matches!(self, Self::Appservice { .. }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait CheckAuth: AuthScheme {
|
||||||
|
type Identity: Send;
|
||||||
|
|
||||||
fn authenticate<R: IncomingRequest + Any, B: AsRef<[u8]> + Sync>(
|
fn authenticate<R: IncomingRequest + Any, B: AsRef<[u8]> + Sync>(
|
||||||
services: &Services,
|
services: &Services,
|
||||||
incoming_request: &hyper::Request<B>,
|
incoming_request: &hyper::Request<B>,
|
||||||
query: AuthQueryParams,
|
query: AuthQueryParams,
|
||||||
) -> impl Future<Output = Result<Auth>> + Send {
|
) -> impl Future<Output = Result<Self::Identity>> + Send {
|
||||||
async move {
|
async move {
|
||||||
let route = TypeId::of::<R>();
|
let route = TypeId::of::<R>();
|
||||||
|
|
||||||
@@ -54,17 +95,19 @@ pub(super) trait CheckAuth: AuthScheme {
|
|||||||
request: &hyper::Request<B>,
|
request: &hyper::Request<B>,
|
||||||
query: AuthQueryParams,
|
query: AuthQueryParams,
|
||||||
route: TypeId,
|
route: TypeId,
|
||||||
) -> impl Future<Output = Result<Auth>> + Send;
|
) -> impl Future<Output = Result<Self::Identity>> + Send;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CheckAuth for ServerSignatures {
|
impl CheckAuth for ServerSignatures {
|
||||||
|
type Identity = OwnedServerName;
|
||||||
|
|
||||||
async fn verify<B: AsRef<[u8]> + Sync>(
|
async fn verify<B: AsRef<[u8]> + Sync>(
|
||||||
services: &Services,
|
services: &Services,
|
||||||
output: Self::Output,
|
output: Self::Output,
|
||||||
request: &hyper::Request<B>,
|
request: &hyper::Request<B>,
|
||||||
_query: AuthQueryParams,
|
_query: AuthQueryParams,
|
||||||
_route: TypeId,
|
_route: TypeId,
|
||||||
) -> Result<Auth> {
|
) -> Result<Self::Identity> {
|
||||||
let destination = services.globals.server_name();
|
let destination = services.globals.server_name();
|
||||||
if output
|
if output
|
||||||
.destination
|
.destination
|
||||||
@@ -96,10 +139,7 @@ impl CheckAuth for ServerSignatures {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Auth {
|
Ok(output.origin)
|
||||||
origin: Some(output.origin.clone()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
| Err(err) =>
|
| Err(err) =>
|
||||||
Err!(Request(Unauthorized(warn!("Failed to verify X-Matrix header: {err}")))),
|
Err!(Request(Unauthorized(warn!("Failed to verify X-Matrix header: {err}")))),
|
||||||
@@ -108,162 +148,181 @@ impl CheckAuth for ServerSignatures {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CheckAuth for AccessToken {
|
impl CheckAuth for AccessToken {
|
||||||
|
type Identity = ClientIdentity;
|
||||||
|
|
||||||
async fn verify<B: AsRef<[u8]> + Sync>(
|
async fn verify<B: AsRef<[u8]> + Sync>(
|
||||||
services: &Services,
|
services: &Services,
|
||||||
output: Self::Output,
|
output: Self::Output,
|
||||||
_request: &hyper::Request<B>,
|
_request: &hyper::Request<B>,
|
||||||
query: AuthQueryParams,
|
query: AuthQueryParams,
|
||||||
route: TypeId,
|
route: TypeId,
|
||||||
) -> Result<Auth> {
|
) -> Result<Self::Identity> {
|
||||||
// Check for appservice tokens first
|
if let Some((sender_user, sender_device, status)) =
|
||||||
|
services.users.find_from_token(&output).await
|
||||||
|
{
|
||||||
|
// If the token is expired we return a soft logout
|
||||||
|
if matches!(status, AccessTokenStatus::Expired) {
|
||||||
|
return Err(Error::Request(
|
||||||
|
ErrorKind::UnknownToken(
|
||||||
|
assign!(UnknownTokenErrorData::new(), { soft_logout: true }),
|
||||||
|
),
|
||||||
|
"This token has expired".into(),
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let (sender_user, sender_device, appservice_info) = {
|
// Locked users can only use /logout and /logout/all
|
||||||
if let Ok((sender_user, sender_device)) =
|
if services
|
||||||
services.users.find_from_token(&output).await
|
.users
|
||||||
|
.is_locked(&sender_user)
|
||||||
|
.await
|
||||||
|
.is_ok_and(std::convert::identity)
|
||||||
{
|
{
|
||||||
// Locked users can only use /logout and /logout/all
|
if !(route == TypeId::of::<client::session::logout::v3::Request>()
|
||||||
if services
|
|| route == TypeId::of::<client::session::logout_all::v3::Request>())
|
||||||
.users
|
|
||||||
.is_locked(&sender_user)
|
|
||||||
.await
|
|
||||||
.is_ok_and(std::convert::identity)
|
|
||||||
{
|
{
|
||||||
if !(route == TypeId::of::<client::session::logout::v3::Request>()
|
return Err!(Request(UserLocked("Your account is locked.")));
|
||||||
|| route == TypeId::of::<client::session::logout_all::v3::Request>())
|
|
||||||
{
|
|
||||||
return Err!(Request(Unauthorized("Your account is locked.")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(Some(sender_user), Some(sender_device), None)
|
Ok(ClientIdentity::User { sender_user, sender_device })
|
||||||
} else if let Ok(appservice_info) = services.appservice.find_from_token(&output).await
|
} else if let Ok(appservice_info) = services.appservice.find_from_token(&output).await {
|
||||||
{
|
let Ok(sender_user) = query.user_id.clone().map_or_else(
|
||||||
let Ok(sender_user) = query.user_id.clone().map_or_else(
|
|| {
|
||||||
|| {
|
UserId::parse_with_server_name(
|
||||||
UserId::parse_with_server_name(
|
appservice_info.registration.sender_localpart.as_str(),
|
||||||
appservice_info.registration.sender_localpart.as_str(),
|
services.globals.server_name(),
|
||||||
services.globals.server_name(),
|
)
|
||||||
)
|
},
|
||||||
},
|
UserId::parse,
|
||||||
UserId::parse,
|
) else {
|
||||||
) else {
|
return Err!(Request(InvalidUsername("Username is invalid.")));
|
||||||
return Err!(Request(InvalidUsername("Username is invalid.")));
|
};
|
||||||
|
|
||||||
|
if !appservice_info.is_user_match(&sender_user) {
|
||||||
|
return Err!(Request(Exclusive("User is not in namespace.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// MSC3202/MSC4190: Handle device_id masquerading for appservices.
|
||||||
|
// The device_id can be provided via `device_id` or
|
||||||
|
// `org.matrix.msc3202.device_id` query parameter.
|
||||||
|
let sender_device =
|
||||||
|
if let Some(device_id) = query.device_id.as_deref().map(Into::into) {
|
||||||
|
// Verify the device exists for this user
|
||||||
|
if services
|
||||||
|
.users
|
||||||
|
.get_device_metadata(&sender_user, device_id)
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return Err!(Request(Forbidden(
|
||||||
|
"Device does not exist for user or appservice cannot masquerade as \
|
||||||
|
this device."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(device_id.to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if !appservice_info.is_user_match(&sender_user) {
|
Ok(ClientIdentity::Appservice {
|
||||||
return Err!(Request(Exclusive("User is not in namespace.")));
|
sender_user,
|
||||||
}
|
sender_device,
|
||||||
|
appservice_info: Box::new(appservice_info),
|
||||||
// MSC3202/MSC4190: Handle device_id masquerading for appservices.
|
})
|
||||||
// The device_id can be provided via `device_id` or
|
} else {
|
||||||
// `org.matrix.msc3202.device_id` query parameter.
|
Err(Error::Request(
|
||||||
let sender_device =
|
ErrorKind::UnknownToken(UnknownTokenErrorData::new()),
|
||||||
if let Some(device_id) = query.device_id.as_deref().map(Into::into) {
|
"Invalid token".into(),
|
||||||
// Verify the device exists for this user
|
StatusCode::UNAUTHORIZED,
|
||||||
if services
|
))
|
||||||
.users
|
}
|
||||||
.get_device_metadata(&sender_user, device_id)
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return Err!(Request(Forbidden(
|
|
||||||
"Device does not exist for user or appservice cannot masquerade \
|
|
||||||
as this device."
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(device_id.to_owned())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
(Some(sender_user), sender_device, Some(appservice_info))
|
|
||||||
} else {
|
|
||||||
return Err!(Request(Unauthorized("Invalid access token.")));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Auth {
|
|
||||||
sender_user,
|
|
||||||
sender_device,
|
|
||||||
appservice_info,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CheckAuth for AccessTokenOptional {
|
impl CheckAuth for AccessTokenOptional {
|
||||||
|
type Identity = Option<ClientIdentity>;
|
||||||
|
|
||||||
async fn verify<B: AsRef<[u8]> + Sync>(
|
async fn verify<B: AsRef<[u8]> + Sync>(
|
||||||
services: &Services,
|
services: &Services,
|
||||||
output: Self::Output,
|
output: Self::Output,
|
||||||
request: &hyper::Request<B>,
|
request: &hyper::Request<B>,
|
||||||
query: AuthQueryParams,
|
query: AuthQueryParams,
|
||||||
route: TypeId,
|
route: TypeId,
|
||||||
) -> Result<Auth> {
|
) -> Result<Self::Identity> {
|
||||||
match output {
|
match output {
|
||||||
| Some(token) =>
|
| Some(token) =>
|
||||||
<AccessToken as CheckAuth>::verify(services, token, request, query, route).await,
|
<AccessToken as CheckAuth>::verify(services, token, request, query, route)
|
||||||
| None => Ok(Auth::default()),
|
.await
|
||||||
|
.map(Some),
|
||||||
|
| None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CheckAuth for AppserviceToken {
|
impl CheckAuth for AppserviceToken {
|
||||||
|
type Identity = RegistrationInfo;
|
||||||
|
|
||||||
async fn verify<B: AsRef<[u8]> + Sync>(
|
async fn verify<B: AsRef<[u8]> + Sync>(
|
||||||
services: &Services,
|
services: &Services,
|
||||||
output: Self::Output,
|
output: Self::Output,
|
||||||
_request: &hyper::Request<B>,
|
_request: &hyper::Request<B>,
|
||||||
_query: AuthQueryParams,
|
_query: AuthQueryParams,
|
||||||
_route: TypeId,
|
_route: TypeId,
|
||||||
) -> Result<Auth> {
|
) -> Result<Self::Identity> {
|
||||||
let Ok(appservice_info) = services.appservice.find_from_token(&output).await else {
|
let Ok(appservice_info) = services.appservice.find_from_token(&output).await else {
|
||||||
return Err!(Request(Unauthorized("Invalid appservice token.")));
|
return Err!(Request(Unauthorized("Invalid appservice token.")));
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Auth {
|
Ok(appservice_info)
|
||||||
appservice_info: Some(appservice_info),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CheckAuth for AppserviceTokenOptional {
|
impl CheckAuth for AppserviceTokenOptional {
|
||||||
|
type Identity = Option<RegistrationInfo>;
|
||||||
|
|
||||||
async fn verify<B: AsRef<[u8]> + Sync>(
|
async fn verify<B: AsRef<[u8]> + Sync>(
|
||||||
services: &Services,
|
services: &Services,
|
||||||
output: Self::Output,
|
output: Self::Output,
|
||||||
request: &hyper::Request<B>,
|
request: &hyper::Request<B>,
|
||||||
query: AuthQueryParams,
|
query: AuthQueryParams,
|
||||||
route: TypeId,
|
route: TypeId,
|
||||||
) -> Result<Auth> {
|
) -> Result<Self::Identity> {
|
||||||
match output {
|
match output {
|
||||||
| Some(token) =>
|
| Some(token) =>
|
||||||
<AppserviceToken as CheckAuth>::verify(services, token, request, query, route)
|
<AppserviceToken as CheckAuth>::verify(services, token, request, query, route)
|
||||||
.await,
|
.await
|
||||||
| None => Ok(Auth::default()),
|
.map(Some),
|
||||||
|
| None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CheckAuth for NoAuthentication {
|
impl CheckAuth for NoAuthentication {
|
||||||
|
type Identity = ();
|
||||||
|
|
||||||
async fn verify<B: AsRef<[u8]> + Sync>(
|
async fn verify<B: AsRef<[u8]> + Sync>(
|
||||||
_services: &Services,
|
_services: &Services,
|
||||||
_output: Self::Output,
|
_output: Self::Output,
|
||||||
_request: &hyper::Request<B>,
|
_request: &hyper::Request<B>,
|
||||||
_query: AuthQueryParams,
|
_query: AuthQueryParams,
|
||||||
_route: TypeId,
|
_route: TypeId,
|
||||||
) -> Result<Auth> {
|
) -> Result<Self::Identity> {
|
||||||
Ok(Auth::default())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CheckAuth for NoAccessToken {
|
impl CheckAuth for NoAccessToken {
|
||||||
|
type Identity = Option<ClientIdentity>;
|
||||||
|
|
||||||
async fn verify<B: AsRef<[u8]> + Sync>(
|
async fn verify<B: AsRef<[u8]> + Sync>(
|
||||||
services: &Services,
|
services: &Services,
|
||||||
_output: Self::Output,
|
_output: Self::Output,
|
||||||
request: &hyper::Request<B>,
|
request: &hyper::Request<B>,
|
||||||
query: AuthQueryParams,
|
query: AuthQueryParams,
|
||||||
route: TypeId,
|
route: TypeId,
|
||||||
) -> Result<Auth> {
|
) -> Result<Self::Identity> {
|
||||||
// We handle these the same as AccessTokenOptional
|
// We handle these the same as AccessTokenOptional
|
||||||
let token = AccessTokenOptional::extract_authentication(request).map_err(|err| {
|
let token = AccessTokenOptional::extract_authentication(request).map_err(|err| {
|
||||||
err!(Request(Unauthorized(warn!("Failed to extract authorization: {}", err))))
|
err!(Request(Unauthorized(warn!("Failed to extract authorization: {}", err))))
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ pub(crate) async fn get_backfill_route(
|
|||||||
) -> Result<get_backfill::v1::Response> {
|
) -> Result<get_backfill::v1::Response> {
|
||||||
AccessCheck {
|
AccessCheck {
|
||||||
services: &services,
|
services: &services,
|
||||||
origin: body.origin(),
|
origin: &body.identity,
|
||||||
room_id: &body.room_id,
|
room_id: &body.room_id,
|
||||||
event_id: None,
|
event_id: None,
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ pub(crate) async fn get_backfill_route(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
origin = body.origin().as_str(),
|
origin = body.identity.as_str(),
|
||||||
"Refusing to serve backfill for room we aren't participating in"
|
"Refusing to serve backfill for room we aren't participating in"
|
||||||
);
|
);
|
||||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||||
@@ -76,7 +76,7 @@ pub(crate) async fn get_backfill_route(
|
|||||||
Ok(services
|
Ok(services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.server_can_see_event(body.origin(), &pdu.room_id_or_hash(), &pdu.event_id)
|
.server_can_see_event(&body.identity, &pdu.room_id_or_hash(), &pdu.event_id)
|
||||||
.await
|
.await
|
||||||
.then_some(pdu))
|
.then_some(pdu))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub(crate) async fn get_event_route(
|
|||||||
|
|
||||||
AccessCheck {
|
AccessCheck {
|
||||||
services: &services,
|
services: &services,
|
||||||
origin: body.origin(),
|
origin: &body.identity,
|
||||||
room_id,
|
room_id,
|
||||||
event_id: Some(&body.event_id),
|
event_id: Some(&body.event_id),
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ pub(crate) async fn get_event_route(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
origin = body.origin().as_str(),
|
origin = body.identity.as_str(),
|
||||||
"Refusing to serve state for room we aren't participating in"
|
"Refusing to serve state for room we aren't participating in"
|
||||||
);
|
);
|
||||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub(crate) async fn get_event_authorization_route(
|
|||||||
) -> Result<get_event_authorization::v1::Response> {
|
) -> Result<get_event_authorization::v1::Response> {
|
||||||
AccessCheck {
|
AccessCheck {
|
||||||
services: &services,
|
services: &services,
|
||||||
origin: body.origin(),
|
origin: &body.identity,
|
||||||
room_id: &body.room_id,
|
room_id: &body.room_id,
|
||||||
event_id: None,
|
event_id: None,
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ pub(crate) async fn get_event_authorization_route(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
origin = body.origin().as_str(),
|
origin = body.identity.as_str(),
|
||||||
"Refusing to serve state for room we aren't participating in"
|
"Refusing to serve state for room we aren't participating in"
|
||||||
);
|
);
|
||||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub(crate) async fn get_missing_events_route(
|
|||||||
) -> Result<get_missing_events::v1::Response> {
|
) -> Result<get_missing_events::v1::Response> {
|
||||||
AccessCheck {
|
AccessCheck {
|
||||||
services: &services,
|
services: &services,
|
||||||
origin: body.origin(),
|
origin: &body.identity,
|
||||||
room_id: &body.room_id,
|
room_id: &body.room_id,
|
||||||
event_id: None,
|
event_id: None,
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ pub(crate) async fn get_missing_events_route(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
origin = body.origin().as_str(),
|
origin = body.identity.as_str(),
|
||||||
"Refusing to serve state for room we aren't participating in"
|
"Refusing to serve state for room we aren't participating in"
|
||||||
);
|
);
|
||||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||||
@@ -91,10 +91,10 @@ pub(crate) async fn get_missing_events_route(
|
|||||||
if !services
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.server_can_see_event(body.origin(), &body.room_id, pdu.event_id())
|
.server_can_see_event(&body.identity, &body.room_id, pdu.event_id())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
debug!(%next_event_id, origin = %body.origin(), "redacting event origin cannot see");
|
debug!(%next_event_id, origin = %body.identity, "redacting event origin cannot see");
|
||||||
pdu.redact(&room_version, json!({}))?;
|
pdu.redact(&room_version, json!({}))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub(crate) async fn get_hierarchy_route(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
origin = body.origin().as_str(),
|
origin = body.identity.as_str(),
|
||||||
"Refusing to serve state for room we aren't participating in"
|
"Refusing to serve state for room we aren't participating in"
|
||||||
);
|
);
|
||||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||||
@@ -29,7 +29,7 @@ pub(crate) async fn get_hierarchy_route(
|
|||||||
let response = services
|
let response = services
|
||||||
.rooms
|
.rooms
|
||||||
.summary
|
.summary
|
||||||
.get_local_room_summary_for_server(body.origin(), &body.room_id, body.suggested_only)
|
.get_local_room_summary_for_server(&body.identity, &body.room_id, body.suggested_only)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Accessibility::Accessible(response) = response {
|
if let Accessibility::Accessible(response) = response {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ pub(crate) async fn create_invite_route(
|
|||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.event_handler
|
.event_handler
|
||||||
.acl_check(body.origin(), &body.room_id)
|
.acl_check(&body.identity, &body.room_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !services.server.supported_room_version(&body.room_version) {
|
if !services.server.supported_room_version(&body.room_version) {
|
||||||
@@ -54,12 +54,11 @@ pub(crate) async fn create_invite_route(
|
|||||||
|
|
||||||
if services
|
if services
|
||||||
.moderation
|
.moderation
|
||||||
.is_remote_server_forbidden(body.origin())
|
.is_remote_server_forbidden(&body.identity)
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Received federated/remote invite from banned server {} for room ID {}. Rejecting.",
|
"Received federated/remote invite from banned server {} for room ID {}. Rejecting.",
|
||||||
body.origin(),
|
body.identity, body.room_id
|
||||||
body.room_id
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
||||||
@@ -105,7 +104,7 @@ pub(crate) async fn create_invite_route(
|
|||||||
.and_then(Result::ok)
|
.and_then(Result::ok)
|
||||||
.ok_or_else(|| err!(Request(InvalidParam("Invalid sender property"))))?;
|
.ok_or_else(|| err!(Request(InvalidParam("Invalid sender property"))))?;
|
||||||
|
|
||||||
if sender_user.server_name() != body.origin() {
|
if sender_user.server_name() != body.identity {
|
||||||
return Err!(Request(Forbidden("Sender's server does not match the origin server.",)));
|
return Err!(Request(Forbidden("Sender's server does not match the origin server.",)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ use crate::Ruma;
|
|||||||
/// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}`
|
/// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}`
|
||||||
///
|
///
|
||||||
/// Creates a join template.
|
/// Creates a join template.
|
||||||
#[tracing::instrument(skip_all, fields(room_id = %body.room_id, user_id = %body.user_id, origin = %body.origin()), level = "info")]
|
#[tracing::instrument(skip_all, fields(room_id = %body.room_id, user_id = %body.user_id, origin = %body.identity), level = "info")]
|
||||||
pub(crate) async fn create_join_event_template_route(
|
pub(crate) async fn create_join_event_template_route(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<prepare_join_event::v1::Request>,
|
body: Ruma<prepare_join_event::v1::Request>,
|
||||||
@@ -45,14 +45,14 @@ pub(crate) async fn create_join_event_template_route(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
origin = body.origin().as_str(),
|
origin = body.identity.as_str(),
|
||||||
room_id = %body.room_id,
|
room_id = %body.room_id,
|
||||||
"Refusing to serve make_join for room we aren't participating in"
|
"Refusing to serve make_join for room we aren't participating in"
|
||||||
);
|
);
|
||||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.user_id.server_name() != body.origin() {
|
if body.user_id.server_name() != body.identity {
|
||||||
return Err!(Request(BadJson("Not allowed to join on behalf of another server/user.")));
|
return Err!(Request(BadJson("Not allowed to join on behalf of another server/user.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,19 +60,17 @@ pub(crate) async fn create_join_event_template_route(
|
|||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.event_handler
|
.event_handler
|
||||||
.acl_check(body.origin(), &body.room_id)
|
.acl_check(&body.identity, &body.room_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if services
|
if services
|
||||||
.moderation
|
.moderation
|
||||||
.is_remote_server_forbidden(body.origin())
|
.is_remote_server_forbidden(&body.identity)
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Server {} for remote user {} tried joining room ID {} which has a server name that \
|
"Server {} for remote user {} tried joining room ID {} which has a server name that \
|
||||||
is globally forbidden. Rejecting.",
|
is globally forbidden. Rejecting.",
|
||||||
body.origin(),
|
body.identity, &body.user_id, &body.room_id,
|
||||||
&body.user_id,
|
|
||||||
&body.room_id,
|
|
||||||
);
|
);
|
||||||
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,14 +28,14 @@ pub(crate) async fn create_knock_event_template_route(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
origin = body.origin().as_str(),
|
origin = body.identity.as_str(),
|
||||||
room_id = %body.room_id,
|
room_id = %body.room_id,
|
||||||
"Refusing to serve make_knock for room we aren't participating in"
|
"Refusing to serve make_knock for room we aren't participating in"
|
||||||
);
|
);
|
||||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.user_id.server_name() != body.origin() {
|
if body.user_id.server_name() != body.identity {
|
||||||
return Err!(Request(BadJson("Not allowed to knock on behalf of another server/user.")));
|
return Err!(Request(BadJson("Not allowed to knock on behalf of another server/user.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,19 +43,17 @@ pub(crate) async fn create_knock_event_template_route(
|
|||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.event_handler
|
.event_handler
|
||||||
.acl_check(body.origin(), &body.room_id)
|
.acl_check(&body.identity, &body.room_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if services
|
if services
|
||||||
.moderation
|
.moderation
|
||||||
.is_remote_server_forbidden(body.origin())
|
.is_remote_server_forbidden(&body.identity)
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Server {} for remote user {} tried knocking room ID {} which has a server name \
|
"Server {} for remote user {} tried knocking room ID {} which has a server name \
|
||||||
that is globally forbidden. Rejecting.",
|
that is globally forbidden. Rejecting.",
|
||||||
body.origin(),
|
body.identity, &body.user_id, &body.room_id,
|
||||||
&body.user_id,
|
|
||||||
&body.room_id,
|
|
||||||
);
|
);
|
||||||
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ pub(crate) async fn create_leave_event_template_route(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
origin = body.origin().as_str(),
|
origin = body.identity.as_str(),
|
||||||
"Refusing to serve make_leave for room we aren't participating in"
|
"Refusing to serve make_leave for room we aren't participating in"
|
||||||
);
|
);
|
||||||
return Err!(Request(NotFound("This server is not participating in that room.")));
|
return Err!(Request(NotFound("This server is not participating in that room.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if body.user_id.server_name() != body.origin() {
|
if body.user_id.server_name() != body.identity {
|
||||||
return Err!(Request(Forbidden(
|
return Err!(Request(Forbidden(
|
||||||
"Not allowed to leave on behalf of another server/user."
|
"Not allowed to leave on behalf of another server/user."
|
||||||
)));
|
)));
|
||||||
@@ -42,7 +42,7 @@ pub(crate) async fn create_leave_event_template_route(
|
|||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.event_handler
|
.event_handler
|
||||||
.acl_check(body.origin(), &body.room_id)
|
.acl_check(&body.identity, &body.room_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
|
let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ pub(crate) async fn send_transaction_message_route(
|
|||||||
ClientIp(client): ClientIp,
|
ClientIp(client): ClientIp,
|
||||||
body: Ruma<send_transaction_message::v1::Request>,
|
body: Ruma<send_transaction_message::v1::Request>,
|
||||||
) -> Result<send_transaction_message::v1::Response> {
|
) -> Result<send_transaction_message::v1::Response> {
|
||||||
if body.origin() != body.body.origin {
|
if body.identity != body.body.origin {
|
||||||
return Err!(Request(Forbidden(
|
return Err!(Request(Forbidden(
|
||||||
"Not allowed to send transactions on behalf of other servers"
|
"Not allowed to send transactions on behalf of other servers"
|
||||||
)));
|
)));
|
||||||
@@ -80,7 +80,7 @@ pub(crate) async fn send_transaction_message_route(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let txn_key = (body.origin().to_owned(), body.transaction_id.clone());
|
let txn_key = (body.identity.clone(), body.transaction_id.clone());
|
||||||
|
|
||||||
// Atomically check cache, join active, or start new transaction
|
// Atomically check cache, join active, or start new transaction
|
||||||
match services
|
match services
|
||||||
@@ -136,7 +136,7 @@ async fn wait_for_result(
|
|||||||
skip_all,
|
skip_all,
|
||||||
fields(
|
fields(
|
||||||
id = ?body.transaction_id.as_str(),
|
id = ?body.transaction_id.as_str(),
|
||||||
origin = ?body.origin()
|
origin = ?body.identity
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
async fn process_inbound_transaction(
|
async fn process_inbound_transaction(
|
||||||
@@ -164,7 +164,7 @@ async fn process_inbound_transaction(
|
|||||||
.stream();
|
.stream();
|
||||||
|
|
||||||
debug!(pdus = body.pdus.len(), edus = body.edus.len(), "Processing transaction",);
|
debug!(pdus = body.pdus.len(), edus = body.edus.len(), "Processing transaction",);
|
||||||
let results = match handle(&services, &client, body.origin(), pdus, edus).await {
|
let results = match handle(&services, &client, &body.identity, pdus, edus).await {
|
||||||
| Ok(results) => results,
|
| Ok(results) => results,
|
||||||
| Err(err) => {
|
| Err(err) => {
|
||||||
fail_federation_txn(services, &txn_key, &sender, err);
|
fail_federation_txn(services, &txn_key, &sender, err);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user