mirror of
https://gitlab.com/famedly/conduit.git
synced 2025-09-15 18:57:03 +00:00
Merge branch 'updated-msc4311' into 'next'
feat: updated MSC4311 support See merge request famedly/conduit!778
This commit is contained in:
commit
cd3a8b9f2e
10 changed files with 236 additions and 183 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -2575,7 +2575,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma"
|
name = "ruma"
|
||||||
version = "0.12.6"
|
version = "0.12.6"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assign",
|
"assign",
|
||||||
"js_int",
|
"js_int",
|
||||||
|
@ -2594,7 +2594,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-appservice-api"
|
name = "ruma-appservice-api"
|
||||||
version = "0.12.2"
|
version = "0.12.2"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
|
@ -2606,7 +2606,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-client-api"
|
name = "ruma-client-api"
|
||||||
version = "0.20.4"
|
version = "0.20.4"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"assign",
|
"assign",
|
||||||
|
@ -2629,7 +2629,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-common"
|
name = "ruma-common"
|
||||||
version = "0.15.4"
|
version = "0.15.4"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
@ -2661,7 +2661,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-events"
|
name = "ruma-events"
|
||||||
version = "0.30.5"
|
version = "0.30.5"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as_variant",
|
"as_variant",
|
||||||
"indexmap 2.9.0",
|
"indexmap 2.9.0",
|
||||||
|
@ -2685,7 +2685,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-federation-api"
|
name = "ruma-federation-api"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"headers",
|
"headers",
|
||||||
|
@ -2707,7 +2707,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-identifiers-validation"
|
name = "ruma-identifiers-validation"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
|
@ -2716,7 +2716,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-macros"
|
name = "ruma-macros"
|
||||||
version = "0.15.2"
|
version = "0.15.2"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
|
@ -2731,7 +2731,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-push-gateway-api"
|
name = "ruma-push-gateway-api"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
|
@ -2743,7 +2743,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-signatures"
|
name = "ruma-signatures"
|
||||||
version = "0.17.1"
|
version = "0.17.1"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
|
@ -2759,7 +2759,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruma-state-res"
|
name = "ruma-state-res"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
source = "git+https://github.com/ruma/ruma.git#d879f7df16ba9928a73649f8149dabeee939691e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js_int",
|
"js_int",
|
||||||
"ruma-common",
|
"ruma-common",
|
||||||
|
|
|
@ -9,7 +9,10 @@ use ruma::{
|
||||||
unban_user,
|
unban_user,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
federation::{self, membership::create_invite},
|
federation::{
|
||||||
|
self,
|
||||||
|
membership::{create_invite, RawStrippedState},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
events::{
|
events::{
|
||||||
room::{
|
room::{
|
||||||
|
@ -188,9 +191,6 @@ pub async fn knock_room_route(
|
||||||
}
|
}
|
||||||
_ => return Err(Error::BadServerResponse("Room version is not supported")),
|
_ => return Err(Error::BadServerResponse("Room version is not supported")),
|
||||||
};
|
};
|
||||||
let rules = room_version_id
|
|
||||||
.rules()
|
|
||||||
.expect("Supported room version has rules");
|
|
||||||
|
|
||||||
let (event_id, knock_event, _) = services().rooms.helpers.populate_membership_template(
|
let (event_id, knock_event, _) = services().rooms.helpers.populate_membership_template(
|
||||||
&knock_template.event,
|
&knock_template.event,
|
||||||
|
@ -215,8 +215,6 @@ pub async fn knock_room_route(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
utils::check_stripped_state(&send_kock_response.knock_room_state, &room_id, &rules)?;
|
|
||||||
|
|
||||||
info!("send_knock finished");
|
info!("send_knock finished");
|
||||||
|
|
||||||
let mut stripped_state = send_kock_response.knock_room_state;
|
let mut stripped_state = send_kock_response.knock_room_state;
|
||||||
|
@ -231,7 +229,7 @@ pub async fn knock_room_route(
|
||||||
.to_stripped_state_event()
|
.to_stripped_state_event()
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
let stripped_state = utils::convert_stripped_state(stripped_state, &rules)?;
|
let stripped_state = utils::convert_stripped_state(stripped_state)?;
|
||||||
|
|
||||||
services().rooms.state_cache.update_membership(
|
services().rooms.state_cache.update_membership(
|
||||||
&room_id,
|
&room_id,
|
||||||
|
@ -717,12 +715,22 @@ pub(crate) async fn invite_helper(
|
||||||
.rooms
|
.rooms
|
||||||
.state
|
.state
|
||||||
.stripped_state_federation(&pdu.room_id())?;
|
.stripped_state_federation(&pdu.room_id())?;
|
||||||
if let Some(sender) = services().rooms.state_accessor.room_state_get(
|
if let Some(sender_member_event_id) =
|
||||||
&pdu.room_id(),
|
services().rooms.state_accessor.room_state_get_id(
|
||||||
&StateEventType::RoomMember,
|
&pdu.room_id(),
|
||||||
sender_user.as_str(),
|
&StateEventType::RoomMember,
|
||||||
)? {
|
sender_user.as_str(),
|
||||||
invite_room_state.push(sender.to_stripped_state_event().into());
|
)?
|
||||||
|
{
|
||||||
|
let pdu = services()
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.get_pdu_json(&sender_member_event_id)
|
||||||
|
.transpose()
|
||||||
|
.expect("Event must be present for it to make up the current state")
|
||||||
|
.map(PduEvent::convert_to_outgoing_federation_event)
|
||||||
|
.map(RawStrippedState::Pdu)?;
|
||||||
|
invite_room_state.push(pdu);
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
|
|
|
@ -2105,7 +2105,7 @@ pub async fn create_invite_route(
|
||||||
.rules()
|
.rules()
|
||||||
.expect("Supported room version has rules");
|
.expect("Supported room version has rules");
|
||||||
|
|
||||||
utils::check_stripped_state(&invite_room_state, &room_id, &rules)?;
|
utils::check_stripped_state(&invite_room_state, &room_id, &rules).await?;
|
||||||
|
|
||||||
let mut signed_event = utils::to_canonical_object(&event)
|
let mut signed_event = utils::to_canonical_object(&event)
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invite event is invalid."))?;
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invite event is invalid."))?;
|
||||||
|
@ -2169,7 +2169,7 @@ pub async fn create_invite_route(
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
invite_state.push(pdu.to_stripped_state_event().into());
|
invite_state.push(pdu.to_stripped_state_event().into());
|
||||||
let invite_state = utils::convert_stripped_state(invite_state, &rules)?;
|
let invite_state = utils::convert_stripped_state(invite_state)?;
|
||||||
|
|
||||||
// If we are active in the room, the remote server will notify us about the join via /send
|
// If we are active in the room, the remote server will notify us about the join via /send
|
||||||
if !services()
|
if !services()
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use std::{collections::HashSet, sync::Arc};
|
use std::{collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::sync::sync_events::StrippedState, events::AnySyncStateEvent, serde::Raw,
|
events::{AnyStrippedStateEvent, AnySyncStateEvent},
|
||||||
|
serde::Raw,
|
||||||
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
last_state: Option<Vec<Raw<StrippedState>>>,
|
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (roomuser_id, userroom_id) = get_room_and_user_byte_ids(room_id, user_id);
|
let (roomuser_id, userroom_id) = get_room_and_user_byte_ids(room_id, user_id);
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
last_state: Option<Vec<Raw<StrippedState>>>,
|
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (roomuser_id, userroom_id) = get_room_and_user_byte_ids(room_id, user_id);
|
let (roomuser_id, userroom_id) = get_room_and_user_byte_ids(room_id, user_id);
|
||||||
|
|
||||||
|
@ -482,7 +483,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
fn rooms_invited<'a>(
|
fn rooms_invited<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<StrippedState>>)>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a> {
|
||||||
scan_userroom_id_memberstate_tree(user_id, &self.userroomid_invitestate)
|
scan_userroom_id_memberstate_tree(user_id, &self.userroomid_invitestate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,7 +493,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
fn rooms_knocked<'a>(
|
fn rooms_knocked<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<StrippedState>>)>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a> {
|
||||||
scan_userroom_id_memberstate_tree(user_id, &self.userroomid_knockstate)
|
scan_userroom_id_memberstate_tree(user_id, &self.userroomid_knockstate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +502,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<Option<Vec<Raw<StrippedState>>>> {
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>> {
|
||||||
let mut key = user_id.as_bytes().to_vec();
|
let mut key = user_id.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(room_id.as_bytes());
|
key.extend_from_slice(room_id.as_bytes());
|
||||||
|
@ -522,7 +523,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<Option<Vec<Raw<StrippedState>>>> {
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>> {
|
||||||
let mut key = user_id.as_bytes().to_vec();
|
let mut key = user_id.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(room_id.as_bytes());
|
key.extend_from_slice(room_id.as_bytes());
|
||||||
|
@ -543,7 +544,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<Option<Vec<Raw<StrippedState>>>> {
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>> {
|
||||||
let mut key = user_id.as_bytes().to_vec();
|
let mut key = user_id.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(room_id.as_bytes());
|
key.extend_from_slice(room_id.as_bytes());
|
||||||
|
|
|
@ -32,6 +32,7 @@ use ruma::{
|
||||||
},
|
},
|
||||||
int,
|
int,
|
||||||
room_version_rules::{AuthorizationRules, RoomVersionRules, StateResolutionV2Rules},
|
room_version_rules::{AuthorizationRules, RoomVersionRules, StateResolutionV2Rules},
|
||||||
|
serde::Base64,
|
||||||
state_res::{self, StateMap},
|
state_res::{self, StateMap},
|
||||||
uint, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch,
|
uint, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch,
|
||||||
OwnedServerName, OwnedServerSigningKeyId, RoomId, ServerName,
|
OwnedServerName, OwnedServerSigningKeyId, RoomId, ServerName,
|
||||||
|
@ -338,43 +339,14 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: For RoomVersion6 we must check that Raw<..> is canonical do we anywhere?: https://matrix.org/docs/spec/rooms/v6#canonical-json
|
// TODO: For RoomVersion6 we must check that Raw<..> is canonical do we anywhere?: https://matrix.org/docs/spec/rooms/v6#canonical-json
|
||||||
|
|
||||||
// We go through all the signatures we see on the value and fetch the corresponding signing
|
// We go through all the signatures we see on the value and fetch the corresponding signing
|
||||||
// keys
|
// keys
|
||||||
self.fetch_required_signing_keys(&value, pub_key_map)
|
self.fetch_required_signing_keys(&value, pub_key_map)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let origin_server_ts = value.get("origin_server_ts").ok_or_else(|| {
|
let filtered_keys = self
|
||||||
error!("Invalid PDU, no origin_server_ts field");
|
.filter_required_signing_keys(&value, pub_key_map, &room_version_rules)
|
||||||
Error::BadRequest(
|
.await?;
|
||||||
ErrorKind::MissingParam,
|
|
||||||
"Invalid PDU, no origin_server_ts field",
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let origin_server_ts: MilliSecondsSinceUnixEpoch = {
|
|
||||||
let ts = origin_server_ts.as_integer().ok_or_else(|| {
|
|
||||||
Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"origin_server_ts must be an integer",
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
MilliSecondsSinceUnixEpoch(i64::from(ts).try_into().map_err(|_| {
|
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Time must be after the unix epoch")
|
|
||||||
})?)
|
|
||||||
};
|
|
||||||
|
|
||||||
let guard = pub_key_map.read().await;
|
|
||||||
|
|
||||||
let pkey_map = (*guard).clone();
|
|
||||||
|
|
||||||
// Removing all the expired keys, unless the room version allows stale keys
|
|
||||||
let filtered_keys = services().globals.filter_keys_server_map(
|
|
||||||
pkey_map,
|
|
||||||
origin_server_ts,
|
|
||||||
&room_version_rules,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut val =
|
let mut val =
|
||||||
match ruma::signatures::verify_event(&filtered_keys, &value, &room_version_rules) {
|
match ruma::signatures::verify_event(&filtered_keys, &value, &room_version_rules) {
|
||||||
|
@ -416,8 +388,6 @@ impl Service {
|
||||||
Ok(ruma::signatures::Verified::All) => value,
|
Ok(ruma::signatures::Verified::All) => value,
|
||||||
};
|
};
|
||||||
|
|
||||||
drop(guard);
|
|
||||||
|
|
||||||
// Now that we have checked the signature and hashes we can add the eventID and convert
|
// Now that we have checked the signature and hashes we can add the eventID and convert
|
||||||
// to our PduEvent type
|
// to our PduEvent type
|
||||||
val.insert(
|
val.insert(
|
||||||
|
@ -1451,6 +1421,47 @@ impl Service {
|
||||||
Ok((sorted, eventid_info))
|
Ok((sorted, eventid_info))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Filters down the given signing keys, only keeping those which could be valid for this event.
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn filter_required_signing_keys(
|
||||||
|
&self,
|
||||||
|
event: &BTreeMap<String, CanonicalJsonValue>,
|
||||||
|
pub_key_map: &RwLock<BTreeMap<String, SigningKeys>>,
|
||||||
|
room_version_rules: &RoomVersionRules,
|
||||||
|
) -> Result<BTreeMap<String, BTreeMap<String, Base64>>> {
|
||||||
|
let origin_server_ts = event.get("origin_server_ts").ok_or_else(|| {
|
||||||
|
error!("Invalid PDU, no origin_server_ts field");
|
||||||
|
Error::BadRequest(
|
||||||
|
ErrorKind::MissingParam,
|
||||||
|
"Invalid PDU, no origin_server_ts field",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let origin_server_ts: MilliSecondsSinceUnixEpoch = {
|
||||||
|
let ts = origin_server_ts.as_integer().ok_or_else(|| {
|
||||||
|
Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"origin_server_ts must be an integer",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
MilliSecondsSinceUnixEpoch(i64::from(ts).try_into().map_err(|_| {
|
||||||
|
Error::BadRequest(ErrorKind::InvalidParam, "Time must be after the unix epoch")
|
||||||
|
})?)
|
||||||
|
};
|
||||||
|
|
||||||
|
let guard = pub_key_map.write().await;
|
||||||
|
|
||||||
|
let pkey_map = (*guard).clone();
|
||||||
|
|
||||||
|
// Removing all the expired keys, unless the room version allows stale keys
|
||||||
|
Ok(services().globals.filter_keys_server_map(
|
||||||
|
pkey_map,
|
||||||
|
origin_server_ts,
|
||||||
|
room_version_rules,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub(crate) async fn fetch_required_signing_keys(
|
pub(crate) async fn fetch_required_signing_keys(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -6,13 +6,11 @@ use std::{
|
||||||
|
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{client::error::ErrorKind, federation::membership::RawStrippedState},
|
||||||
client::{error::ErrorKind, sync::sync_events::StrippedState},
|
|
||||||
federation::membership::RawStrippedState,
|
|
||||||
},
|
|
||||||
events::{
|
events::{
|
||||||
room::{create::RoomCreateEventContent, member::MembershipState},
|
room::{create::RoomCreateEventContent, member::MembershipState},
|
||||||
StateEventType, TimelineEventType, RECOMMENDED_STRIPPED_STATE_EVENT_TYPES,
|
AnyStrippedStateEvent, StateEventType, TimelineEventType,
|
||||||
|
RECOMMENDED_STRIPPED_STATE_EVENT_TYPES,
|
||||||
},
|
},
|
||||||
room_version_rules::AuthorizationRules,
|
room_version_rules::AuthorizationRules,
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
|
@ -273,31 +271,28 @@ impl Service {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(room_id, state_event_type, "")
|
.room_state_get_id(room_id, state_event_type, "")
|
||||||
.transpose()
|
.transpose()
|
||||||
})
|
})
|
||||||
.map(|e| {
|
.map(|e| {
|
||||||
if e.as_ref()
|
e.and_then(|e| {
|
||||||
.is_ok_and(|e| e.kind == TimelineEventType::RoomCreate)
|
services()
|
||||||
{
|
.rooms
|
||||||
e.and_then(|e| {
|
.timeline
|
||||||
services()
|
.get_pdu_json(&e)
|
||||||
.rooms
|
.transpose()
|
||||||
.timeline
|
.expect("Event must be present for it to make up the current state")
|
||||||
.get_pdu_json(&e.event_id)
|
.map(PduEvent::convert_to_outgoing_federation_event)
|
||||||
.transpose()
|
.map(RawStrippedState::Pdu)
|
||||||
.expect("Event must be present for it to make up the current state")
|
})
|
||||||
.map(PduEvent::convert_to_outgoing_federation_event)
|
|
||||||
.map(RawStrippedState::Pdu)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
e.map(|e| RawStrippedState::Stripped(e.to_stripped_state_event()))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()
|
.collect::<Result<Vec<_>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stripped_state_client(&self, room_id: &RoomId) -> Result<Vec<Raw<StrippedState>>> {
|
pub fn stripped_state_client(
|
||||||
|
&self,
|
||||||
|
room_id: &RoomId,
|
||||||
|
) -> Result<Vec<Raw<AnyStrippedStateEvent>>> {
|
||||||
RECOMMENDED_STRIPPED_STATE_EVENT_TYPES
|
RECOMMENDED_STRIPPED_STATE_EVENT_TYPES
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|state_event_type| {
|
.filter_map(|state_event_type| {
|
||||||
|
@ -307,7 +302,7 @@ impl Service {
|
||||||
.room_state_get(room_id, state_event_type, "")
|
.room_state_get(room_id, state_event_type, "")
|
||||||
.transpose()
|
.transpose()
|
||||||
})
|
})
|
||||||
.map(|e| e.map(|e| e.to_stripped_state_event().cast()))
|
.map(|e| e.map(|e| e.to_stripped_state_event()))
|
||||||
.collect::<Result<Vec<_>>>()
|
.collect::<Result<Vec<_>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@ use std::{collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
use crate::{service::appservice::RegistrationInfo, Result};
|
use crate::{service::appservice::RegistrationInfo, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::sync::sync_events::StrippedState, events::AnySyncStateEvent, serde::Raw,
|
events::{AnyStrippedStateEvent, AnySyncStateEvent},
|
||||||
|
serde::Raw,
|
||||||
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,13 +14,13 @@ pub trait Data: Send + Sync {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
last_state: Option<Vec<Raw<StrippedState>>>,
|
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
fn mark_as_knocked(
|
fn mark_as_knocked(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
last_state: Option<Vec<Raw<StrippedState>>>,
|
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
fn mark_as_left(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>;
|
fn mark_as_left(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>;
|
||||||
|
|
||||||
|
@ -85,32 +86,32 @@ pub trait Data: Send + Sync {
|
||||||
fn rooms_invited<'a>(
|
fn rooms_invited<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<StrippedState>>)>> + 'a>;
|
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a>;
|
||||||
|
|
||||||
/// Returns an iterator over all rooms a user has knocked on.
|
/// Returns an iterator over all rooms a user has knocked on.
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn rooms_knocked<'a>(
|
fn rooms_knocked<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<StrippedState>>)>> + 'a>;
|
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a>;
|
||||||
|
|
||||||
fn invite_state(
|
fn invite_state(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<Option<Vec<Raw<StrippedState>>>>;
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>>;
|
||||||
|
|
||||||
fn knock_state(
|
fn knock_state(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<Option<Vec<Raw<StrippedState>>>>;
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>>;
|
||||||
|
|
||||||
fn left_state(
|
fn left_state(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<Option<Vec<Raw<StrippedState>>>>;
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>>;
|
||||||
|
|
||||||
/// Returns an iterator over all rooms a user left.
|
/// Returns an iterator over all rooms a user left.
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
|
|
|
@ -4,12 +4,12 @@ use std::{collections::HashSet, sync::Arc};
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::sync::sync_events::StrippedState,
|
|
||||||
events::{
|
events::{
|
||||||
direct::DirectEvent,
|
direct::DirectEvent,
|
||||||
ignored_user_list::IgnoredUserListEvent,
|
ignored_user_list::IgnoredUserListEvent,
|
||||||
room::{create::RoomCreateEventContent, member::MembershipState},
|
room::{create::RoomCreateEventContent, member::MembershipState},
|
||||||
AnySyncStateEvent, GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType,
|
AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType,
|
||||||
|
RoomAccountDataEventType, StateEventType,
|
||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
||||||
|
@ -31,7 +31,7 @@ impl Service {
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
membership: MembershipState,
|
membership: MembershipState,
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
last_state: Option<Vec<Raw<StrippedState>>>,
|
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
|
||||||
update_joined_count: bool,
|
update_joined_count: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Keep track what remote users exist by adding them as "deactivated" users
|
// Keep track what remote users exist by adding them as "deactivated" users
|
||||||
|
@ -317,7 +317,7 @@ impl Service {
|
||||||
pub fn rooms_invited<'a>(
|
pub fn rooms_invited<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> impl Iterator<Item = Result<(OwnedRoomId, Vec<Raw<StrippedState>>)>> + 'a {
|
) -> impl Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a {
|
||||||
self.db.rooms_invited(user_id)
|
self.db.rooms_invited(user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +326,7 @@ impl Service {
|
||||||
pub fn rooms_knocked<'a>(
|
pub fn rooms_knocked<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> impl Iterator<Item = Result<(OwnedRoomId, Vec<Raw<StrippedState>>)>> + 'a {
|
) -> impl Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a {
|
||||||
self.db.rooms_knocked(user_id)
|
self.db.rooms_knocked(user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +335,7 @@ impl Service {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<Option<Vec<Raw<StrippedState>>>> {
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>> {
|
||||||
self.db.invite_state(user_id, room_id)
|
self.db.invite_state(user_id, room_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ impl Service {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<Option<Vec<Raw<StrippedState>>>> {
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>> {
|
||||||
self.db.knock_state(user_id, room_id)
|
self.db.knock_state(user_id, room_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +353,7 @@ impl Service {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Result<Option<Vec<Raw<StrippedState>>>> {
|
) -> Result<Option<Vec<Raw<AnyStrippedStateEvent>>>> {
|
||||||
self.db.left_state(user_id, room_id)
|
self.db.left_state(user_id, room_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -455,7 +455,7 @@ impl Service {
|
||||||
.state
|
.state
|
||||||
.stripped_state_client(&pdu.room_id())?;
|
.stripped_state_client(&pdu.room_id())?;
|
||||||
// So that clients can get info about who invitied them (not relevant for knocking), the reason, when, etc.
|
// So that clients can get info about who invitied them (not relevant for knocking), the reason, when, etc.
|
||||||
state.push(pdu.to_stripped_state_event().cast());
|
state.push(pdu.to_stripped_state_event());
|
||||||
Some(state)
|
Some(state)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
185
src/utils/mod.rs
185
src/utils/mod.rs
|
@ -5,24 +5,26 @@ use cmp::Ordering;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use ring::digest;
|
use ring::digest;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{client::error::ErrorKind, federation::membership::RawStrippedState},
|
||||||
client::{error::ErrorKind, sync::sync_events::StrippedState},
|
|
||||||
federation::membership::RawStrippedState,
|
|
||||||
},
|
|
||||||
canonical_json::try_from_json_map,
|
canonical_json::try_from_json_map,
|
||||||
events::{AnyStateEvent, StateEventType},
|
events::AnyStrippedStateEvent,
|
||||||
room_version_rules::RoomVersionRules,
|
room_version_rules::RoomVersionRules,
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
|
signatures::Verified,
|
||||||
CanonicalJsonError, CanonicalJsonObject, CanonicalJsonValue, RoomId,
|
CanonicalJsonError, CanonicalJsonObject, CanonicalJsonValue, RoomId,
|
||||||
};
|
};
|
||||||
use serde_json::value::to_raw_value;
|
use serde_json::value::to_raw_value;
|
||||||
use std::{
|
use std::{
|
||||||
cmp, fmt,
|
cmp,
|
||||||
|
collections::BTreeMap,
|
||||||
|
fmt,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{service::pdu::gen_event_id_canonical_json, Result};
|
use crate::{service::pdu::gen_event_id_canonical_json, services, Error, Result};
|
||||||
|
|
||||||
pub fn millis_since_unix_epoch() -> u64 {
|
pub fn millis_since_unix_epoch() -> u64 {
|
||||||
SystemTime::now()
|
SystemTime::now()
|
||||||
|
@ -199,45 +201,39 @@ impl fmt::Display for HtmlEscape<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts `RawStrippedState` (federation format) into `Raw<StrippedState>` (client format)
|
/// Converts `RawStrippedState` (federation format) into `Raw<AnyStrippedState>` (client format)
|
||||||
pub fn convert_stripped_state(
|
pub fn convert_stripped_state(
|
||||||
stripped_state: Vec<RawStrippedState>,
|
stripped_state: Vec<RawStrippedState>,
|
||||||
rules: &RoomVersionRules,
|
) -> Result<Vec<Raw<AnyStrippedStateEvent>>> {
|
||||||
) -> Result<Vec<Raw<StrippedState>>> {
|
|
||||||
stripped_state
|
stripped_state
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|stripped_state| match stripped_state {
|
.map(|stripped_state| match stripped_state {
|
||||||
RawStrippedState::Stripped(state) => Ok(state.cast()),
|
RawStrippedState::Stripped(state) => Ok(state),
|
||||||
RawStrippedState::Pdu(state) => {
|
RawStrippedState::Pdu(state) => {
|
||||||
let (event_id, mut event) = gen_event_id_canonical_json(&state, rules)?;
|
let mut event: CanonicalJsonObject =
|
||||||
|
serde_json::from_str(state.get()).map_err(|e| {
|
||||||
|
warn!("Error parsing incoming event {:?}: {:?}", state, e);
|
||||||
|
Error::BadServerResponse("Invalid PDU in server response")
|
||||||
|
})?;
|
||||||
|
|
||||||
event.retain(|k, _| {
|
event.retain(|k, _| {
|
||||||
matches!(
|
matches!(k.as_str(), "content" | "sender" | "state_key" | "type")
|
||||||
k.as_str(),
|
|
||||||
"content"
|
|
||||||
| "event_id"
|
|
||||||
| "origin_server_ts"
|
|
||||||
| "room_id"
|
|
||||||
| "sender"
|
|
||||||
| "state_key"
|
|
||||||
| "type"
|
|
||||||
| "unsigned"
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
event.insert("event_id".to_owned(), event_id.as_str().into());
|
|
||||||
|
|
||||||
let raw_value = to_raw_value(&CanonicalJsonValue::Object(event))
|
let raw_value = to_raw_value(&CanonicalJsonValue::Object(event))
|
||||||
.expect("To raw json should not fail since only change was adding signature");
|
.expect("To raw json should not fail since only change was adding signature");
|
||||||
|
|
||||||
Ok(Raw::<AnyStateEvent>::from_json(raw_value).cast())
|
Ok(Raw::<AnyStrippedStateEvent>::from_json(raw_value))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_stripped_state(
|
/// Performs checks on incoming stripped state, as per [MSC4311]
|
||||||
stripped_state: &Vec<RawStrippedState>,
|
///
|
||||||
|
/// [MSC4311]: https://github.com/matrix-org/matrix-spec-proposals/pull/4311
|
||||||
|
pub async fn check_stripped_state(
|
||||||
|
stripped_state: &[RawStrippedState],
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
rules: &RoomVersionRules,
|
rules: &RoomVersionRules,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
@ -246,65 +242,106 @@ pub fn check_stripped_state(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "enforce_msc4311")]
|
||||||
let mut seen_create_event = false;
|
let mut seen_create_event = false;
|
||||||
#[cfg(feature = "enforce_msc4311")]
|
#[cfg(feature = "enforce_msc4311")]
|
||||||
let mut seen_valid_create_event = false;
|
if !stripped_state.iter().all(|state| match state {
|
||||||
|
RawStrippedState::Pdu(_) => true,
|
||||||
|
RawStrippedState::Stripped(_) => false,
|
||||||
|
}) {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Non-pdu found in stripped state",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
for state in stripped_state {
|
let stripped_state = stripped_state
|
||||||
match state {
|
.iter()
|
||||||
RawStrippedState::Pdu(pdu) => {
|
.filter_map(|event| {
|
||||||
let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, rules) else {
|
if let RawStrippedState::Pdu(pdu) = event {
|
||||||
continue;
|
Some(pdu)
|
||||||
};
|
} else {
|
||||||
let Some(event_type) = value.get("type").and_then(|t| t.as_str()) else {
|
None
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if event_type != "m.room.create" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if seen_create_event {
|
|
||||||
return Err(error::Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Stripped state has multiple create events",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if event_id.localpart() != room_id.strip_sigil() {
|
|
||||||
return Err(error::Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Room ID generated from create event does not match that from the request",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
seen_create_event = true;
|
|
||||||
#[cfg(feature = "enforce_msc4311")]
|
|
||||||
{
|
|
||||||
seen_valid_create_event = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RawStrippedState::Stripped(event) => {
|
})
|
||||||
let Ok(event) = event.deserialize() else {
|
.map(|pdu| gen_event_id_canonical_json(pdu, rules))
|
||||||
continue;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
};
|
|
||||||
|
|
||||||
if event.event_type() != StateEventType::RoomCreate {
|
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if seen_create_event {
|
for (_, pdu) in &stripped_state {
|
||||||
return Err(error::Error::BadRequest(
|
services()
|
||||||
ErrorKind::InvalidParam,
|
.rooms
|
||||||
"Stripped state has multiple create events",
|
.event_handler
|
||||||
));
|
.fetch_required_signing_keys(pdu, &pub_key_map)
|
||||||
}
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (event_id, pdu) in stripped_state {
|
||||||
|
let filtered_keys = services()
|
||||||
|
.rooms
|
||||||
|
.event_handler
|
||||||
|
.filter_required_signing_keys(&pdu, &pub_key_map, rules)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !ruma::signatures::verify_event(&filtered_keys, &pdu, rules)
|
||||||
|
.is_ok_and(|verified| verified == Verified::All)
|
||||||
|
{
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Signature check on stripped state failed",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(event_type) = pdu.get("type").and_then(|t| t.as_str()) else {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Event with no type returned",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
if !(event_type == "m.room.create" && rules.authorization.room_create_event_id_as_room_id) {
|
||||||
|
let pdu_room_id = pdu
|
||||||
|
.get("room_id")
|
||||||
|
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing room ID"))
|
||||||
|
.map(|v| v.as_str())?
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::BadRequest(ErrorKind::InvalidParam, "Event has non-string room id")
|
||||||
|
})
|
||||||
|
.map(RoomId::parse)?
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::BadRequest(ErrorKind::InvalidParam, "Event has invalid room ID")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if pdu_room_id != room_id {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Stripped state room ID does not match the one of the request",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if event_type == "m.room.create" {
|
||||||
|
#[allow(clippy::collapsible_if)]
|
||||||
|
if event_id.localpart() != room_id.strip_sigil()
|
||||||
|
&& rules.authorization.room_create_event_id_as_room_id
|
||||||
|
{
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Room ID generated from create event does not match that from the request",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "enforce_msc4311")]
|
||||||
|
{
|
||||||
seen_create_event = true;
|
seen_create_event = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "enforce_msc4311")]
|
#[cfg(feature = "enforce_msc4311")]
|
||||||
if !seen_valid_create_event {
|
if !seen_create_event {
|
||||||
return Err(error::Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Stripped state contained no valid create PDUs",
|
"Stripped state contained no valid create PDUs",
|
||||||
));
|
));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue