mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49ce6b4072 | |||
| f6ad1787a0 | |||
| 81ff8f1bd3 | |||
| 04980b3ee7 |
Generated
+11
-11
@@ -4063,7 +4063,7 @@ checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma"
|
name = "ruma"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assign",
|
"assign",
|
||||||
"js_int",
|
"js_int",
|
||||||
@@ -4083,7 +4083,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-appservice-api"
|
name = "ruma-appservice-api"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
@@ -4095,7 +4095,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-client-api"
|
name = "ruma-client-api"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"assign",
|
"assign",
|
||||||
@@ -4118,7 +4118,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-common"
|
name = "ruma-common"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
@@ -4150,7 +4150,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-events"
|
name = "ruma-events"
|
||||||
version = "0.28.1"
|
version = "0.28.1"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
@@ -4175,7 +4175,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-federation-api"
|
name = "ruma-federation-api"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"headers",
|
"headers",
|
||||||
@@ -4197,7 +4197,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identifiers-validation"
|
name = "ruma-identifiers-validation"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
@@ -4206,7 +4206,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identity-service-api"
|
name = "ruma-identity-service-api"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
@@ -4216,7 +4216,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-macros"
|
name = "ruma-macros"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
@@ -4231,7 +4231,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-push-gateway-api"
|
name = "ruma-push-gateway-api"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
@@ -4243,7 +4243,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-signatures"
|
name = "ruma-signatures"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=27abe0dcd33fd4056efc94bab3582646b31b6ce9#27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=3a6f29fda2c3ccf07282c746dc0e2021defc50bb#3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
|
|||||||
+62
-62
@@ -33,11 +33,11 @@ features = ["serde"]
|
|||||||
[workspace.dependencies.smallvec]
|
[workspace.dependencies.smallvec]
|
||||||
version = "1.14.0"
|
version = "1.14.0"
|
||||||
features = [
|
features = [
|
||||||
"const_generics",
|
"const_generics",
|
||||||
"const_new",
|
"const_new",
|
||||||
"serde",
|
"serde",
|
||||||
"union",
|
"union",
|
||||||
"write",
|
"write",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.smallstr]
|
[workspace.dependencies.smallstr]
|
||||||
@@ -96,13 +96,13 @@ version = "1.11.1"
|
|||||||
version = "0.7.9"
|
version = "0.7.9"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"form",
|
"form",
|
||||||
"http1",
|
"http1",
|
||||||
"http2",
|
"http2",
|
||||||
"json",
|
"json",
|
||||||
"matched-path",
|
"matched-path",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.axum-extra]
|
[workspace.dependencies.axum-extra]
|
||||||
@@ -149,10 +149,10 @@ features = ["aws_lc_rs"]
|
|||||||
version = "0.12.15"
|
version = "0.12.15"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"rustls-tls-native-roots",
|
"rustls-tls-native-roots",
|
||||||
"socks",
|
"socks",
|
||||||
"hickory-dns",
|
"hickory-dns",
|
||||||
"http2",
|
"http2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.serde]
|
[workspace.dependencies.serde]
|
||||||
@@ -188,18 +188,18 @@ default-features = false
|
|||||||
version = "0.25.5"
|
version = "0.25.5"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"jpeg",
|
"jpeg",
|
||||||
"png",
|
"png",
|
||||||
"gif",
|
"gif",
|
||||||
"webp",
|
"webp",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.blurhash]
|
[workspace.dependencies.blurhash]
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"fast-linear-to-srgb",
|
"fast-linear-to-srgb",
|
||||||
"image",
|
"image",
|
||||||
]
|
]
|
||||||
|
|
||||||
# logging
|
# logging
|
||||||
@@ -229,13 +229,13 @@ default-features = false
|
|||||||
version = "4.5.35"
|
version = "4.5.35"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"derive",
|
"derive",
|
||||||
"env",
|
"env",
|
||||||
"error-context",
|
"error-context",
|
||||||
"help",
|
"help",
|
||||||
"std",
|
"std",
|
||||||
"string",
|
"string",
|
||||||
"usage",
|
"usage",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.futures]
|
[workspace.dependencies.futures]
|
||||||
@@ -247,15 +247,15 @@ features = ["std", "async-await"]
|
|||||||
version = "1.44.2"
|
version = "1.44.2"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"fs",
|
"fs",
|
||||||
"net",
|
"net",
|
||||||
"macros",
|
"macros",
|
||||||
"sync",
|
"sync",
|
||||||
"signal",
|
"signal",
|
||||||
"time",
|
"time",
|
||||||
"rt-multi-thread",
|
"rt-multi-thread",
|
||||||
"io-util",
|
"io-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.tokio-metrics]
|
[workspace.dependencies.tokio-metrics]
|
||||||
@@ -280,18 +280,18 @@ default-features = false
|
|||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"server",
|
"server",
|
||||||
"http1",
|
"http1",
|
||||||
"http2",
|
"http2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.hyper-util]
|
[workspace.dependencies.hyper-util]
|
||||||
version = "=0.1.17"
|
version = "=0.1.17"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"server-auto",
|
"server-auto",
|
||||||
"server-graceful",
|
"server-graceful",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
# to support multiple variations of setting a config option
|
# to support multiple variations of setting a config option
|
||||||
@@ -310,9 +310,9 @@ features = ["env", "toml"]
|
|||||||
version = "0.25.1"
|
version = "0.25.1"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"serde",
|
"serde",
|
||||||
"system-config",
|
"system-config",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Used for conduwuit::Error type
|
# Used for conduwuit::Error type
|
||||||
@@ -351,7 +351,7 @@ version = "0.1.2"
|
|||||||
# Used for matrix spec type definitions and helpers
|
# Used for matrix spec type definitions and helpers
|
||||||
[workspace.dependencies.ruma]
|
[workspace.dependencies.ruma]
|
||||||
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
||||||
rev = "27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
rev = "3a6f29fda2c3ccf07282c746dc0e2021defc50bb"
|
||||||
features = [
|
features = [
|
||||||
"compat",
|
"compat",
|
||||||
"rand",
|
"rand",
|
||||||
@@ -381,13 +381,13 @@ features = [
|
|||||||
"unstable-msc4095",
|
"unstable-msc4095",
|
||||||
"unstable-msc4121",
|
"unstable-msc4121",
|
||||||
"unstable-msc4125",
|
"unstable-msc4125",
|
||||||
"unstable-msc4155",
|
"unstable-msc4155",
|
||||||
"unstable-msc4186",
|
"unstable-msc4186",
|
||||||
"unstable-msc4203", # sending to-device events to appservices
|
"unstable-msc4203", # sending to-device events to appservices
|
||||||
"unstable-msc4210", # remove legacy mentions
|
"unstable-msc4210", # remove legacy mentions
|
||||||
"unstable-extensible-events",
|
"unstable-extensible-events",
|
||||||
"unstable-pdu",
|
"unstable-pdu",
|
||||||
"unstable-msc4155"
|
"unstable-msc4155"
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.rust-rocksdb]
|
[workspace.dependencies.rust-rocksdb]
|
||||||
@@ -395,11 +395,11 @@ git = "https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1"
|
|||||||
rev = "61d9d23872197e9ace4a477f2617d5c9f50ecb23"
|
rev = "61d9d23872197e9ace4a477f2617d5c9f50ecb23"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"multi-threaded-cf",
|
"multi-threaded-cf",
|
||||||
"mt_static",
|
"mt_static",
|
||||||
"lz4",
|
"lz4",
|
||||||
"zstd",
|
"zstd",
|
||||||
"bzip2",
|
"bzip2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.sha2]
|
[workspace.dependencies.sha2]
|
||||||
@@ -458,16 +458,16 @@ git = "https://forgejo.ellis.link/continuwuation/jemallocator"
|
|||||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"background_threads_runtime_support",
|
"background_threads_runtime_support",
|
||||||
"unprefixed_malloc_on_supported_platforms",
|
"unprefixed_malloc_on_supported_platforms",
|
||||||
]
|
]
|
||||||
[workspace.dependencies.tikv-jemallocator]
|
[workspace.dependencies.tikv-jemallocator]
|
||||||
git = "https://forgejo.ellis.link/continuwuation/jemallocator"
|
git = "https://forgejo.ellis.link/continuwuation/jemallocator"
|
||||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"background_threads_runtime_support",
|
"background_threads_runtime_support",
|
||||||
"unprefixed_malloc_on_supported_platforms",
|
"unprefixed_malloc_on_supported_platforms",
|
||||||
]
|
]
|
||||||
[workspace.dependencies.tikv-jemalloc-ctl]
|
[workspace.dependencies.tikv-jemalloc-ctl]
|
||||||
git = "https://forgejo.ellis.link/continuwuation/jemallocator"
|
git = "https://forgejo.ellis.link/continuwuation/jemallocator"
|
||||||
@@ -491,9 +491,9 @@ default-features = false
|
|||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"static",
|
"static",
|
||||||
"gcc",
|
"gcc",
|
||||||
"light",
|
"light",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.rustyline-async]
|
[workspace.dependencies.rustyline-async]
|
||||||
|
|||||||
@@ -1515,16 +1515,11 @@
|
|||||||
#
|
#
|
||||||
#block_non_admin_invites = false
|
#block_non_admin_invites = false
|
||||||
|
|
||||||
# Enable or disable making requests to MSC4284 Policy Servers.
|
# This item is undocumented. Please contribute documentation for it.
|
||||||
# It is recommended you keep this enabled unless you experience frequent
|
|
||||||
# connectivity issues, such as in a restricted networking environment.
|
|
||||||
#
|
#
|
||||||
#enable_msc4284_policy_servers = true
|
#enable_msc4284_policy_servers = true
|
||||||
|
|
||||||
# Enable running locally generated events through configured MSC4284
|
# This item is undocumented. Please contribute documentation for it.
|
||||||
# policy servers. You may wish to disable this if your server is
|
|
||||||
# single-user for a slight speed benefit in some rooms, but otherwise
|
|
||||||
# should leave it enabled.
|
|
||||||
#
|
#
|
||||||
#policy_server_check_own_events = true
|
#policy_server_check_own_events = true
|
||||||
|
|
||||||
@@ -1739,6 +1734,12 @@
|
|||||||
#
|
#
|
||||||
#ldap = false
|
#ldap = false
|
||||||
|
|
||||||
|
# Configuration for protocol experiments that enable experimental
|
||||||
|
# features. Each one is associated with a matrix spec proposal, a list of
|
||||||
|
# which are published at https://spec.matrix.org/proposals/
|
||||||
|
#
|
||||||
|
#experiments = false
|
||||||
|
|
||||||
[global.tls]
|
[global.tls]
|
||||||
|
|
||||||
# Path to a valid TLS certificate file.
|
# Path to a valid TLS certificate file.
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use ruma::{
|
|||||||
room::member::{MembershipState, RoomMemberEventContent},
|
room::member::{MembershipState, RoomMemberEventContent},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use serde_json::value::RawValue;
|
||||||
use service::Services;
|
use service::Services;
|
||||||
|
|
||||||
use super::banned_room_check;
|
use super::banned_room_check;
|
||||||
@@ -146,7 +147,17 @@ pub(crate) async fn invite_helper(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let invite_room_state = services.rooms.state.summary_stripped(&pdu, room_id).await;
|
let invite_room_state = services
|
||||||
|
.rooms
|
||||||
|
.state
|
||||||
|
.summary(&pdu, room_id)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.map(|evt| RawValue::from_string(evt.json().get().to_owned()))
|
||||||
|
.collect::<std::result::Result<Vec<_>, _>>()
|
||||||
|
.map_err(|e| {
|
||||||
|
err!(Request(BadJson(warn!("Could not clone invite state event: {e}"))))
|
||||||
|
})?;
|
||||||
|
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
|
|
||||||
|
|||||||
+351
-89
@@ -1,30 +1,327 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, PduEvent, Result, err,
|
Err, Error, EventTypeExt, PduEvent, Result, RoomVersion, debug, debug_warn, err,
|
||||||
matrix::{Event, event::gen_event_id},
|
matrix::{Event, StateKey, event::gen_event_id},
|
||||||
|
trace,
|
||||||
utils::{self, hash::sha256},
|
utils::{self, hash::sha256},
|
||||||
warn,
|
warn,
|
||||||
};
|
};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonValue, OwnedUserId, UserId,
|
CanonicalJsonValue, OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
|
||||||
api::{client::error::ErrorKind, federation::membership::create_invite},
|
api::{client::error::ErrorKind, federation::membership::create_invite},
|
||||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
events::{
|
||||||
serde::JsonObject,
|
StateEventType,
|
||||||
|
room::{
|
||||||
|
create::RoomCreateEventContent,
|
||||||
|
member::{MembershipState, RoomMemberEventContent},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
use serde_json::value::RawValue;
|
||||||
|
use service::{Services, rooms::timeline::pdu_fits};
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
|
|
||||||
|
/// Ensures that the state received from the invite endpoint is sane, correct,
|
||||||
|
/// and complies with the room version's requirements.
|
||||||
|
async fn check_invite_state(
|
||||||
|
services: &Services,
|
||||||
|
stripped_state: &Vec<Box<RawValue>>,
|
||||||
|
room_id: &RoomId,
|
||||||
|
room_version_id: &RoomVersionId,
|
||||||
|
) -> Result<()> {
|
||||||
|
let room_version = RoomVersion::new(room_version_id).map_err(|e| {
|
||||||
|
err!(Request(UnsupportedRoomVersion("Invalid room version provided: {e}")))
|
||||||
|
})?;
|
||||||
|
let mut room_state: HashMap<(StateEventType, StateKey), PduEvent> = HashMap::new();
|
||||||
|
|
||||||
|
// Build the room state from the provided state events,
|
||||||
|
// ensuring that there's no duplicates. We need to check that m.room.create is
|
||||||
|
// present and lines up with the other things we've been told, and then verify
|
||||||
|
// any signatures present to ensure this isn't forged.
|
||||||
|
for raw_event in stripped_state {
|
||||||
|
trace!("Processing invite state event: {:?}", raw_event);
|
||||||
|
let canonical = utils::to_canonical_object(raw_event)?;
|
||||||
|
let event_id = gen_event_id(&canonical, room_version_id)?;
|
||||||
|
let event = PduEvent::from_id_val(&event_id, canonical.clone())
|
||||||
|
.map_err(|e| err!(Request(InvalidParam("Invite state event is invalid: {e}"))))?;
|
||||||
|
if event.state_key().is_none() {
|
||||||
|
return Err!(Request(InvalidParam("Invite state event missing event type.")));
|
||||||
|
}
|
||||||
|
let key = event
|
||||||
|
.event_type()
|
||||||
|
.with_state_key(event.state_key().unwrap());
|
||||||
|
if room_state.contains_key(&key) {
|
||||||
|
return Err!(Request(InvalidParam("Duplicate state event found for {key:?}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify the event
|
||||||
|
if !pdu_fits(&canonical) {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"An invite state event ({event_id}) is too large"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
services
|
||||||
|
.server_keys
|
||||||
|
.verify_event(&canonical, Some(room_version_id))
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
err!(Request(InvalidParam(
|
||||||
|
"Signature failed verification on event {event_id}: {e}"
|
||||||
|
)))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Ensure all events are in the same room
|
||||||
|
if event.room_id_or_hash() != room_id {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"State event room ID for {} does not match the expected room ID {}.",
|
||||||
|
event.event_id,
|
||||||
|
room_id,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
room_state.insert(key, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify m.room.create is present, has a matching room ID, and a matching room
|
||||||
|
// version.
|
||||||
|
let create_event = room_state
|
||||||
|
.get(&(StateEventType::RoomCreate, "".into()))
|
||||||
|
.ok_or_else(|| err!(Request(MissingParam("Missing m.room.create in stripped state."))))?;
|
||||||
|
let create_event_content: RoomCreateEventContent = create_event
|
||||||
|
.get_content()
|
||||||
|
.map_err(|e| err!(Request(InvalidParam("Invalid m.room.create content: {e}"))))?;
|
||||||
|
// Room v12 removed room IDs over federation, so we'll need to see if the event
|
||||||
|
// ID matches the room ID instead.
|
||||||
|
if room_version.room_ids_as_hashes {
|
||||||
|
let given_room_id = create_event.event_id().as_str().replace('$', "!");
|
||||||
|
if given_room_id != room_id.as_str() {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"m.room.create event ID does not match the room ID."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else if create_event.room_id().unwrap() != room_id {
|
||||||
|
return Err!(Request(InvalidParam("m.room.create room ID does not match the room ID.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the room version matches
|
||||||
|
if &create_event_content.room_version != room_version_id {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"m.room.create room version does not match the given room version."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks solid
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures that the invite event received from the invite endpoint is sane,
|
||||||
|
/// correct, and complies with the room version's requirements.
|
||||||
|
/// Returns the invited user ID on success.
|
||||||
|
async fn check_invite_event(
|
||||||
|
services: &Services,
|
||||||
|
invite_event: &PduEvent,
|
||||||
|
origin: &ServerName,
|
||||||
|
room_id: &RoomId,
|
||||||
|
room_version_id: &RoomVersionId,
|
||||||
|
) -> Result<OwnedUserId> {
|
||||||
|
// Check: The event sender is not a user ID on the origin server.
|
||||||
|
if invite_event.sender.server_name() != origin {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"Invite event sender's server does not match the origin server."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
// Check: The `state_key` is not a user ID on the receiving server.
|
||||||
|
let state_key: &UserId = invite_event
|
||||||
|
.state_key()
|
||||||
|
.ok_or_else(|| err!(Request(MissingParam("Invite event missing state_key."))))?
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| err!(Request(InvalidParam("Invalid state_key property: {e}"))))?;
|
||||||
|
if !services.globals.server_is_ours(state_key.server_name()) {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"Invite event state_key does not belong to this homeserver."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check: The event's room ID matches the expected room ID.
|
||||||
|
if let Some(evt_room_id) = invite_event.room_id() {
|
||||||
|
if evt_room_id != room_id {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"Invite event room ID does not match the expected room ID."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err!(Request(MissingParam("Invite event missing room ID.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check: the membership really is "invite"
|
||||||
|
let content = invite_event.get_content::<RoomMemberEventContent>()?;
|
||||||
|
if content.membership != MembershipState::Invite {
|
||||||
|
return Err!(Request(InvalidParam("Invite event is not a membership invite.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check: signature is valid
|
||||||
|
let as_obj = &mut utils::to_canonical_object(invite_event)?;
|
||||||
|
// remove the event_id before verification
|
||||||
|
as_obj.remove("event_id");
|
||||||
|
services
|
||||||
|
.server_keys
|
||||||
|
.verify_event(as_obj, Some(room_version_id))
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
err!(Request(InvalidParam("Invite event signature failed verification: {e}")))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(state_key.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs only legacy checks on the invite, for use when the requesting
|
||||||
|
/// server doesn't support matrix v1.16 invites.
|
||||||
|
/// This is significantly less secure than the full checks and should only be
|
||||||
|
/// used if the user has allowed it.
|
||||||
|
async fn legacy_check_invite(
|
||||||
|
services: &Services,
|
||||||
|
origin: &ServerName,
|
||||||
|
invite_event: &PduEvent,
|
||||||
|
stripped_state: &Vec<Box<RawValue>>,
|
||||||
|
room_id: &RoomId,
|
||||||
|
room_version_id: &RoomVersionId,
|
||||||
|
) -> Result<OwnedUserId> {
|
||||||
|
// Ensure the sender is from origin, the state key is a user ID that points at a
|
||||||
|
// local user, the event type is m.room.member with membership "invite", and
|
||||||
|
// the room ID matches.
|
||||||
|
if invite_event.sender().server_name() != origin {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"Invite event sender's server does not match the origin server."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let state_key: &UserId = invite_event
|
||||||
|
.state_key()
|
||||||
|
.ok_or_else(|| err!(Request(MissingParam("Invite event missing state_key."))))?
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| err!(Request(InvalidParam("Invalid state_key property: {e}"))))?;
|
||||||
|
if !services.globals.server_is_ours(state_key.server_name()) {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"Invite event state_key does not belong to this homeserver."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if let Some(evt_room_id) = invite_event.room_id() {
|
||||||
|
if evt_room_id != room_id {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"Invite event room ID does not match the expected room ID."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err!(Request(MissingParam("Invite event missing room ID.")));
|
||||||
|
}
|
||||||
|
let content = invite_event.get_content::<RoomMemberEventContent>()?;
|
||||||
|
if content.membership != MembershipState::Invite {
|
||||||
|
return Err!(Request(InvalidParam("Invite event is not a membership invite.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can also opportunistically check that the m.room.create event is present
|
||||||
|
// and matches the room version, to avoid accepting invites to rooms that
|
||||||
|
// don't match.
|
||||||
|
let mut has_create = false;
|
||||||
|
for raw_event in stripped_state {
|
||||||
|
let canonical = utils::to_canonical_object(raw_event)?;
|
||||||
|
if canonical.get("room_id").is_none() {
|
||||||
|
// This is a stripped event, skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(event_type) = canonical.get("type") {
|
||||||
|
if event_type.as_str().unwrap_or_default() == "m.room.create" {
|
||||||
|
has_create = true;
|
||||||
|
let event_id = gen_event_id(&canonical, room_version_id)?;
|
||||||
|
let event = PduEvent::from_id_val(&event_id, canonical.clone()).map_err(|e| {
|
||||||
|
err!(Request(InvalidParam("Invite state event is invalid: {e}")))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// We can verify that the room ID is correct now
|
||||||
|
let version = RoomVersion::new(room_version_id)?;
|
||||||
|
if version.room_ids_as_hashes {
|
||||||
|
let given_room_id = event.event_id().as_str().replace('$', "!");
|
||||||
|
if given_room_id != room_id.as_str() {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"m.room.create event ID does not match the room ID."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else if event.room_id().unwrap() != room_id {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"m.room.create room ID does not match the room ID."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
// Everything's as fine as we're getting with this event
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !has_create {
|
||||||
|
warn!(
|
||||||
|
"federated invite is missing m.room.create event in stripped state, the remote \
|
||||||
|
server is either outdated or trying something fishy."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(state_key.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks the incoming event is allowed and not forged.
|
||||||
|
/// If the MSC4311 enforcement experiment is enabled, performs full checks,
|
||||||
|
/// otherwise performs legacy checks only.
|
||||||
|
async fn check_invite(
|
||||||
|
services: &Services,
|
||||||
|
invite_event: &PduEvent,
|
||||||
|
stripped_state: &Vec<Box<RawValue>>,
|
||||||
|
origin: &ServerName,
|
||||||
|
room_id: &RoomId,
|
||||||
|
room_version_id: &RoomVersionId,
|
||||||
|
) -> Result<OwnedUserId> {
|
||||||
|
if services.config.experiments.enforce_msc4311 {
|
||||||
|
debug!("Checking invite event validity");
|
||||||
|
let user = check_invite_event(services, invite_event, origin, room_id, room_version_id)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
debug_warn!("Invite event validity check failed: {e}");
|
||||||
|
})?;
|
||||||
|
debug!("Checking invite state validity");
|
||||||
|
check_invite_state(services, stripped_state, room_id, room_version_id)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
debug_warn!("Invite state validity check failed: {e}");
|
||||||
|
})?;
|
||||||
|
Ok(user)
|
||||||
|
} else {
|
||||||
|
debug!("Performing legacy invite checks");
|
||||||
|
legacy_check_invite(
|
||||||
|
services,
|
||||||
|
origin,
|
||||||
|
invite_event,
|
||||||
|
stripped_state,
|
||||||
|
room_id,
|
||||||
|
room_version_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
debug_warn!("Legacy invite validity check failed: {e}");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
|
/// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
|
||||||
///
|
///
|
||||||
/// Invites a remote user to a room.
|
/// Invites a remote user to a room.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
|
#[tracing::instrument(skip_all, fields(%client, room_id=?body.room_id), name = "invite")]
|
||||||
pub(crate) async fn create_invite_route(
|
pub(crate) async fn create_invite_route(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
InsecureClientIp(client): InsecureClientIp,
|
InsecureClientIp(client): InsecureClientIp,
|
||||||
body: Ruma<create_invite::v2::Request>,
|
body: Ruma<create_invite::v2::Request>,
|
||||||
) -> Result<create_invite::v2::Response> {
|
) -> Result<create_invite::v2::Response> {
|
||||||
|
debug!("Received invite request from {}: {:?}", body.room_id, body.origin());
|
||||||
|
|
||||||
// ACL check origin
|
// ACL check origin
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
@@ -33,6 +330,7 @@ pub(crate) async fn create_invite_route(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !services.server.supported_room_version(&body.room_version) {
|
if !services.server.supported_room_version(&body.room_version) {
|
||||||
|
debug_warn!("Unsupported room version: {}", body.room_version);
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::IncompatibleRoomVersion { room_version: body.room_version.clone() },
|
ErrorKind::IncompatibleRoomVersion { room_version: body.room_version.clone() },
|
||||||
"Server does not support this room version.",
|
"Server does not support this room version.",
|
||||||
@@ -41,6 +339,7 @@ pub(crate) async fn create_invite_route(
|
|||||||
|
|
||||||
if let Some(server) = body.room_id.server_name() {
|
if let Some(server) = body.room_id.server_name() {
|
||||||
if services.moderation.is_remote_server_forbidden(server) {
|
if services.moderation.is_remote_server_forbidden(server) {
|
||||||
|
warn!("Received invite to room created by a banned server: {}. Rejecting.", server);
|
||||||
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,7 +349,7 @@ pub(crate) async fn create_invite_route(
|
|||||||
.is_remote_server_forbidden(body.origin())
|
.is_remote_server_forbidden(body.origin())
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Received federated/remote invite from banned server {} for room ID {}. Rejecting.",
|
"Received invite from banned server {} for room ID {}. Rejecting.",
|
||||||
body.origin(),
|
body.origin(),
|
||||||
body.room_id
|
body.room_id
|
||||||
);
|
);
|
||||||
@@ -61,57 +360,42 @@ pub(crate) async fn create_invite_route(
|
|||||||
let mut signed_event = utils::to_canonical_object(&body.event)
|
let mut signed_event = utils::to_canonical_object(&body.event)
|
||||||
.map_err(|_| err!(Request(InvalidParam("Invite event is invalid."))))?;
|
.map_err(|_| err!(Request(InvalidParam("Invite event is invalid."))))?;
|
||||||
|
|
||||||
// Ensure this is a membership event
|
// We need to hash and sign the event before we can generate the event ID.
|
||||||
if signed_event
|
// It is important that this signed event does not get sent back to the caller
|
||||||
.get("type")
|
// until we've verified this isn't incorrect.
|
||||||
.expect("event must have a type")
|
trace!(event=?signed_event, "Hashing & signing invite event");
|
||||||
.as_str()
|
services
|
||||||
.expect("type must be a string")
|
.server_keys
|
||||||
!= "m.room.member"
|
.hash_and_sign_event(&mut signed_event, &body.room_version)
|
||||||
{
|
.map_err(|e| err!(Request(InvalidParam("Failed to sign event: {e}"))))?;
|
||||||
return Err!(Request(BadJson(
|
let event_id = gen_event_id(&signed_event.clone(), &body.room_version)?;
|
||||||
"Not allowed to send non-membership event to invite endpoint."
|
if event_id != body.event_id {
|
||||||
)));
|
warn!("Event ID mismatch: expected {}, got {}", event_id, body.event_id);
|
||||||
|
return Err!(Request(InvalidParam("Event ID does not match the generated event ID.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let content: RoomMemberEventContent = serde_json::from_value(
|
let pdu = PduEvent::from_id_val(&event_id, signed_event.clone())
|
||||||
signed_event
|
.map_err(|e| err!(Request(InvalidParam("Invite event is invalid: {e}"))))?;
|
||||||
.get("content")
|
|
||||||
.ok_or_else(|| err!(Request(BadJson("Event missing content property"))))?
|
let recipient_user = check_invite(
|
||||||
.clone()
|
&services,
|
||||||
.into(),
|
&pdu,
|
||||||
|
&body.invite_room_state,
|
||||||
|
body.origin(),
|
||||||
|
&body.room_id,
|
||||||
|
&body.room_version,
|
||||||
)
|
)
|
||||||
.map_err(|e| err!(Request(BadJson(warn!("Event content is empty or invalid: {e}")))))?;
|
.await?;
|
||||||
|
|
||||||
// Ensure this is an invite membership event
|
// Make sure the room isn't banned and we allow invites
|
||||||
if content.membership != MembershipState::Invite {
|
if services.config.block_non_admin_invites && !services.users.is_admin(&recipient_user).await
|
||||||
return Err!(Request(BadJson(
|
|
||||||
"Not allowed to send a non-invite membership event to invite endpoint."
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the sending user isn't a lying bozo
|
|
||||||
let sender_server = signed_event
|
|
||||||
.get("sender")
|
|
||||||
.try_into()
|
|
||||||
.map(UserId::server_name)
|
|
||||||
.map_err(|e| err!(Request(InvalidParam("Invalid sender property: {e}"))))?;
|
|
||||||
if sender_server != body.origin() {
|
|
||||||
return Err!(Request(Forbidden("Sender's server does not match the origin server.",)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the target user belongs to this server
|
|
||||||
let recipient_user: OwnedUserId = signed_event
|
|
||||||
.get("state_key")
|
|
||||||
.try_into()
|
|
||||||
.map(UserId::to_owned)
|
|
||||||
.map_err(|e| err!(Request(InvalidParam("Invalid state_key property: {e}"))))?;
|
|
||||||
|
|
||||||
if !services
|
|
||||||
.globals
|
|
||||||
.server_is_ours(recipient_user.server_name())
|
|
||||||
{
|
{
|
||||||
return Err!(Request(InvalidParam("User does not belong to this homeserver.")));
|
return Err!(Request(Forbidden("This server does not allow room invites.")));
|
||||||
|
}
|
||||||
|
if services.rooms.metadata.is_banned(&body.room_id).await
|
||||||
|
&& !services.users.is_admin(&recipient_user).await
|
||||||
|
{
|
||||||
|
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we're not ACL'ed from their room.
|
// Make sure we're not ACL'ed from their room.
|
||||||
@@ -121,45 +405,9 @@ pub(crate) async fn create_invite_route(
|
|||||||
.acl_check(recipient_user.server_name(), &body.room_id)
|
.acl_check(recipient_user.server_name(), &body.room_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
services
|
|
||||||
.server_keys
|
|
||||||
.hash_and_sign_event(&mut signed_event, &body.room_version)
|
|
||||||
.map_err(|e| err!(Request(InvalidParam("Failed to sign event: {e}"))))?;
|
|
||||||
|
|
||||||
// Generate event id
|
|
||||||
let event_id = gen_event_id(&signed_event, &body.room_version)?;
|
|
||||||
|
|
||||||
// Add event_id back
|
// Add event_id back
|
||||||
signed_event.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.to_string()));
|
signed_event.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.to_string()));
|
||||||
|
|
||||||
let sender_user: &UserId = signed_event
|
|
||||||
.get("sender")
|
|
||||||
.try_into()
|
|
||||||
.map_err(|e| err!(Request(InvalidParam("Invalid sender property: {e}"))))?;
|
|
||||||
|
|
||||||
if services.rooms.metadata.is_banned(&body.room_id).await
|
|
||||||
&& !services.users.is_admin(&recipient_user).await
|
|
||||||
{
|
|
||||||
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if services.config.block_non_admin_invites && !services.users.is_admin(&recipient_user).await
|
|
||||||
{
|
|
||||||
return Err!(Request(Forbidden("This server does not allow room invites.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut invite_state = body.invite_room_state.clone();
|
|
||||||
|
|
||||||
let mut event: JsonObject = serde_json::from_str(body.event.get())
|
|
||||||
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?;
|
|
||||||
|
|
||||||
event.insert("event_id".to_owned(), "$placeholder".into());
|
|
||||||
|
|
||||||
let pdu: PduEvent = serde_json::from_value(event.into())
|
|
||||||
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?;
|
|
||||||
|
|
||||||
invite_state.push(pdu.to_format());
|
|
||||||
|
|
||||||
// If we are active in the room, the remote server will notify us about the
|
// If we are active in the room, the remote server will notify us about the
|
||||||
// join/invite through /send. If we are not in the room, we need to manually
|
// join/invite through /send. If we are not in the room, we need to manually
|
||||||
// record the invited state for client /sync through update_membership(), and
|
// record the invited state for client /sync through update_membership(), and
|
||||||
@@ -170,6 +418,19 @@ pub(crate) async fn create_invite_route(
|
|||||||
.server_in_room(services.globals.server_name(), &body.room_id)
|
.server_in_room(services.globals.server_name(), &body.room_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
let mut invite_state: Vec<CanonicalJsonValue> = body
|
||||||
|
.invite_room_state
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| utils::to_canonical_object(&v).unwrap().into())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
invite_state.push(pdu.to_canonical_object().into());
|
||||||
|
let sender_user: &UserId = signed_event
|
||||||
|
.get("sender")
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| err!(Request(InvalidParam("Invalid sender property: {e}"))))?;
|
||||||
|
debug!("Marking user {} as invited to remote room {}", recipient_user, body.room_id);
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
@@ -208,6 +469,7 @@ pub(crate) async fn create_invite_route(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("Invite is valid, returning signed event");
|
||||||
Ok(create_invite::v2::Response {
|
Ok(create_invite::v2::Response {
|
||||||
event: services
|
event: services
|
||||||
.sending
|
.sending
|
||||||
|
|||||||
@@ -175,11 +175,7 @@ pub(crate) async fn create_knock_event_v1_route(
|
|||||||
.send_pdu_room(&body.room_id, &pdu_id)
|
.send_pdu_room(&body.room_id, &pdu_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let knock_room_state = services
|
let knock_room_state = services.rooms.state.summary(&pdu, &body.room_id).await;
|
||||||
.rooms
|
|
||||||
.state
|
|
||||||
.summary_stripped(&pdu, &body.room_id)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(send_knock::v1::Response { knock_room_state })
|
Ok(send_knock::v1::Response { knock_room_state })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
mod msc4284_policy_servers;
|
||||||
|
|
||||||
|
use conduwuit_macros::config_example_generator;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
|
#[config_example_generator(filename = "conduwuit-example.toml", section = "global.experiments")]
|
||||||
|
pub struct Experiments {
|
||||||
|
/// Enforce MSC4311's updated requirements on all incoming invites.
|
||||||
|
///
|
||||||
|
/// This drastically increases the security and filtering capabilities
|
||||||
|
/// when processing invites over federation, at the cost of compatibility.
|
||||||
|
/// Servers that do not implement MSC4311 will be unable to send invites
|
||||||
|
/// to your server when this is enabled, including continuwuity 0.5.0 and
|
||||||
|
/// below.
|
||||||
|
///
|
||||||
|
/// default: false
|
||||||
|
/// Introduced in: (unreleased)
|
||||||
|
#[serde(default)]
|
||||||
|
pub enforce_msc4311: bool,
|
||||||
|
|
||||||
|
/// MSC4284 Policy Server support configuration.
|
||||||
|
#[serde(default)]
|
||||||
|
pub msc4284: msc4284_policy_servers::MSC4248,
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
use conduwuit_macros::config_example_generator;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
fn true_fn() -> bool { true }
|
||||||
|
|
||||||
|
fn default_federation_timeout() -> u64 { 25 }
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
|
#[config_example_generator(
|
||||||
|
filename = "conduwuit-example.toml",
|
||||||
|
section = "global.experiments.msc4284"
|
||||||
|
)]
|
||||||
|
pub struct MSC4248 {
|
||||||
|
/// Enable or disable making requests to MSC4284 Policy Servers.
|
||||||
|
/// It is recommended you keep this enabled unless you experience frequent
|
||||||
|
/// connectivity issues, such as in a restricted networking environment.
|
||||||
|
///
|
||||||
|
/// default: true
|
||||||
|
/// Introduced in: 0.5.0
|
||||||
|
#[serde(default = "true_fn")]
|
||||||
|
pub enabled: bool,
|
||||||
|
|
||||||
|
/// Enable running locally generated events through configured MSC4284
|
||||||
|
/// policy servers. You may wish to disable this if your server is
|
||||||
|
/// single-user for a slight speed benefit in some rooms, but otherwise
|
||||||
|
/// should leave it enabled.
|
||||||
|
///
|
||||||
|
/// If the room's policy server configuration requires event signatures,
|
||||||
|
/// this option is effectively ignored, as otherwise local events would
|
||||||
|
/// be rejected for missing the policy server's signature.
|
||||||
|
///
|
||||||
|
/// default: true
|
||||||
|
/// Introduced in: 0.5.0
|
||||||
|
#[serde(default = "true_fn")]
|
||||||
|
pub check_own_events: bool,
|
||||||
|
|
||||||
|
/// MSC4284 Policy server request timeout (seconds). Generally policy
|
||||||
|
/// servers should respond near instantly, however may slow down under
|
||||||
|
/// load. If a policy server doesn't respond in a short amount of time, the
|
||||||
|
/// room it is configured in may become unusable if this limit is set too
|
||||||
|
/// high. 25 seconds is a good default, however should be raised if you
|
||||||
|
/// experience too many connection issues.
|
||||||
|
///
|
||||||
|
/// Please be aware that policy requests are *NOT* currently re-tried, so if
|
||||||
|
/// a spam check request fails, the event will be assumed to be not spam,
|
||||||
|
/// which in some cases may result in spam being sent to or received from
|
||||||
|
/// the room that would typically be prevented.
|
||||||
|
///
|
||||||
|
/// If your request timeout is too low, and the policy server requires
|
||||||
|
/// signatures, you may find that you are unable to send events that are
|
||||||
|
/// accepted regardless.
|
||||||
|
///
|
||||||
|
/// About policy servers: https://matrix.org/blog/2025/04/introducing-policy-servers/
|
||||||
|
/// default: 25
|
||||||
|
/// Introduced in: 0.5.0
|
||||||
|
#[serde(default = "default_federation_timeout")]
|
||||||
|
pub request_timeout: u64,
|
||||||
|
}
|
||||||
+11
-8
@@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::doc_link_with_quotes)]
|
#![allow(clippy::doc_link_with_quotes)]
|
||||||
pub mod check;
|
pub mod check;
|
||||||
|
pub mod experiments;
|
||||||
pub mod manager;
|
pub mod manager;
|
||||||
pub mod proxy;
|
pub mod proxy;
|
||||||
|
|
||||||
@@ -1734,16 +1735,9 @@ pub struct Config {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub block_non_admin_invites: bool,
|
pub block_non_admin_invites: bool,
|
||||||
|
|
||||||
/// Enable or disable making requests to MSC4284 Policy Servers.
|
|
||||||
/// It is recommended you keep this enabled unless you experience frequent
|
|
||||||
/// connectivity issues, such as in a restricted networking environment.
|
|
||||||
#[serde(default = "true_fn")]
|
#[serde(default = "true_fn")]
|
||||||
pub enable_msc4284_policy_servers: bool,
|
pub enable_msc4284_policy_servers: bool,
|
||||||
|
|
||||||
/// Enable running locally generated events through configured MSC4284
|
|
||||||
/// policy servers. You may wish to disable this if your server is
|
|
||||||
/// single-user for a slight speed benefit in some rooms, but otherwise
|
|
||||||
/// should leave it enabled.
|
|
||||||
#[serde(default = "true_fn")]
|
#[serde(default = "true_fn")]
|
||||||
pub policy_server_check_own_events: bool,
|
pub policy_server_check_own_events: bool,
|
||||||
|
|
||||||
@@ -2003,6 +1997,13 @@ pub struct Config {
|
|||||||
// external structure; separate section
|
// external structure; separate section
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub blurhashing: BlurhashConfig,
|
pub blurhashing: BlurhashConfig,
|
||||||
|
|
||||||
|
/// Configuration for protocol experiments that enable experimental
|
||||||
|
/// features. Each one is associated with a matrix spec proposal, a list of
|
||||||
|
/// which are published at https://spec.matrix.org/proposals/
|
||||||
|
#[serde(default)]
|
||||||
|
pub experiments: experiments::Experiments,
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
#[allow(clippy::zero_sized_map_values)]
|
#[allow(clippy::zero_sized_map_values)]
|
||||||
// this is a catchall, the map shouldn't be zero at runtime
|
// this is a catchall, the map shouldn't be zero at runtime
|
||||||
@@ -2216,7 +2217,7 @@ struct ListeningAddr {
|
|||||||
addrs: Either<IpAddr, Vec<IpAddr>>,
|
addrs: Either<IpAddr, Vec<IpAddr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEPRECATED_KEYS: &[&str; 9] = &[
|
const DEPRECATED_KEYS: &[&str; 11] = &[
|
||||||
"cache_capacity",
|
"cache_capacity",
|
||||||
"conduit_cache_capacity_modifier",
|
"conduit_cache_capacity_modifier",
|
||||||
"max_concurrent_requests",
|
"max_concurrent_requests",
|
||||||
@@ -2226,6 +2227,8 @@ const DEPRECATED_KEYS: &[&str; 9] = &[
|
|||||||
"well_known_support_role",
|
"well_known_support_role",
|
||||||
"well_known_support_email",
|
"well_known_support_email",
|
||||||
"well_known_support_mxid",
|
"well_known_support_mxid",
|
||||||
|
"enable_msc4284_policy_servers",
|
||||||
|
"policy_server_check_own_events",
|
||||||
];
|
];
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ use futures::{
|
|||||||
use ruma::{
|
use ruma::{
|
||||||
EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, UserId,
|
EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, UserId,
|
||||||
events::{
|
events::{
|
||||||
AnyStrippedStateEvent, StateEventType, TimelineEventType,
|
AnyStateEvent, StateEventType, TimelineEventType, room::create::RoomCreateEventContent,
|
||||||
room::create::RoomCreateEventContent,
|
|
||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
};
|
};
|
||||||
@@ -307,12 +306,22 @@ impl Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a summary of the room state for invites and knock responses.
|
||||||
|
///
|
||||||
|
/// This used to return stripped state, but now returns complete events.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
///
|
||||||
|
/// - m.room.create
|
||||||
|
/// - m.room.join_rules
|
||||||
|
/// - m.room.canonical_alias
|
||||||
|
/// - m.room.name
|
||||||
|
/// - m.room.avatar
|
||||||
|
/// - m.room.member (of the event sender)
|
||||||
|
/// - m.room.encryption
|
||||||
|
/// - m.room.topic
|
||||||
#[tracing::instrument(skip_all, level = "debug")]
|
#[tracing::instrument(skip_all, level = "debug")]
|
||||||
pub async fn summary_stripped<'a, E>(
|
pub async fn summary<'a, E>(&self, event: &'a E, room_id: &RoomId) -> Vec<Raw<AnyStateEvent>>
|
||||||
&self,
|
|
||||||
event: &'a E,
|
|
||||||
room_id: &RoomId,
|
|
||||||
) -> Vec<Raw<AnyStrippedStateEvent>>
|
|
||||||
where
|
where
|
||||||
E: Event + Send + Sync,
|
E: Event + Send + Sync,
|
||||||
&'a E: Event + Send,
|
&'a E: Event + Send,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use conduwuit::{Err, Event, Pdu, Result, implement, is_not_empty, utils::ReadyExt, warn};
|
use conduwuit::{Err, Event, Pdu, Result, implement, is_not_empty, utils::ReadyExt};
|
||||||
use database::{Json, serialize_key};
|
use database::{Json, serialize_key};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedServerName, RoomId, UserId,
|
CanonicalJsonValue, OwnedServerName, RoomId, UserId,
|
||||||
events::{
|
events::{
|
||||||
AnyStrippedStateEvent, GlobalAccountDataEventType, RoomAccountDataEventType,
|
AnyStrippedStateEvent, GlobalAccountDataEventType, RoomAccountDataEventType,
|
||||||
StateEventType,
|
StateEventType,
|
||||||
@@ -334,7 +334,7 @@ pub async fn mark_as_invited(
|
|||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
sender_user: &UserId,
|
sender_user: &UserId,
|
||||||
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
|
last_state: Option<Vec<CanonicalJsonValue>>,
|
||||||
invite_via: Option<Vec<OwnedServerName>>,
|
invite_via: Option<Vec<OwnedServerName>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// return an error for blocked invites. ignored invites aren't handled here
|
// return an error for blocked invites. ignored invites aren't handled here
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use serde_json::value::{RawValue, to_raw_value};
|
|||||||
|
|
||||||
use super::RoomMutexGuard;
|
use super::RoomMutexGuard;
|
||||||
|
|
||||||
pub fn pdu_fits(owned_obj: &mut CanonicalJsonObject) -> bool {
|
pub fn pdu_fits(owned_obj: &CanonicalJsonObject) -> bool {
|
||||||
// room IDs, event IDs, senders, types, and state keys must all be <= 255 bytes
|
// room IDs, event IDs, senders, types, and state keys must all be <= 255 bytes
|
||||||
if let Some(CanonicalJsonValue::String(room_id)) = owned_obj.get("room_id") {
|
if let Some(CanonicalJsonValue::String(room_id)) = owned_obj.get("room_id") {
|
||||||
if room_id.len() > 255 {
|
if room_id.len() > 255 {
|
||||||
|
|||||||
Reference in New Issue
Block a user