]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/mod.rs
73a7edb6e613d56f3fa9d8ab43a2a0938e39f562
[lemmy.git] / crates / apub / src / activities / deletion / mod.rs
1 use crate::{
2   activities::{
3     community::{announce::GetCommunity, send_activity_in_community},
4     send_lemmy_activity,
5     verify_is_public,
6     verify_mod_action,
7     verify_person,
8     verify_person_in_community,
9   },
10   activity_lists::AnnouncableActivities,
11   local_instance,
12   objects::{
13     comment::ApubComment,
14     community::ApubCommunity,
15     person::ApubPerson,
16     post::ApubPost,
17     private_message::ApubPrivateMessage,
18   },
19   protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
20   ActorType,
21 };
22 use activitypub_federation::{
23   core::object_id::ObjectId,
24   traits::{Actor, ApubObject},
25   utils::verify_domains_match,
26 };
27 use activitystreams_kinds::public;
28 use lemmy_db_schema::{
29   source::{
30     comment::{Comment, CommentUpdateForm},
31     community::{Community, CommunityUpdateForm},
32     person::Person,
33     post::{Post, PostUpdateForm},
34     private_message::{PrivateMessage, PrivateMessageUpdateForm},
35   },
36   traits::Crud,
37 };
38 use lemmy_utils::error::LemmyError;
39 use lemmy_websocket::{
40   send::{
41     send_comment_ws_message_simple,
42     send_community_ws_message,
43     send_pm_ws_message,
44     send_post_ws_message,
45   },
46   LemmyContext,
47   UserOperationCrud,
48 };
49 use std::ops::Deref;
50 use url::Url;
51
52 pub mod delete;
53 pub mod delete_user;
54 pub mod undo_delete;
55
56 /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
57 /// action was done by a normal user.
58 #[tracing::instrument(skip_all)]
59 pub async fn send_apub_delete_in_community(
60   actor: Person,
61   community: Community,
62   object: DeletableObjects,
63   reason: Option<String>,
64   deleted: bool,
65   context: &LemmyContext,
66 ) -> Result<(), LemmyError> {
67   let actor = ApubPerson::from(actor);
68   let activity = if deleted {
69     let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
70     AnnouncableActivities::Delete(delete)
71   } else {
72     let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
73     AnnouncableActivities::UndoDelete(undo)
74   };
75   send_activity_in_community(activity, &actor, &community.into(), vec![], context).await
76 }
77
78 #[tracing::instrument(skip_all)]
79 pub async fn send_apub_delete_private_message(
80   actor: &ApubPerson,
81   pm: PrivateMessage,
82   deleted: bool,
83   context: &LemmyContext,
84 ) -> Result<(), LemmyError> {
85   let recipient_id = pm.recipient_id;
86   let recipient: ApubPerson = Person::read(context.pool(), recipient_id).await?.into();
87
88   let deletable = DeletableObjects::PrivateMessage(Box::new(pm.into()));
89   let inbox = vec![recipient.shared_inbox_or_inbox()];
90   if deleted {
91     let delete = Delete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
92     send_lemmy_activity(context, delete, actor, inbox, true).await?;
93   } else {
94     let undo = UndoDelete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
95     send_lemmy_activity(context, undo, actor, inbox, true).await?;
96   };
97   Ok(())
98 }
99
100 pub enum DeletableObjects {
101   Community(Box<ApubCommunity>),
102   Comment(Box<ApubComment>),
103   Post(Box<ApubPost>),
104   PrivateMessage(Box<ApubPrivateMessage>),
105 }
106
107 impl DeletableObjects {
108   #[tracing::instrument(skip_all)]
109   pub(crate) async fn read_from_db(
110     ap_id: &Url,
111     context: &LemmyContext,
112   ) -> Result<DeletableObjects, LemmyError> {
113     if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? {
114       return Ok(DeletableObjects::Community(Box::new(c)));
115     }
116     if let Some(p) = ApubPost::read_from_apub_id(ap_id.clone(), context).await? {
117       return Ok(DeletableObjects::Post(Box::new(p)));
118     }
119     if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? {
120       return Ok(DeletableObjects::Comment(Box::new(c)));
121     }
122     if let Some(p) = ApubPrivateMessage::read_from_apub_id(ap_id.clone(), context).await? {
123       return Ok(DeletableObjects::PrivateMessage(Box::new(p)));
124     }
125     Err(diesel::NotFound.into())
126   }
127
128   pub(crate) fn id(&self) -> Url {
129     match self {
130       DeletableObjects::Community(c) => c.actor_id(),
131       DeletableObjects::Comment(c) => c.ap_id.clone().into(),
132       DeletableObjects::Post(p) => p.ap_id.clone().into(),
133       DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
134     }
135   }
136 }
137
138 #[tracing::instrument(skip_all)]
139 pub(in crate::activities) async fn verify_delete_activity(
140   activity: &Delete,
141   is_mod_action: bool,
142   context: &LemmyContext,
143   request_counter: &mut i32,
144 ) -> Result<(), LemmyError> {
145   let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
146   match object {
147     DeletableObjects::Community(community) => {
148       verify_is_public(&activity.to, &[])?;
149       if community.local {
150         // can only do this check for local community, in remote case it would try to fetch the
151         // deleted community (which fails)
152         verify_person_in_community(&activity.actor, &community, context, request_counter).await?;
153       }
154       // community deletion is always a mod (or admin) action
155       verify_mod_action(
156         &activity.actor,
157         activity.object.id(),
158         community.id,
159         context,
160         request_counter,
161       )
162       .await?;
163     }
164     DeletableObjects::Post(p) => {
165       verify_is_public(&activity.to, &[])?;
166       verify_delete_post_or_comment(
167         &activity.actor,
168         &p.ap_id.clone().into(),
169         &activity.get_community(context, request_counter).await?,
170         is_mod_action,
171         context,
172         request_counter,
173       )
174       .await?;
175     }
176     DeletableObjects::Comment(c) => {
177       verify_is_public(&activity.to, &[])?;
178       verify_delete_post_or_comment(
179         &activity.actor,
180         &c.ap_id.clone().into(),
181         &activity.get_community(context, request_counter).await?,
182         is_mod_action,
183         context,
184         request_counter,
185       )
186       .await?;
187     }
188     DeletableObjects::PrivateMessage(_) => {
189       verify_person(&activity.actor, context, request_counter).await?;
190       verify_domains_match(activity.actor.inner(), activity.object.id())?;
191     }
192   }
193   Ok(())
194 }
195
196 #[tracing::instrument(skip_all)]
197 async fn verify_delete_post_or_comment(
198   actor: &ObjectId<ApubPerson>,
199   object_id: &Url,
200   community: &ApubCommunity,
201   is_mod_action: bool,
202   context: &LemmyContext,
203   request_counter: &mut i32,
204 ) -> Result<(), LemmyError> {
205   verify_person_in_community(actor, community, context, request_counter).await?;
206   if is_mod_action {
207     verify_mod_action(actor, object_id, community.id, context, request_counter).await?;
208   } else {
209     // domain of post ap_id and post.creator ap_id are identical, so we just check the former
210     verify_domains_match(actor.inner(), object_id)?;
211   }
212   Ok(())
213 }
214
215 /// Write deletion or restoring of an object to the database, and send websocket message.
216 #[tracing::instrument(skip_all)]
217 async fn receive_delete_action(
218   object: &Url,
219   actor: &ObjectId<ApubPerson>,
220   deleted: bool,
221   context: &LemmyContext,
222   request_counter: &mut i32,
223 ) -> Result<(), LemmyError> {
224   match DeletableObjects::read_from_db(object, context).await? {
225     DeletableObjects::Community(community) => {
226       if community.local {
227         let mod_: Person = actor
228           .dereference(context, local_instance(context).await, request_counter)
229           .await?
230           .deref()
231           .clone();
232         let object = DeletableObjects::Community(community.clone());
233         let c: Community = community.deref().deref().clone();
234         send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
235       }
236
237       let community = Community::update(
238         context.pool(),
239         community.id,
240         &CommunityUpdateForm::builder()
241           .deleted(Some(deleted))
242           .build(),
243       )
244       .await?;
245       send_community_ws_message(
246         community.id,
247         UserOperationCrud::DeleteCommunity,
248         None,
249         None,
250         context,
251       )
252       .await?;
253     }
254     DeletableObjects::Post(post) => {
255       if deleted != post.deleted {
256         let deleted_post = Post::update(
257           context.pool(),
258           post.id,
259           &PostUpdateForm::builder().deleted(Some(deleted)).build(),
260         )
261         .await?;
262         send_post_ws_message(
263           deleted_post.id,
264           UserOperationCrud::DeletePost,
265           None,
266           None,
267           context,
268         )
269         .await?;
270       }
271     }
272     DeletableObjects::Comment(comment) => {
273       if deleted != comment.deleted {
274         let deleted_comment = Comment::update(
275           context.pool(),
276           comment.id,
277           &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
278         )
279         .await?;
280         send_comment_ws_message_simple(
281           deleted_comment.id,
282           UserOperationCrud::DeleteComment,
283           context,
284         )
285         .await?;
286       }
287     }
288     DeletableObjects::PrivateMessage(pm) => {
289       let deleted_private_message = PrivateMessage::update(
290         context.pool(),
291         pm.id,
292         &PrivateMessageUpdateForm::builder()
293           .deleted(Some(deleted))
294           .build(),
295       )
296       .await?;
297
298       send_pm_ws_message(
299         deleted_private_message.id,
300         UserOperationCrud::DeletePrivateMessage,
301         None,
302         context,
303       )
304       .await?;
305     }
306   }
307   Ok(())
308 }