This commit is contained in:
Erik Johnston
2026-04-07 15:04:05 +01:00
parent 9056570daa
commit c1cbd8cf40
2 changed files with 56 additions and 78 deletions
+46 -10
View File
@@ -21,6 +21,7 @@
//! Classes for representing Events.
use std::{
borrow::Cow,
collections::HashMap,
str::FromStr,
sync::{Arc, RwLock},
@@ -595,8 +596,6 @@ struct EventCommonFields {
type_: Box<str>,
signatures: Signatures,
room_id: Option<Box<str>>,
#[serde(default)]
unsigned: JsonObjectMutable,
@@ -616,7 +615,7 @@ struct Event {
#[pymethods]
impl Event {
#[new]
fn new<'a, 'py>(
fn new_from_py<'a, 'py>(
py: Python<'py>,
event_dict: &'a Bound<'py, PyAny>,
room_version: &'a Bound<'py, PyAny>,
@@ -648,7 +647,7 @@ impl Event {
let event_format_v3_v4_plus: EventFormatV3V4Container = depythonize(event_dict)?;
event_format_v3_v4_plus.check_valid(room_version)?;
event_format_v3_v4_plus.validate(room_version)?;
let internal_metadata = Py::new(py, EventInternalMetadata::new(internal_metadata_dict)?)?;
@@ -701,10 +700,9 @@ impl Event {
}
#[getter]
fn room_id(&self) -> Option<&str> {
fn room_id(&self) -> PyResult<Cow<'_, str>> {
match &self.inner {
EventFormatEnum::V3V4Plus(format) => format.common_fields.room_id.as_deref(),
// ...
EventFormatEnum::V3V4Plus(format) => Ok(format.room_id(&self.event_id)?),
}
}
@@ -923,6 +921,7 @@ enum EventFormatEnum {
struct EventFormatV3V4Plus {
auth_events: Vec<String>,
prev_events: Vec<String>,
room_id: Option<Box<str>>,
}
#[derive(Serialize, Deserialize)]
@@ -934,8 +933,28 @@ struct EventFormatV3V4Container {
}
impl EventFormatV3V4Container {
fn check_valid(&self, room_version: &RoomVersion) -> Result<(), Error> {
if self.common_fields.room_id.is_none() {
fn room_id(&self, event_id: &EventID) -> Result<Cow<'_, str>, Error> {
if let Some(room_id) = self.specific_fields.room_id.as_deref() {
return Ok(room_id.into());
}
// Ensure that this is a create event, as all other events must
// have a room ID.
if &*self.common_fields.type_ != "m.room.create" {
bail!("Event '{event_id}' has no room ID");
}
// The room ID is derived from the event ID by replacing the
// leading '$' with a '!'.
let mut room_id = String::with_capacity(event_id.len());
room_id.push('!');
room_id.push_str(&event_id[1..]);
Ok(room_id.into())
}
fn validate(&self, room_version: &RoomVersion) -> Result<(), Error> {
if self.specific_fields.room_id.is_none() {
// Only create events in event formats v4 plus can have a missing room_id.
if room_version.event_format != EventFormatVersions::ROOM_V4_PLUS {
bail!("room_id is required for event formats v3 and below");
@@ -953,6 +972,8 @@ impl EventFormatV3V4Container {
#[cfg(test)]
mod tests {
use signed_json::json;
use super::*;
#[test]
@@ -966,7 +987,7 @@ mod tests {
assert_eq!(&*event.common_fields.type_, "m.room.create");
assert_eq!(
event.common_fields.room_id.as_deref(),
event.specific_fields.room_id.as_deref(),
Some("!qVoJSympOqdUQRUfiC:localhost:8800")
);
@@ -996,4 +1017,19 @@ mod tests {
serde_json::from_str::<serde_json::Value>(json).unwrap()
);
}
#[test]
fn test_room_id_for_create_event() {
let json = r#"{"auth_events":[],"prev_events":[],"type":"m.room.create","sender":"@erikj:jki.re","content":{"room_version":"12","predecessor":{"room_id":"!VuNGkDTdbMOOxSmuDa:jki.re"}},"depth":1,"state_key":"","origin_server_ts":1775568141481,"hashes":{"sha256":"qBX+glsKvogXFrvsEN0eh13pO2kpuE6o/b4yREPtOqw"},"signatures":{"jki.re":{"ed25519:auto":"n/4gHQRagk3r1r24L/7a+oaMMf9cysVfQRYdjpDZcf4ppkVym33rhTW18Vy4zMa1L5nsWLkxsBvbrRRDYUOhBQ"}},"unsigned":{"age_ts":1775568141481}}"#;
let event_value: serde_json::Value = serde_json::from_str(json).unwrap();
let event: EventFormatV3V4Container = serde_json::from_str(json).unwrap();
let event_id = calculate_event_id(&event_value, &RoomVersion::V12).unwrap();
assert_eq!(
&*event.room_id(&event_id).unwrap(),
"!BeXKh925K_M46DwsuJFR0EyBpE1P7CFUDGuWW4xw55Y"
);
}
}
+10 -68
View File
@@ -112,7 +112,7 @@ pub fn redact(event: &Value, room_version: &RoomVersion) -> anyhow::Result<Value
]);
// Earlier room versions had additional allowed keys
if !use_updated_redaction_rules(room_version) {
if !room_version.updated_redaction_rules {
allowed_keys.extend([(PREV_STATE, ()), (MEMBERSHIP, ()), (ORIGIN, ())]);
}
@@ -140,10 +140,10 @@ pub fn redact(event: &Value, room_version: &RoomVersion) -> anyhow::Result<Value
match event_type {
M_ROOM_MEMBER => {
add_content_field(membership_field::MEMBERSHIP);
if use_restricted_join_rule_fix(room_version) {
if room_version.restricted_join_rule_fix {
add_content_field(membership_field::JOIN_AUTHORISED_VIA_USERS_SERVER);
}
if use_updated_redaction_rules(room_version) {
if room_version.updated_redaction_rules {
// Preserve the signed field under third_party_invite.
if let Some(third_party_invite) =
event_content.get(membership_field::THIRD_PARTY_INVITE)
@@ -163,7 +163,7 @@ pub fn redact(event: &Value, room_version: &RoomVersion) -> anyhow::Result<Value
}
}
M_ROOM_CREATE => {
if use_updated_redaction_rules(room_version) {
if room_version.updated_redaction_rules {
// MSC2176 rules state that create events cannot have their `content` redacted.
if let Some(event_content_object) = event_content.as_object() {
for (field, _value) in event_content_object {
@@ -171,18 +171,18 @@ pub fn redact(event: &Value, room_version: &RoomVersion) -> anyhow::Result<Value
}
}
}
if !use_implicit_room_creator(room_version) {
if !room_version.implicit_room_creator {
// Some room versions give meaning to `creator`
add_content_field(create_field::CREATOR);
}
if use_room_ids_as_hashes(room_version) {
if room_version.msc4291_room_ids_as_hashes {
// room_id is not allowed on the create event as it's derived from the event ID
allowed_keys.remove(ROOM_ID);
}
}
M_ROOM_JOIN_RULES => {
add_content_field(join_rules_field::JOIN_RULE);
if use_restricted_join_rule(room_version) {
if room_version.restricted_join_rule {
add_content_field(join_rules_field::ALLOW);
}
}
@@ -195,17 +195,17 @@ pub fn redact(event: &Value, room_version: &RoomVersion) -> anyhow::Result<Value
add_content_field(power_levels_field::BAN);
add_content_field(power_levels_field::KICK);
add_content_field(power_levels_field::REDACT);
if use_updated_redaction_rules(room_version) {
if room_version.updated_redaction_rules {
add_content_field(power_levels_field::INVITE);
}
}
M_ROOM_ALIASES if use_special_case_aliases_auth(room_version) => {
M_ROOM_ALIASES if room_version.special_case_aliases_auth => {
add_content_field(aliases_field::ALIASES);
}
M_ROOM_HISTORY_VISIBILITY => {
add_content_field(history_visibility_field::HISTORY_VISIBILITY)
}
M_ROOM_REDACTION if use_updated_redaction_rules(room_version) => {
M_ROOM_REDACTION if room_version.updated_redaction_rules => {
add_content_field(redaction_field::REDACTS);
}
_ => (),
@@ -261,64 +261,6 @@ pub fn redact_internal(event: &Value, room_version: &RoomVersion) -> anyhow::Res
Ok(redacted)
}
/// Whether the `redacts` field has been moved from the top level to inside `content` for the given
/// room version. The top-level `origin`, `membership`, and `prev_state` properties are no longer
/// protected from redaction. The `m.room.create` event now keeps the entire content property. The
/// `m.room.redaction` event keeps the `redacts` property under `content`. The
/// `m.room.power_levels` event keeps the `invite` property under `content`.
///
/// Added in room version 11.
/// <https://spec.matrix.org/v1.16/rooms/v11/#redactions>
pub fn use_updated_redaction_rules(room_version: &RoomVersion) -> bool {
room_version.updated_redaction_rules
}
/// Whether `m.room.aliases` has significant meaning.
/// This means that the `aliases` property under `content` is no longer kept for the `m.room.aliases`
/// event type.
///
/// Removed in room version 6.
/// <https://spec.matrix.org/v1.16/rooms/v6/#redactions>
fn use_special_case_aliases_auth(room_version_id: &RoomVersion) -> bool {
room_version_id.special_case_aliases_auth
}
/// `m.room.join_rules` events now keep `allow` in addition to other keys in `content` when being
/// redacted.
///
/// Added in room version 8.
/// <https://spec.matrix.org/v1.16/rooms/v8/#redactions>
fn use_restricted_join_rule(room_version_id: &RoomVersion) -> bool {
room_version_id.restricted_join_rule
}
/// `m.room.member` events now keep `join_authorised_via_users_server` in addition to other keys in
/// `content` when being redacted.
///
/// Added in room version 9.
/// <https://spec.matrix.org/v1.16/rooms/v9/#redactions>
fn use_restricted_join_rule_fix(room_version_id: &RoomVersion) -> bool {
room_version_id.restricted_join_rule_fix
}
/// The content of a `m.room.create` event no longer has a `creator` property, which previously was
/// always equivalent to the `sender` of the event.
///
/// Added in room version 11.
/// <https://spec.matrix.org/v1.16/rooms/v11/#redactions>
fn use_implicit_room_creator(room_version_id: &RoomVersion) -> bool {
room_version_id.implicit_room_creator
}
/// Whether room ids are hashes, or still the older localpart:domain style.
/// Returns true if the room_id is a hash for the given room version.
///
/// Added in room version 12.
/// <https://spec.matrix.org/v1.16/rooms/v12/#redactions>
pub fn use_room_ids_as_hashes(room_version_id: &RoomVersion) -> bool {
room_version_id.msc4291_room_ids_as_hashes
}
#[cfg(test)]
mod tests {
use serde_json::json;