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