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