fix: Merge nex/fix-redactions-boogaloo-2

This commit is contained in:
Jacob Taylor
2026-10-05 07:41:01 -07:00
parent 32a0893db3
commit ef7eb0848c
3 changed files with 132 additions and 15 deletions
+23 -2
View File
@@ -6,7 +6,10 @@
matrix::pdu::{PartialPdu, sticky},
utils,
};
use ruma::{api::client::message::send_message_event, events::MessageLikeEventType};
use ruma::{
OwnedEventId, api::client::message::send_message_event, events::MessageLikeEventType,
};
use serde::Deserialize;
use serde_json::from_str;
use crate::{Ruma, client_ip::ClientIp};
@@ -77,6 +80,13 @@ pub(crate) async fn send_message_event_route(
let content = from_str(body.body.body.json().get())
.map_err(|e| err!(Request(BadJson("Invalid JSON body: {e}"))))?;
let redacts = if body.body.event_type == MessageLikeEventType::RoomRedaction {
from_str::<MaybeRedaction>(body.body.body.json().get())
.ok()
.and_then(|r| r.redacts)
} else {
None
};
let event_id = services
.rooms
@@ -92,11 +102,14 @@ pub(crate) async fn send_message_event_route(
.flatten()
.map(sticky::object),
unsigned: Some(unsigned),
timestamp: if body.identity.is_appservice() {
timestamp: if body.identity.is_appservice()
|| services.admin.user_is_admin(sender_user).await
{
body.timestamp
} else {
None
},
redacts,
..Default::default()
},
sender_user,
@@ -116,3 +129,11 @@ pub(crate) async fn send_message_event_route(
Ok(send_message_event::v3::Response::new(event_id))
}
/// Struct containing exclusively the `redacts` field for MSC4169 compatible
/// redaction moving.
#[derive(Deserialize)]
struct MaybeRedaction {
#[serde(default)]
redacts: Option<OwnedEventId>,
}
+100 -7
View File
@@ -56,7 +56,12 @@ pub(super) fn is_redacted<E: Event>(event: &E) -> bool {
return false;
};
unsigned.redacted_because.is_some()
unsigned.redacted_by.is_some()
}
#[derive(Deserialize)]
struct RedactionField {
redacts: OwnedEventId,
}
#[must_use]
@@ -69,16 +74,104 @@ pub(super) fn redacts_id<E: Event>(
}
if room_version_rules.redaction.content_field_redacts {
event.redacts().map(ToOwned::to_owned)
} else {
event
.get_content::<RoomRedactionEventContent>()
.ok()?
.redacts
.get_content::<RedactionField>()
.map(|r| r.redacts)
.ok()
} else {
event.redacts().map(ToOwned::to_owned)
}
}
#[derive(Deserialize)]
struct ExtractRedactedBecause {
redacted_because: Option<serde::de::IgnoredAny>,
#[serde(rename = "org.continuwuity.redacted_by")]
redacted_by: Option<serde::de::IgnoredAny>,
}
#[cfg(test)]
mod tests {
use ruma::{UInt, events::TimelineEventType, room_version_rules::RoomVersionRules};
use serde_json::json;
use crate::{Event, Pdu, pdu::EventHash};
#[test]
fn v11_redacts_from_content() {
let p = Pdu {
event_id: ruma::owned_event_id!("$v11"),
room_id: Some(ruma::owned_room_id!("!v11:example.com")),
sender: ruma::owned_user_id!("@user:example.com"),
origin: None,
origin_server_ts: UInt::default(),
kind: TimelineEventType::RoomRedaction,
content: serde_json::value::to_raw_value(&json!({"redacts": "$content_redacts"}))
.unwrap(),
sticky: None,
state_key: None,
prev_events: vec![],
depth: UInt::default(),
auth_events: vec![],
redacts: Some(ruma::owned_event_id!("$apex_redacts")),
unsigned: None,
hashes: EventHash { sha256: String::new() },
signatures: None,
};
assert_eq!(
p.redacts_id(&RoomVersionRules::V11),
Some(ruma::owned_event_id!("$content_redacts"))
);
}
#[test]
fn v10_redacts_from_apex() {
let p = Pdu {
event_id: ruma::owned_event_id!("$v11"),
room_id: Some(ruma::owned_room_id!("!v11:example.com")),
sender: ruma::owned_user_id!("@user:example.com"),
origin: None,
origin_server_ts: UInt::default(),
kind: TimelineEventType::RoomRedaction,
content: serde_json::value::to_raw_value(&json!({"redacts": "$content_redacts"}))
.unwrap(),
sticky: None,
state_key: None,
prev_events: vec![],
depth: UInt::default(),
auth_events: vec![],
redacts: Some(ruma::owned_event_id!("$apex_redacts")),
unsigned: None,
hashes: EventHash { sha256: String::new() },
signatures: None,
};
assert_eq!(
p.redacts_id(&RoomVersionRules::V10),
Some(ruma::owned_event_id!("$apex_redacts"))
);
}
#[test]
fn redacts_ignores_non_redaction_event() {
let p = Pdu {
event_id: ruma::owned_event_id!("$v11"),
room_id: Some(ruma::owned_room_id!("!v11:example.com")),
sender: ruma::owned_user_id!("@user:example.com"),
origin: None,
origin_server_ts: UInt::default(),
kind: TimelineEventType::RoomMessage,
content: serde_json::value::to_raw_value(&json!({"redacts": "$content_redacts"}))
.unwrap(),
sticky: None,
state_key: None,
prev_events: vec![],
depth: UInt::default(),
auth_events: vec![],
redacts: Some(ruma::owned_event_id!("$apex_redacts")),
unsigned: None,
hashes: EventHash { sha256: String::new() },
signatures: None,
};
assert_eq!(p.redacts_id(&RoomVersionRules::V11), None);
assert_eq!(p.redacts_id(&RoomVersionRules::V10), None);
}
}
+9 -6
View File
@@ -183,11 +183,6 @@ pub async fn create_event(
.room_state_get(room_id, &event_type.clone().to_string().into(), state_key)
.await
{
unsigned.insert("prev_content".to_owned(), prev_pdu.get_content_as_value());
unsigned.insert(
"prev_sender".to_owned(),
serde_json::to_value(prev_pdu.sender())?,
);
unsigned.insert(
"replaces_state".to_owned(),
serde_json::to_value(prev_pdu.event_id())?,
@@ -195,9 +190,17 @@ pub async fn create_event(
}
}
}
let redacts = match redacts {
// If this isn't a real redaction event, there's no redacts field.
| _ if event_type != TimelineEventType::RoomRedaction || state_key.is_some() => None,
// If the redaction is itself in the event content, we don't set the top-level field.
| Some(_) if room_version_rules.redaction.content_field_redacts => None,
// Otherwise, we do if it's present
| other => other,
};
let pdu = PduEvent {
event_id: ruma::event_id!("$thiswillbefilledinlater").into(),
event_id: ruma::owned_event_id!("$thiswillbefilledinlater"),
room_id: room_id.map(ToOwned::to_owned),
sender: sender.to_owned(),
origin: None,