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