]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/mod.rs
Fix verify_mod_action check for remote admin actions (#2190)
[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(
166         &activity.actor,
167         activity.object.id(),
168         &community,
169         context,
170         request_counter,
171       )
172       .await?;
173     }
174     DeletableObjects::Post(p) => {
175       verify_is_public(&activity.to, &[])?;
176       verify_delete_post_or_comment(
177         &activity.actor,
178         &p.ap_id.clone().into(),
179         &activity.get_community(context, request_counter).await?,
180         is_mod_action,
181         context,
182         request_counter,
183       )
184       .await?;
185     }
186     DeletableObjects::Comment(c) => {
187       verify_is_public(&activity.to, &[])?;
188       verify_delete_post_or_comment(
189         &activity.actor,
190         &c.ap_id.clone().into(),
191         &activity.get_community(context, request_counter).await?,
192         is_mod_action,
193         context,
194         request_counter,
195       )
196       .await?;
197     }
198     DeletableObjects::PrivateMessage(_) => {
199       verify_person(&activity.actor, context, request_counter).await?;
200       verify_domains_match(activity.actor.inner(), activity.object.id())?;
201     }
202   }
203   Ok(())
204 }
205
206 #[tracing::instrument(skip_all)]
207 async fn verify_delete_post_or_comment(
208   actor: &ObjectId<ApubPerson>,
209   object_id: &Url,
210   community: &ApubCommunity,
211   is_mod_action: bool,
212   context: &LemmyContext,
213   request_counter: &mut i32,
214 ) -> Result<(), LemmyError> {
215   verify_person_in_community(actor, community, context, request_counter).await?;
216   if is_mod_action {
217     verify_mod_action(actor, object_id, community, context, request_counter).await?;
218   } else {
219     // domain of post ap_id and post.creator ap_id are identical, so we just check the former
220     verify_domains_match(actor.inner(), object_id)?;
221   }
222   Ok(())
223 }
224
225 /// Write deletion or restoring of an object to the database, and send websocket message.
226 #[tracing::instrument(skip_all)]
227 async fn receive_delete_action(
228   object: &Url,
229   actor: &ObjectId<ApubPerson>,
230   deleted: bool,
231   context: &LemmyContext,
232   request_counter: &mut i32,
233 ) -> Result<(), LemmyError> {
234   match DeletableObjects::read_from_db(object, context).await? {
235     DeletableObjects::Community(community) => {
236       if community.local {
237         let mod_: Person = actor
238           .dereference(context, context.client(), request_counter)
239           .await?
240           .deref()
241           .clone();
242         let object = DeletableObjects::Community(community.clone());
243         let c: Community = community.deref().deref().clone();
244         send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
245       }
246
247       let community = blocking(context.pool(), move |conn| {
248         Community::update_deleted(conn, community.id, deleted)
249       })
250       .await??;
251       send_community_ws_message(
252         community.id,
253         UserOperationCrud::DeleteCommunity,
254         None,
255         None,
256         context,
257       )
258       .await?;
259     }
260     DeletableObjects::Post(post) => {
261       if deleted != post.deleted {
262         let deleted_post = blocking(context.pool(), move |conn| {
263           Post::update_deleted(conn, post.id, deleted)
264         })
265         .await??;
266         send_post_ws_message(
267           deleted_post.id,
268           UserOperationCrud::DeletePost,
269           None,
270           None,
271           context,
272         )
273         .await?;
274       }
275     }
276     DeletableObjects::Comment(comment) => {
277       if deleted != comment.deleted {
278         let deleted_comment = blocking(context.pool(), move |conn| {
279           Comment::update_deleted(conn, comment.id, deleted)
280         })
281         .await??;
282         send_comment_ws_message_simple(
283           deleted_comment.id,
284           UserOperationCrud::DeleteComment,
285           context,
286         )
287         .await?;
288       }
289     }
290     DeletableObjects::PrivateMessage(pm) => {
291       let deleted_private_message = blocking(context.pool(), move |conn| {
292         PrivateMessage::update_deleted(conn, pm.id, deleted)
293       })
294       .await??;
295
296       send_pm_ws_message(
297         deleted_private_message.id,
298         UserOperationCrud::DeletePrivateMessage,
299         None,
300         context,
301       )
302       .await?;
303     }
304   }
305   Ok(())
306 }