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