fix room version comparisons

Fixes a set of bugs introduced by 00b77144c1,
where we replaced explicit `RoomVersionId` matches with `version < V11`
comparisons. The `Ord` impl on `RoomVersionId` does not work like that,
and is in fact a lexicographic string comparison[1]. The most visible
effect of these bugs is that incoming redaction events would sometimes
be ignored.

Instead of reverting to the explicit matches, which were quite verbose,
I implemented a `RoomVersion` struct that has flags for each property
that we care about. This is similar to the approach used by ruma[2] and
synapse[3].

[1]: 7cfa3be0c6/crates/ruma-common/src/identifiers/room_version_id.rs (L136)
[2]: 7cfa3be0c6/crates/ruma-state-res/src/room_version.rs
[3]: c856ae4724/synapse/api/room_versions.py
This commit is contained in:
Benjamin Lee 2024-09-24 21:26:06 -07:00
parent ad37eae869
commit 9add9a1e96
No known key found for this signature in database
GPG key ID: FB9624E2885D55A4
8 changed files with 237 additions and 199 deletions

View file

@ -27,7 +27,7 @@ use ruma::{
StateEventType, TimelineEventType,
},
int,
state_res::{self, RoomVersion, StateMap},
state_res::{self, StateMap},
uint, CanonicalJsonObject, CanonicalJsonValue, EventId,
MilliSecondsSinceUnixEpoch, OwnedServerName, OwnedServerSigningKeyId,
RoomId, RoomVersionId, ServerName,
@ -44,7 +44,7 @@ use super::{
use crate::{
service::{globals::SigningKeys, pdu},
services,
utils::debug_slice_truncated,
utils::{debug_slice_truncated, room_version::RoomVersion},
Error, PduEvent, Result,
};
@ -339,8 +339,10 @@ impl Service {
)?;
let room_version_id = &create_event_content.room_version;
let room_version = RoomVersion::new(room_version_id)
.expect("room version is supported");
let ruma_room_version =
state_res::RoomVersion::new(room_version_id).map_err(|_| {
Error::UnsupportedRoomVersion(room_version_id.clone())
})?;
// TODO: For RoomVersion6 we must check that Raw<..> is canonical,
// do we anywhere?
@ -527,7 +529,7 @@ impl Service {
}
if !state_res::event_auth::auth_check(
&room_version,
&ruma_room_version,
&incoming_pdu,
// TODO: third party invite
None::<PduEvent>,
@ -601,8 +603,11 @@ impl Service {
)?;
let room_version_id = &create_event_content.room_version;
let room_version = RoomVersion::new(room_version_id)
.expect("room version is supported");
let room_version = RoomVersion::try_from(room_version_id)?;
let ruma_room_version = state_res::RoomVersion::new(room_version_id)
.map_err(|_| {
Error::UnsupportedRoomVersion(room_version_id.clone())
})?;
// 10. Fetch missing state and auth chain events by calling /state_ids
// at backwards extremities doing all the checks in this list
@ -885,7 +890,7 @@ impl Service {
// 11. Check the auth of the event passes based on the state of the
// event
let check_result = state_res::event_auth::auth_check(
&room_version,
&ruma_room_version,
&incoming_pdu,
// TODO: third party invite
None::<PduEvent>,
@ -930,7 +935,7 @@ impl Service {
)?;
let soft_fail = !state_res::event_auth::auth_check(
&room_version,
&ruma_room_version,
&incoming_pdu,
None::<PduEvent>,
|k, s| auth_events.get(&(k.clone(), s.to_owned())),
@ -939,45 +944,34 @@ impl Service {
Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.")
})? || incoming_pdu.kind
== TimelineEventType::RoomRedaction
&& match room_version_id {
room_version if *room_version < RoomVersionId::V11 => {
if let Some(redact_id) = &incoming_pdu.redacts {
!services().rooms.state_accessor.user_can_redact(
redact_id,
&incoming_pdu.sender,
&incoming_pdu.room_id,
true,
)?
} else {
false
}
}
RoomVersionId::V11 => {
let content = serde_json::from_str::<
RoomRedactionEventContent,
>(
incoming_pdu.content.get()
&& if room_version.redaction_event_redacts_in_content {
let content =
serde_json::from_str::<RoomRedactionEventContent>(
incoming_pdu.content.get(),
)
.map_err(|_| {
Error::bad_database("Invalid content in redaction pdu.")
})?;
if let Some(redact_id) = &content.redacts {
!services().rooms.state_accessor.user_can_redact(
redact_id,
&incoming_pdu.sender,
&incoming_pdu.room_id,
true,
)?
} else {
false
}
}
_ => {
return Err(Error::BadServerResponse(
"Unsupported room version.",
))
if let Some(redact_id) = &content.redacts {
!services().rooms.state_accessor.user_can_redact(
redact_id,
&incoming_pdu.sender,
&incoming_pdu.room_id,
true,
)?
} else {
false
}
} else if let Some(redact_id) = &incoming_pdu.redacts {
!services().rooms.state_accessor.user_can_redact(
redact_id,
&incoming_pdu.sender,
&incoming_pdu.room_id,
true,
)?
} else {
false
};
// 13. Use state resolution to find new room state