tuwunel_service/rooms/state_accessor/
erased.rs1use futures::FutureExt;
2use ruma::{
3 CanonicalJsonObject, CanonicalJsonValue, EventId, RoomId, ServerName, UserId,
4 canonical_json::redact_in_place, events::room::member::MembershipState,
5};
6use tuwunel_core::{
7 implement,
8 matrix::{Event, Pdu},
9 utils::{
10 BoolExt,
11 future::BoolExt as _,
12 result::{FlatOk, LogErr},
13 },
14};
15
16#[implement(super::Service)]
20pub async fn erased_for_server(
21 &self,
22 origin: &ServerName,
23 mut pdu: CanonicalJsonObject,
24) -> CanonicalJsonObject {
25 if !self
26 .services
27 .config
28 .enforce_erasure_over_federation
29 {
30 return pdu;
31 }
32
33 let sender = pdu
34 .get("sender")
35 .and_then(CanonicalJsonValue::as_str)
36 .map(<&UserId>::try_from)
37 .flat_ok();
38
39 let Some(sender) = sender else {
40 return pdu;
41 };
42
43 if !self.services.users.is_erased(sender).await {
44 return pdu;
45 }
46
47 let event_id = pdu
48 .get("event_id")
49 .and_then(CanonicalJsonValue::as_str)
50 .map(<&EventId>::try_from)
51 .flat_ok();
52
53 let Some(event_id) = event_id else {
54 return pdu;
55 };
56
57 if self.server_joined_at_pdu(origin, event_id).await {
58 return pdu;
59 }
60
61 let room_id = pdu
62 .get("room_id")
63 .and_then(CanonicalJsonValue::as_str)
64 .map(<&RoomId>::try_from)
65 .flat_ok();
66
67 let Some(room_id) = room_id else {
68 return pdu;
69 };
70
71 let Ok(rules) = self
72 .services
73 .state
74 .get_room_version_rules(room_id)
75 .await
76 .log_err()
77 else {
78 return pdu;
79 };
80
81 redact_in_place(&mut pdu, &rules.redaction, None)
82 .log_err()
83 .ok();
84
85 pdu
86}
87
88#[implement(super::Service)]
94pub async fn erased_view(&self, user_id: &UserId, pdu: &Pdu) -> Option<Pdu> {
95 self.erased_for(user_id, pdu)
96 .map(|erased| erased.then_async(|| self.pruned(pdu)))
97 .flatten()
98 .await
99 .flatten()
100}
101
102#[implement(super::Service)]
109pub async fn erased_for(&self, user_id: &UserId, pdu: &Pdu) -> bool {
110 let is_erased = self.services.users.is_erased(pdu.sender());
111 let not_joined = self
112 .user_membership_at_pdu(user_id, pdu)
113 .map(|membership| membership.ne(&MembershipState::Join));
114
115 is_erased.and(not_joined).await
116}
117
118#[implement(super::Service)]
121async fn pruned(&self, pdu: &Pdu) -> Option<Pdu> {
122 let rules = self
123 .services
124 .state
125 .get_room_version_rules(pdu.room_id())
126 .await
127 .log_err()
128 .ok()?;
129
130 pdu.redacted(&rules.redaction).log_err().ok()
131}