arminius_lib_events/
redaction.rs1use std::collections::BTreeMap;
11
12use ruma::{
13 CanonicalJsonObject, CanonicalJsonValue, MilliSecondsSinceUnixEpoch, UserId,
14 canonical_json::{self, RedactedBecause},
15 room_version_rules::{RedactionRules, RoomVersionRules},
16};
17use serde_json::value::RawValue as RawJsonValue;
18
19use crate::{
20 ContextualizedFields as _,
21 errors::{JsonSerDeError, RedactionError, ValidationError},
22 states::{Signed, Verified},
23 traits::Redactable,
24 transitions::RoomEvent,
25};
26
27const fn get_redaction_rules(room_version: &RoomVersionRules) -> &RedactionRules {
32 &room_version.redaction
33}
34
35fn build_redacted_because(
52 redactor: &UserId,
53 reason: Option<String>,
54 timestamp: MilliSecondsSinceUnixEpoch,
55) -> RedactedBecause {
56 let mut content = BTreeMap::new();
57 if let Some(reason_text) = reason {
58 content.insert("reason".to_owned(), CanonicalJsonValue::String(reason_text));
59 }
60
61 let mut redaction = BTreeMap::new();
62 redaction.insert("type".to_owned(), CanonicalJsonValue::String("m.room.redaction".to_owned()));
63 redaction.insert("sender".to_owned(), CanonicalJsonValue::String(redactor.to_string()));
64 redaction.insert("content".to_owned(), CanonicalJsonValue::Object(content));
65 redaction.insert(
66 "origin_server_ts".to_owned(),
67 CanonicalJsonValue::Integer(
68 ruma::Int::try_from(u64::from(timestamp.0)).unwrap_or(ruma::Int::MIN),
69 ),
70 );
71
72 RedactedBecause::from_json(redaction)
73}
74
75fn redact_event_content(
79 event_object: CanonicalJsonObject,
80 room_version: &RoomVersionRules,
81 redacted_because: &RedactedBecause,
82) -> Result<Box<serde_json::value::RawValue>, ValidationError> {
83 let redaction_rules = get_redaction_rules(room_version);
84
85 let redacted_object =
87 canonical_json::redact(event_object, redaction_rules, Some(redacted_because.clone()))
88 .map_err(RedactionError::Ruma)?;
89
90 let content_value = redacted_object.get("content").ok_or(RedactionError::MissingContent)?;
92
93 let content_json =
94 serde_json::to_string(&content_value).map_err(JsonSerDeError::JsonSerialization)?;
95
96 RawJsonValue::from_string(content_json).map_err(|serde_json_err| {
97 ValidationError::JsonSerDe(JsonSerDeError::ContentDeserialization(serde_json_err))
98 })
99}
100
101impl Redactable for RoomEvent<Signed> {
102 fn redact(&mut self, redactor: &UserId, reason: Option<String>) -> Result<(), ValidationError> {
103 let redacted_because =
104 build_redacted_because(redactor, reason, self.state.contextualized.origin_server_ts);
105 let event_object = self
106 .state
107 .build_event_object()
108 .map_err(|error| ValidationError::JsonCanonicalization(error.to_string()))?;
109 let new_content =
110 redact_event_content(event_object, &self.room_version().rules(), &redacted_because)?;
111 self.state
112 .contextualized
113 .draft
114 .redact(new_content)
115 .map_err(JsonSerDeError::ContentDeserialization)?;
116
117 Ok(())
118 }
119}
120
121impl Redactable for RoomEvent<Verified> {
122 fn redact(&mut self, redactor: &UserId, reason: Option<String>) -> Result<(), ValidationError> {
123 let redacted_because = build_redacted_because(
124 redactor,
125 reason,
126 self.state.signed.contextualized.origin_server_ts,
127 );
128 let event_object = self
129 .state
130 .signed
131 .contextualized
132 .build_canonical_event_object()
133 .map_err(|error| ValidationError::JsonCanonicalization(error.to_string()))?;
134 let new_content =
135 redact_event_content(event_object, &self.room_version().rules(), &redacted_because)?;
136 self.state
137 .signed
138 .contextualized
139 .draft
140 .redact(new_content)
141 .map_err(JsonSerDeError::ContentDeserialization)?;
142
143 Ok(())
144 }
145}