mirror of
https://gitlab.com/famedly/conduit.git
synced 2025-08-11 17:50:59 +00:00
feat: MSC4311, Ensuring the create event is available on invites and knocks
This commit is contained in:
parent
d71d94a0c8
commit
532b17ade8
5 changed files with 153 additions and 17 deletions
|
@ -176,6 +176,7 @@ features = [
|
|||
"state-res",
|
||||
"unstable-msc2448",
|
||||
"unstable-msc4186",
|
||||
"unstable-msc4311",
|
||||
]
|
||||
git = "https://github.com/ruma/ruma.git"
|
||||
|
||||
|
@ -197,6 +198,8 @@ jemalloc = ["tikv-jemallocator"]
|
|||
sqlite = ["parking_lot", "rusqlite", "tokio/signal"]
|
||||
systemd = ["sd-notify"]
|
||||
|
||||
enforce_msc4311 = []
|
||||
|
||||
[[bin]]
|
||||
name = "conduit"
|
||||
path = "src/main.rs"
|
||||
|
|
|
@ -188,6 +188,9 @@ pub async fn knock_room_route(
|
|||
}
|
||||
_ => 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(
|
||||
&knock_template.event,
|
||||
|
@ -212,6 +215,8 @@ pub async fn knock_room_route(
|
|||
)
|
||||
.await?;
|
||||
|
||||
utils::check_stripped_state(&send_kock_response.knock_room_state, &room_id, &rules)?;
|
||||
|
||||
info!("send_knock finished");
|
||||
|
||||
let mut stripped_state = send_kock_response.knock_room_state;
|
||||
|
|
|
@ -2101,6 +2101,12 @@ pub async fn create_invite_route(
|
|||
));
|
||||
}
|
||||
|
||||
let rules = room_version
|
||||
.rules()
|
||||
.expect("Supported room version has rules");
|
||||
|
||||
utils::check_stripped_state(&invite_room_state, &room_id, &rules)?;
|
||||
|
||||
let mut signed_event = utils::to_canonical_object(&event)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invite event is invalid."))?;
|
||||
|
||||
|
@ -2108,23 +2114,15 @@ pub async fn create_invite_route(
|
|||
services().globals.server_name().as_str(),
|
||||
services().globals.keypair(),
|
||||
&mut signed_event,
|
||||
&room_version
|
||||
.rules()
|
||||
.expect("Supported room version has rules")
|
||||
.redaction,
|
||||
&rules.redaction,
|
||||
)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event."))?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = EventId::parse(format!(
|
||||
"${}",
|
||||
ruma::signatures::reference_hash(
|
||||
&signed_event,
|
||||
&room_version
|
||||
.rules()
|
||||
.expect("Supported room version has rules")
|
||||
)
|
||||
.expect("Event format validated when event was hashed")
|
||||
ruma::signatures::reference_hash(&signed_event, &rules)
|
||||
.expect("Event format validated when event was hashed")
|
||||
))
|
||||
.expect("ruma's reference hashes are valid event ids");
|
||||
|
||||
|
|
|
@ -276,8 +276,25 @@ impl Service {
|
|||
.room_state_get(room_id, state_event_type, "")
|
||||
.transpose()
|
||||
})
|
||||
.map(|e| e.map(|e| RawStrippedState::Stripped(e.to_stripped_state_event())))
|
||||
.collect()
|
||||
.map(|e| {
|
||||
if e.as_ref()
|
||||
.is_ok_and(|e| e.kind == TimelineEventType::RoomCreate)
|
||||
{
|
||||
e.and_then(|e| {
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&e.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)
|
||||
})
|
||||
} else {
|
||||
e.map(|e| RawStrippedState::Stripped(e.to_stripped_state_event()))
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()
|
||||
}
|
||||
|
||||
pub fn stripped_state_client(&self, room_id: &RoomId) -> Result<Vec<Raw<StrippedState>>> {
|
||||
|
|
121
src/utils/mod.rs
121
src/utils/mod.rs
|
@ -5,18 +5,24 @@ use cmp::Ordering;
|
|||
use rand::prelude::*;
|
||||
use ring::digest;
|
||||
use ruma::{
|
||||
api::{client::sync::sync_events::StrippedState, federation::membership::RawStrippedState},
|
||||
api::{
|
||||
client::{error::ErrorKind, sync::sync_events::StrippedState},
|
||||
federation::membership::RawStrippedState,
|
||||
},
|
||||
canonical_json::try_from_json_map,
|
||||
events::{AnyStateEvent, StateEventType},
|
||||
room_version_rules::RoomVersionRules,
|
||||
serde::Raw,
|
||||
CanonicalJsonError, CanonicalJsonObject, RoomId,
|
||||
CanonicalJsonError, CanonicalJsonObject, CanonicalJsonValue, RoomId,
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
use std::{
|
||||
cmp, fmt,
|
||||
str::FromStr,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use crate::Result;
|
||||
use crate::{service::pdu::gen_event_id_canonical_json, services, Result};
|
||||
|
||||
pub fn millis_since_unix_epoch() -> u64 {
|
||||
SystemTime::now()
|
||||
|
@ -196,12 +202,119 @@ impl fmt::Display for HtmlEscape<'_> {
|
|||
/// Converts `RawStrippedState` (federation format) into `Raw<StrippedState>` (client format)
|
||||
pub fn convert_stripped_state(
|
||||
stripped_state: Vec<RawStrippedState>,
|
||||
_room_id: &RoomId,
|
||||
room_id: &RoomId,
|
||||
) -> Result<Vec<Raw<StrippedState>>> {
|
||||
stripped_state
|
||||
.into_iter()
|
||||
.map(|stripped_state| match stripped_state {
|
||||
RawStrippedState::Stripped(state) => Ok(state.cast()),
|
||||
RawStrippedState::Pdu(state) => {
|
||||
let rules = services()
|
||||
.rooms
|
||||
.state
|
||||
.get_room_version(room_id)?
|
||||
.rules()
|
||||
.expect("Supported room version must have rules.");
|
||||
let (event_id, mut event) = gen_event_id_canonical_json(&state, &rules)?;
|
||||
|
||||
event.retain(|k, _| {
|
||||
matches!(
|
||||
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))
|
||||
.expect("To raw json should not fail since only change was adding signature");
|
||||
|
||||
Ok(Raw::<AnyStateEvent>::from_json(raw_value).cast())
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn check_stripped_state(
|
||||
stripped_state: &Vec<RawStrippedState>,
|
||||
room_id: &RoomId,
|
||||
rules: &RoomVersionRules,
|
||||
) -> Result<()> {
|
||||
// Nothing needs to be done for legacy room ids
|
||||
if room_id.server_name().is_some() && !rules.authorization.room_create_event_id_as_room_id {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut seen_create_event = false;
|
||||
#[cfg(feature = "enforce_msc4311")]
|
||||
let mut seen_valid_create_event = false;
|
||||
|
||||
for state in stripped_state {
|
||||
match state {
|
||||
RawStrippedState::Pdu(pdu) => {
|
||||
let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, rules) else {
|
||||
continue;
|
||||
};
|
||||
let Some(event_type) = value.get("type").and_then(|t| t.as_str()) else {
|
||||
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 {
|
||||
continue;
|
||||
};
|
||||
|
||||
if event.event_type() != StateEventType::RoomCreate {
|
||||
continue;
|
||||
}
|
||||
|
||||
if seen_create_event {
|
||||
return Err(error::Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Stripped state has multiple create events",
|
||||
));
|
||||
}
|
||||
|
||||
seen_create_event = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "enforce_msc4311")]
|
||||
if !seen_valid_create_event {
|
||||
return Err(error::Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Stripped state contained no valid create PDUs",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue