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