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

@ -21,10 +21,9 @@ use ruma::{
GlobalAccountDataEventType, StateEventType, TimelineEventType,
},
push::{Action, Ruleset, Tweak},
state_res::{self, Event, RoomVersion},
state_res::{self, Event},
uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId,
OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, RoomVersionId,
ServerName, UserId,
OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, ServerName, UserId,
};
use serde::Deserialize;
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
@ -40,7 +39,7 @@ use crate::{
pdu::{EventHash, PduBuilder},
},
services,
utils::{self, on_demand_hashmap::KeyToken},
utils::{self, on_demand_hashmap::KeyToken, room_version::RoomVersion},
Error, PduEvent, Result,
};
@ -410,44 +409,32 @@ impl Service {
TimelineEventType::RoomRedaction => {
let room_version_id =
services().rooms.state.get_room_version(&pdu.room_id)?;
match &room_version_id {
room_version if *room_version < RoomVersionId::V11 => {
if let Some(redact_id) = &pdu.redacts {
if services().rooms.state_accessor.user_can_redact(
redact_id,
&pdu.sender,
&pdu.room_id,
false,
)? {
self.redact_pdu(redact_id, pdu, shortroomid)?;
}
let room_version = RoomVersion::try_from(&room_version_id)?;
if room_version.redaction_event_redacts_in_content {
let content = serde_json::from_str::<
RoomRedactionEventContent,
>(pdu.content.get())
.map_err(|_| {
Error::bad_database("Invalid content in redaction pdu.")
})?;
if let Some(redact_id) = &content.redacts {
if services().rooms.state_accessor.user_can_redact(
redact_id,
&pdu.sender,
&pdu.room_id,
false,
)? {
self.redact_pdu(redact_id, pdu, shortroomid)?;
}
}
RoomVersionId::V11 => {
let content =
serde_json::from_str::<RoomRedactionEventContent>(
pdu.content.get(),
)
.map_err(|_| {
Error::bad_database(
"Invalid content in redaction pdu.",
)
})?;
if let Some(redact_id) = &content.redacts {
if services().rooms.state_accessor.user_can_redact(
redact_id,
&pdu.sender,
&pdu.room_id,
false,
)? {
self.redact_pdu(redact_id, pdu, shortroomid)?;
}
}
}
_ => {
return Err(Error::BadServerResponse(
"Unsupported room version.",
));
} else if let Some(redact_id) = &pdu.redacts {
if services().rooms.state_accessor.user_can_redact(
redact_id,
&pdu.sender,
&pdu.room_id,
false,
)? {
self.redact_pdu(redact_id, pdu, shortroomid)?;
}
};
}
@ -737,8 +724,10 @@ impl Service {
}
})?;
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())
})?;
let auth_events = services().rooms.state.get_auth_events(
room_id,
@ -810,7 +799,7 @@ impl Service {
};
let auth_check = state_res::auth_check(
&room_version,
&ruma_room_version,
&pdu,
// TODO: third_party_invite
None::<PduEvent>,
@ -1008,57 +997,41 @@ impl Service {
// If redaction event is not authorized, do not append it to the
// timeline
if pdu.kind == TimelineEventType::RoomRedaction {
match services().rooms.state.get_room_version(&pdu.room_id)? {
RoomVersionId::V1
| RoomVersionId::V2
| RoomVersionId::V3
| RoomVersionId::V4
| RoomVersionId::V5
| RoomVersionId::V6
| RoomVersionId::V7
| RoomVersionId::V8
| RoomVersionId::V9
| RoomVersionId::V10 => {
if let Some(redact_id) = &pdu.redacts {
if !services().rooms.state_accessor.user_can_redact(
redact_id,
&pdu.sender,
&pdu.room_id,
false,
)? {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"User cannot redact this event.",
));
}
};
}
RoomVersionId::V11 => {
let content = serde_json::from_str::<
RoomRedactionEventContent,
>(pdu.content.get())
let room_version_id =
services().rooms.state.get_room_version(&pdu.room_id)?;
let room_version = RoomVersion::try_from(&room_version_id)?;
if room_version.redaction_event_redacts_in_content {
let content =
serde_json::from_str::<RoomRedactionEventContent>(
pdu.content.get(),
)
.map_err(|_| {
Error::bad_database("Invalid content in redaction pdu.")
})?;
if let Some(redact_id) = &content.redacts {
if !services().rooms.state_accessor.user_can_redact(
redact_id,
&pdu.sender,
&pdu.room_id,
false,
)? {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"User cannot redact this event.",
));
}
if let Some(redact_id) = &content.redacts {
if !services().rooms.state_accessor.user_can_redact(
redact_id,
&pdu.sender,
&pdu.room_id,
false,
)? {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"User cannot redact this event.",
));
}
}
_ => {
} else if let Some(redact_id) = &pdu.redacts {
if !services().rooms.state_accessor.user_can_redact(
redact_id,
&pdu.sender,
&pdu.room_id,
false,
)? {
return Err(Error::BadRequest(
ErrorKind::UnsupportedRoomVersion,
"Unsupported room version",
ErrorKind::forbidden(),
"User cannot redact this event.",
));
}
}