tuwunel_service/rooms/state_accessor/
erased.rs1use futures::FutureExt;
8use ruma::{
9 CanonicalJsonObject, CanonicalJsonValue, EventId, RoomId, ServerName, UserId,
10 canonical_json::redact_in_place, events::room::member::MembershipState,
11};
12use tuwunel_core::{
13 implement,
14 matrix::{Event, Pdu},
15 utils::{
16 BoolExt,
17 result::{FlatOk, LogErr},
18 },
19};
20
21#[implement(super::Service)]
27pub async fn erased_for_server(
28 &self,
29 origin: &ServerName,
30 mut pdu: CanonicalJsonObject,
31) -> CanonicalJsonObject {
32 if !self
33 .services
34 .config
35 .enforce_erasure_over_federation
36 {
37 return pdu;
38 }
39
40 let sender = pdu
41 .get("sender")
42 .and_then(CanonicalJsonValue::as_str)
43 .map(<&UserId>::try_from)
44 .flat_ok();
45
46 let Some(sender) = sender else {
47 return pdu;
48 };
49
50 if !self.services.users.is_erased(sender).await {
51 return pdu;
52 }
53
54 let event_id = pdu
55 .get("event_id")
56 .and_then(CanonicalJsonValue::as_str)
57 .map(<&EventId>::try_from)
58 .flat_ok();
59
60 let Some(event_id) = event_id else {
61 return pdu;
62 };
63
64 if self.server_joined_at_pdu(origin, event_id).await {
65 return pdu;
66 }
67
68 let room_id = pdu
69 .get("room_id")
70 .and_then(CanonicalJsonValue::as_str)
71 .map(<&RoomId>::try_from)
72 .flat_ok();
73
74 let Some(room_id) = room_id else {
75 return pdu;
76 };
77
78 let Ok(rules) = self
79 .services
80 .state
81 .get_room_version_rules(room_id)
82 .await
83 .log_err()
84 else {
85 return pdu;
86 };
87
88 redact_in_place(&mut pdu, &rules.redaction, None)
89 .log_err()
90 .ok();
91
92 pdu
93}
94
95#[implement(super::Service)]
101pub async fn erased_view(&self, user_id: &UserId, pdu: &Pdu) -> Option<Pdu> {
102 self.erased_for(user_id, pdu)
103 .map(|erased| erased.then_async(|| self.pruned(pdu)))
104 .flatten()
105 .await
106 .flatten()
107}
108
109#[implement(super::Service)]
116pub async fn erased_for(&self, user_id: &UserId, pdu: &Pdu) -> bool {
117 self.services.users.is_erased(pdu.sender()).await
118 && self
119 .user_membership_at_pdu(user_id, pdu)
120 .map(|membership| membership.ne(&MembershipState::Join))
121 .await
122}
123
124#[implement(super::Service)]
127async fn pruned(&self, pdu: &Pdu) -> Option<Pdu> {
128 let rules = self
129 .services
130 .state
131 .get_room_version_rules(pdu.room_id())
132 .await
133 .log_err()
134 .ok()?;
135
136 pdu.redacted(&rules.redaction).log_err().ok()
137}