]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/mod.rs
Merge pull request #2593 from LemmyNet/refactor-notifications
[lemmy.git] / crates / apub / src / activities / deletion / mod.rs
1 use crate::{
2   activities::{
3     community::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::{
20     activities::deletion::{delete::Delete, undo_delete::UndoDelete},
21     InCommunity,
22   },
23   ActorType,
24   SendActivity,
25 };
26 use activitypub_federation::{
27   core::object_id::ObjectId,
28   traits::{Actor, ApubObject},
29   utils::verify_domains_match,
30 };
31 use activitystreams_kinds::public;
32 use lemmy_api_common::{
33   comment::{CommentResponse, DeleteComment, RemoveComment},
34   community::{CommunityResponse, DeleteCommunity, RemoveCommunity},
35   context::LemmyContext,
36   post::{DeletePost, PostResponse, RemovePost},
37   private_message::{DeletePrivateMessage, PrivateMessageResponse},
38   utils::get_local_user_view_from_jwt,
39   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     UserOperationCrud,
47   },
48 };
49 use lemmy_db_schema::{
50   source::{
51     comment::{Comment, CommentUpdateForm},
52     community::{Community, CommunityUpdateForm},
53     person::Person,
54     post::{Post, PostUpdateForm},
55     private_message::{PrivateMessage, PrivateMessageUpdateForm},
56   },
57   traits::Crud,
58 };
59 use lemmy_utils::error::LemmyError;
60 use std::ops::Deref;
61 use url::Url;
62
63 pub mod delete;
64 pub mod delete_user;
65 pub mod undo_delete;
66
67 #[async_trait::async_trait(?Send)]
68 impl SendActivity for DeletePost {
69   type Response = PostResponse;
70
71   async fn send_activity(
72     request: &Self,
73     response: &Self::Response,
74     context: &LemmyContext,
75   ) -> Result<(), LemmyError> {
76     let local_user_view =
77       get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
78     let community = Community::read(context.pool(), response.post_view.community.id).await?;
79     let deletable = DeletableObjects::Post(Box::new(response.post_view.post.clone().into()));
80     send_apub_delete_in_community(
81       local_user_view.person,
82       community,
83       deletable,
84       None,
85       request.deleted,
86       context,
87     )
88     .await
89   }
90 }
91
92 #[async_trait::async_trait(?Send)]
93 impl SendActivity for RemovePost {
94   type Response = PostResponse;
95
96   async fn send_activity(
97     request: &Self,
98     response: &Self::Response,
99     context: &LemmyContext,
100   ) -> Result<(), LemmyError> {
101     let local_user_view =
102       get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
103     let community = Community::read(context.pool(), response.post_view.community.id).await?;
104     let deletable = DeletableObjects::Post(Box::new(response.post_view.post.clone().into()));
105     send_apub_delete_in_community(
106       local_user_view.person,
107       community,
108       deletable,
109       request.reason.clone().or_else(|| Some(String::new())),
110       request.removed,
111       context,
112     )
113     .await
114   }
115 }
116
117 #[async_trait::async_trait(?Send)]
118 impl SendActivity for DeleteComment {
119   type Response = CommentResponse;
120
121   async fn send_activity(
122     request: &Self,
123     response: &Self::Response,
124     context: &LemmyContext,
125   ) -> Result<(), LemmyError> {
126     let community_id = response.comment_view.community.id;
127     let community = Community::read(context.pool(), community_id).await?;
128     let person = Person::read(context.pool(), response.comment_view.creator.id).await?;
129     let deletable =
130       DeletableObjects::Comment(Box::new(response.comment_view.comment.clone().into()));
131     send_apub_delete_in_community(person, community, deletable, None, request.deleted, context)
132       .await
133   }
134 }
135
136 #[async_trait::async_trait(?Send)]
137 impl SendActivity for RemoveComment {
138   type Response = CommentResponse;
139
140   async fn send_activity(
141     request: &Self,
142     response: &Self::Response,
143     context: &LemmyContext,
144   ) -> Result<(), LemmyError> {
145     let local_user_view =
146       get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
147     let comment = Comment::read(context.pool(), request.comment_id).await?;
148     let community = Community::read(context.pool(), response.comment_view.community.id).await?;
149     let deletable = DeletableObjects::Comment(Box::new(comment.into()));
150     send_apub_delete_in_community(
151       local_user_view.person,
152       community,
153       deletable,
154       request.reason.clone().or_else(|| Some(String::new())),
155       request.removed,
156       context,
157     )
158     .await
159   }
160 }
161
162 #[async_trait::async_trait(?Send)]
163 impl SendActivity for DeletePrivateMessage {
164   type Response = PrivateMessageResponse;
165
166   async fn send_activity(
167     request: &Self,
168     response: &Self::Response,
169     context: &LemmyContext,
170   ) -> Result<(), LemmyError> {
171     let local_user_view =
172       get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
173     send_apub_delete_private_message(
174       &local_user_view.person.into(),
175       response.private_message_view.private_message.clone(),
176       request.deleted,
177       context,
178     )
179     .await
180   }
181 }
182
183 #[async_trait::async_trait(?Send)]
184 impl SendActivity for DeleteCommunity {
185   type Response = CommunityResponse;
186
187   async fn send_activity(
188     request: &Self,
189     _response: &Self::Response,
190     context: &LemmyContext,
191   ) -> Result<(), LemmyError> {
192     let local_user_view =
193       get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
194     let community = Community::read(context.pool(), request.community_id).await?;
195     let deletable = DeletableObjects::Community(Box::new(community.clone().into()));
196     send_apub_delete_in_community(
197       local_user_view.person,
198       community,
199       deletable,
200       None,
201       request.deleted,
202       context,
203     )
204     .await
205   }
206 }
207
208 #[async_trait::async_trait(?Send)]
209 impl SendActivity for RemoveCommunity {
210   type Response = CommunityResponse;
211
212   async fn send_activity(
213     request: &Self,
214     _response: &Self::Response,
215     context: &LemmyContext,
216   ) -> Result<(), LemmyError> {
217     let local_user_view =
218       get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
219     let community = Community::read(context.pool(), request.community_id).await?;
220     let deletable = DeletableObjects::Community(Box::new(community.clone().into()));
221     send_apub_delete_in_community(
222       local_user_view.person,
223       community,
224       deletable,
225       request.reason.clone().or_else(|| Some(String::new())),
226       request.removed,
227       context,
228     )
229     .await
230   }
231 }
232
233 /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
234 /// action was done by a normal user.
235 #[tracing::instrument(skip_all)]
236 async fn send_apub_delete_in_community(
237   actor: Person,
238   community: Community,
239   object: DeletableObjects,
240   reason: Option<String>,
241   deleted: bool,
242   context: &LemmyContext,
243 ) -> Result<(), LemmyError> {
244   let actor = ApubPerson::from(actor);
245   let is_mod_action = reason.is_some();
246   let activity = if deleted {
247     let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
248     AnnouncableActivities::Delete(delete)
249   } else {
250     let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
251     AnnouncableActivities::UndoDelete(undo)
252   };
253   send_activity_in_community(
254     activity,
255     &actor,
256     &community.into(),
257     vec![],
258     is_mod_action,
259     context,
260   )
261   .await
262 }
263
264 #[tracing::instrument(skip_all)]
265 async fn send_apub_delete_private_message(
266   actor: &ApubPerson,
267   pm: PrivateMessage,
268   deleted: bool,
269   context: &LemmyContext,
270 ) -> Result<(), LemmyError> {
271   let recipient_id = pm.recipient_id;
272   let recipient: ApubPerson = Person::read(context.pool(), recipient_id).await?.into();
273
274   let deletable = DeletableObjects::PrivateMessage(Box::new(pm.into()));
275   let inbox = vec![recipient.shared_inbox_or_inbox()];
276   if deleted {
277     let delete = Delete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
278     send_lemmy_activity(context, delete, actor, inbox, true).await?;
279   } else {
280     let undo = UndoDelete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
281     send_lemmy_activity(context, undo, actor, inbox, true).await?;
282   };
283   Ok(())
284 }
285
286 pub enum DeletableObjects {
287   Community(Box<ApubCommunity>),
288   Comment(Box<ApubComment>),
289   Post(Box<ApubPost>),
290   PrivateMessage(Box<ApubPrivateMessage>),
291 }
292
293 impl DeletableObjects {
294   #[tracing::instrument(skip_all)]
295   pub(crate) async fn read_from_db(
296     ap_id: &Url,
297     context: &LemmyContext,
298   ) -> Result<DeletableObjects, LemmyError> {
299     if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? {
300       return Ok(DeletableObjects::Community(Box::new(c)));
301     }
302     if let Some(p) = ApubPost::read_from_apub_id(ap_id.clone(), context).await? {
303       return Ok(DeletableObjects::Post(Box::new(p)));
304     }
305     if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? {
306       return Ok(DeletableObjects::Comment(Box::new(c)));
307     }
308     if let Some(p) = ApubPrivateMessage::read_from_apub_id(ap_id.clone(), context).await? {
309       return Ok(DeletableObjects::PrivateMessage(Box::new(p)));
310     }
311     Err(diesel::NotFound.into())
312   }
313
314   pub(crate) fn id(&self) -> Url {
315     match self {
316       DeletableObjects::Community(c) => c.actor_id(),
317       DeletableObjects::Comment(c) => c.ap_id.clone().into(),
318       DeletableObjects::Post(p) => p.ap_id.clone().into(),
319       DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
320     }
321   }
322 }
323
324 #[tracing::instrument(skip_all)]
325 pub(in crate::activities) async fn verify_delete_activity(
326   activity: &Delete,
327   is_mod_action: bool,
328   context: &LemmyContext,
329   request_counter: &mut i32,
330 ) -> Result<(), LemmyError> {
331   let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
332   match object {
333     DeletableObjects::Community(community) => {
334       verify_is_public(&activity.to, &[])?;
335       if community.local {
336         // can only do this check for local community, in remote case it would try to fetch the
337         // deleted community (which fails)
338         verify_person_in_community(&activity.actor, &community, context, request_counter).await?;
339       }
340       // community deletion is always a mod (or admin) action
341       verify_mod_action(
342         &activity.actor,
343         activity.object.id(),
344         community.id,
345         context,
346         request_counter,
347       )
348       .await?;
349     }
350     DeletableObjects::Post(p) => {
351       verify_is_public(&activity.to, &[])?;
352       verify_delete_post_or_comment(
353         &activity.actor,
354         &p.ap_id.clone().into(),
355         &activity.community(context, request_counter).await?,
356         is_mod_action,
357         context,
358         request_counter,
359       )
360       .await?;
361     }
362     DeletableObjects::Comment(c) => {
363       verify_is_public(&activity.to, &[])?;
364       verify_delete_post_or_comment(
365         &activity.actor,
366         &c.ap_id.clone().into(),
367         &activity.community(context, request_counter).await?,
368         is_mod_action,
369         context,
370         request_counter,
371       )
372       .await?;
373     }
374     DeletableObjects::PrivateMessage(_) => {
375       verify_person(&activity.actor, context, request_counter).await?;
376       verify_domains_match(activity.actor.inner(), activity.object.id())?;
377     }
378   }
379   Ok(())
380 }
381
382 #[tracing::instrument(skip_all)]
383 async fn verify_delete_post_or_comment(
384   actor: &ObjectId<ApubPerson>,
385   object_id: &Url,
386   community: &ApubCommunity,
387   is_mod_action: bool,
388   context: &LemmyContext,
389   request_counter: &mut i32,
390 ) -> Result<(), LemmyError> {
391   verify_person_in_community(actor, community, context, request_counter).await?;
392   if is_mod_action {
393     verify_mod_action(actor, object_id, community.id, context, request_counter).await?;
394   } else {
395     // domain of post ap_id and post.creator ap_id are identical, so we just check the former
396     verify_domains_match(actor.inner(), object_id)?;
397   }
398   Ok(())
399 }
400
401 /// Write deletion or restoring of an object to the database, and send websocket message.
402 #[tracing::instrument(skip_all)]
403 async fn receive_delete_action(
404   object: &Url,
405   actor: &ObjectId<ApubPerson>,
406   deleted: bool,
407   context: &LemmyContext,
408   request_counter: &mut i32,
409 ) -> Result<(), LemmyError> {
410   match DeletableObjects::read_from_db(object, context).await? {
411     DeletableObjects::Community(community) => {
412       if community.local {
413         let mod_: Person = actor
414           .dereference(context, local_instance(context).await, request_counter)
415           .await?
416           .deref()
417           .clone();
418         let object = DeletableObjects::Community(community.clone());
419         let c: Community = community.deref().deref().clone();
420         send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
421       }
422
423       let community = Community::update(
424         context.pool(),
425         community.id,
426         &CommunityUpdateForm::builder()
427           .deleted(Some(deleted))
428           .build(),
429       )
430       .await?;
431       send_community_ws_message(
432         community.id,
433         UserOperationCrud::DeleteCommunity,
434         None,
435         None,
436         context,
437       )
438       .await?;
439     }
440     DeletableObjects::Post(post) => {
441       if deleted != post.deleted {
442         let deleted_post = Post::update(
443           context.pool(),
444           post.id,
445           &PostUpdateForm::builder().deleted(Some(deleted)).build(),
446         )
447         .await?;
448         send_post_ws_message(
449           deleted_post.id,
450           UserOperationCrud::DeletePost,
451           None,
452           None,
453           context,
454         )
455         .await?;
456       }
457     }
458     DeletableObjects::Comment(comment) => {
459       if deleted != comment.deleted {
460         let deleted_comment = Comment::update(
461           context.pool(),
462           comment.id,
463           &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
464         )
465         .await?;
466         send_comment_ws_message_simple(
467           deleted_comment.id,
468           UserOperationCrud::DeleteComment,
469           context,
470         )
471         .await?;
472       }
473     }
474     DeletableObjects::PrivateMessage(pm) => {
475       let deleted_private_message = PrivateMessage::update(
476         context.pool(),
477         pm.id,
478         &PrivateMessageUpdateForm::builder()
479           .deleted(Some(deleted))
480           .build(),
481       )
482       .await?;
483
484       send_pm_ws_message(
485         deleted_private_message.id,
486         UserOperationCrud::DeletePrivateMessage,
487         None,
488         context,
489       )
490       .await?;
491     }
492   }
493   Ok(())
494 }