tuwunel_service/deactivate/
mod.rs1use std::sync::Arc;
2
3use futures::{FutureExt, StreamExt};
4use ruma::{
5 OwnedRoomId, UserId,
6 events::{StateEventType, room::power_levels::RoomPowerLevelsEventContent},
7 profile::ProfileFieldName,
8};
9use tuwunel_core::{Event, Result, info, pdu::PduBuilder, warn};
10
11use crate::profile::Propagation;
12
13pub struct Service {
14 services: Arc<crate::services::OnceServices>,
15}
16
17impl crate::Service for Service {
18 fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
19 Ok(Arc::new(Self { services: args.services.clone() }))
20 }
21
22 fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
23}
24
25impl Service {
26 pub async fn full_deactivate(&self, user_id: &UserId, erase: bool) -> Result {
37 self.services
38 .users
39 .deactivate_account(user_id)
40 .await?;
41
42 self.services
43 .profile
44 .clear_profile_keys(user_id)
45 .await;
46
47 self.services
48 .profile
49 .update_all_rooms(
50 user_id,
51 &[(ProfileFieldName::DisplayName, None), (ProfileFieldName::AvatarUrl, None)],
52 Propagation::All,
53 )
54 .await;
55
56 let all_joined_rooms: Vec<OwnedRoomId> = self
57 .services
58 .state_cache
59 .rooms_joined(user_id)
60 .map(Into::into)
61 .collect()
62 .await;
63
64 for room_id in all_joined_rooms {
65 let state_lock = self.services.state.mutex.lock(&room_id).await;
66
67 let room_power_levels = self
68 .services
69 .state_accessor
70 .get_power_levels(&room_id)
71 .await
72 .ok();
73
74 let user_can_change_self = room_power_levels
75 .as_ref()
76 .is_some_and(|power_levels| {
77 power_levels.user_can_change_user_power_level(user_id, user_id)
78 });
79
80 let user_can_demote_self = user_can_change_self
81 || self
82 .services
83 .state_accessor
84 .room_state_get(&room_id, &StateEventType::RoomCreate, "")
85 .await
86 .is_ok_and(|event| event.sender() == user_id);
87
88 if user_can_demote_self {
89 let mut power_levels_content: RoomPowerLevelsEventContent = room_power_levels
90 .map(TryInto::try_into)
91 .transpose()?
92 .unwrap_or_default();
93
94 power_levels_content.users.remove(user_id);
95
96 match self
98 .services
99 .timeline
100 .build_and_append_pdu(
101 PduBuilder::state(String::new(), &power_levels_content),
102 user_id,
103 &room_id,
104 &state_lock,
105 )
106 .await
107 {
108 | Err(e) => {
109 warn!(%room_id, %user_id, "Failed to demote user's own power level: {e}");
110 },
111 | _ => {
112 info!("Demoted {user_id} in {room_id} as part of account deactivation");
113 },
114 }
115 }
116 }
117
118 let rooms_joined = self
119 .services
120 .state_cache
121 .rooms_joined(user_id)
122 .map(ToOwned::to_owned);
123
124 let rooms_invited = self
125 .services
126 .state_cache
127 .rooms_invited(user_id)
128 .map(ToOwned::to_owned);
129
130 let rooms_knocked = self
131 .services
132 .state_cache
133 .rooms_knocked(user_id)
134 .map(ToOwned::to_owned);
135
136 let all_rooms: Vec<_> = rooms_joined
137 .chain(rooms_invited)
138 .chain(rooms_knocked)
139 .collect()
140 .await;
141
142 if erase {
144 self.services
145 .account_data
146 .erase_user(user_id, None)
147 .await;
148
149 let rooms_left: Vec<OwnedRoomId> = self
150 .services
151 .state_cache
152 .rooms_left(user_id)
153 .map(ToOwned::to_owned)
154 .collect()
155 .await;
156
157 for room_id in all_rooms.iter().chain(rooms_left.iter()) {
158 self.services
159 .account_data
160 .erase_user(user_id, Some(room_id))
161 .await;
162 }
163 }
164
165 for room_id in all_rooms {
166 let state_lock = self.services.state.mutex.lock(&room_id).await;
167
168 if let Err(e) = self
170 .services
171 .membership
172 .leave(user_id, &room_id, None, false, &state_lock)
173 .boxed()
174 .await
175 {
176 warn!(%user_id, "Failed to leave {room_id} remotely: {e}");
177 }
178
179 drop(state_lock);
180
181 self.services
182 .state_cache
183 .forget(&room_id, user_id);
184 }
185
186 Ok(())
187 }
188}