3 community::send_activity_in_community,
8 verify_person_in_community,
10 activity_lists::AnnouncableActivities,
13 community::ApubCommunity,
16 private_message::ApubPrivateMessage,
19 activities::deletion::{delete::Delete, undo_delete::UndoDelete},
24 use activitypub_federation::{
26 fetch::object_id::ObjectId,
28 protocol::verification::verify_domains_match,
29 traits::{Actor, Object},
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::get_local_user_view_from_jwt,
38 websocket::UserOperationCrud,
40 use lemmy_db_schema::{
42 comment::{Comment, CommentUpdateForm},
43 community::{Community, CommunityUpdateForm},
45 post::{Post, PostUpdateForm},
46 private_message::{PrivateMessage, PrivateMessageUpdateForm},
50 use lemmy_utils::error::LemmyError;
58 #[async_trait::async_trait]
59 impl SendActivity for DeletePost {
60 type Response = PostResponse;
62 async fn send_activity(
64 response: &Self::Response,
65 context: &Data<LemmyContext>,
66 ) -> Result<(), LemmyError> {
68 get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
69 let community = Community::read(context.pool(), response.post_view.community.id).await?;
70 let deletable = DeletableObjects::Post(response.post_view.post.clone().into());
71 send_apub_delete_in_community(
72 local_user_view.person,
83 #[async_trait::async_trait]
84 impl SendActivity for RemovePost {
85 type Response = PostResponse;
87 async fn send_activity(
89 response: &Self::Response,
90 context: &Data<LemmyContext>,
91 ) -> Result<(), LemmyError> {
93 get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
94 let community = Community::read(context.pool(), response.post_view.community.id).await?;
95 let deletable = DeletableObjects::Post(response.post_view.post.clone().into());
96 send_apub_delete_in_community(
97 local_user_view.person,
100 request.reason.clone().or_else(|| Some(String::new())),
108 #[async_trait::async_trait]
109 impl SendActivity for DeleteComment {
110 type Response = CommentResponse;
112 async fn send_activity(
114 response: &Self::Response,
115 context: &Data<LemmyContext>,
116 ) -> Result<(), LemmyError> {
117 let community_id = response.comment_view.community.id;
118 let community = Community::read(context.pool(), community_id).await?;
119 let person = Person::read(context.pool(), response.comment_view.creator.id).await?;
120 let deletable = DeletableObjects::Comment(response.comment_view.comment.clone().into());
121 send_apub_delete_in_community(person, community, deletable, None, request.deleted, context)
126 #[async_trait::async_trait]
127 impl SendActivity for RemoveComment {
128 type Response = CommentResponse;
130 async fn send_activity(
132 response: &Self::Response,
133 context: &Data<LemmyContext>,
134 ) -> Result<(), LemmyError> {
135 let local_user_view =
136 get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
137 let comment = Comment::read(context.pool(), request.comment_id).await?;
138 let community = Community::read(context.pool(), response.comment_view.community.id).await?;
139 let deletable = DeletableObjects::Comment(comment.into());
140 send_apub_delete_in_community(
141 local_user_view.person,
144 request.reason.clone().or_else(|| Some(String::new())),
152 #[async_trait::async_trait]
153 impl SendActivity for DeletePrivateMessage {
154 type Response = PrivateMessageResponse;
156 async fn send_activity(
158 response: &Self::Response,
159 context: &Data<LemmyContext>,
160 ) -> Result<(), LemmyError> {
161 let local_user_view =
162 get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
163 send_apub_delete_private_message(
164 &local_user_view.person.into(),
165 response.private_message_view.private_message.clone(),
173 #[async_trait::async_trait]
174 impl SendActivity for DeleteCommunity {
175 type Response = CommunityResponse;
177 async fn send_activity(
179 _response: &Self::Response,
180 context: &Data<LemmyContext>,
181 ) -> Result<(), LemmyError> {
182 let local_user_view =
183 get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
184 let community = Community::read(context.pool(), request.community_id).await?;
185 let deletable = DeletableObjects::Community(community.clone().into());
186 send_apub_delete_in_community(
187 local_user_view.person,
198 #[async_trait::async_trait]
199 impl SendActivity for RemoveCommunity {
200 type Response = CommunityResponse;
202 async fn send_activity(
204 _response: &Self::Response,
205 context: &Data<LemmyContext>,
206 ) -> Result<(), LemmyError> {
207 let local_user_view =
208 get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
209 let community = Community::read(context.pool(), request.community_id).await?;
210 let deletable = DeletableObjects::Community(community.clone().into());
211 send_apub_delete_in_community(
212 local_user_view.person,
215 request.reason.clone().or_else(|| Some(String::new())),
223 /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
224 /// action was done by a normal user.
225 #[tracing::instrument(skip_all)]
226 async fn send_apub_delete_in_community(
228 community: Community,
229 object: DeletableObjects,
230 reason: Option<String>,
232 context: &Data<LemmyContext>,
233 ) -> Result<(), LemmyError> {
234 let actor = ApubPerson::from(actor);
235 let is_mod_action = reason.is_some();
236 let activity = if deleted {
237 let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
238 AnnouncableActivities::Delete(delete)
240 let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
241 AnnouncableActivities::UndoDelete(undo)
243 send_activity_in_community(
254 #[tracing::instrument(skip_all)]
255 async fn send_apub_delete_private_message(
259 context: &Data<LemmyContext>,
260 ) -> Result<(), LemmyError> {
261 let recipient_id = pm.recipient_id;
262 let recipient: ApubPerson = Person::read(context.pool(), recipient_id).await?.into();
264 let deletable = DeletableObjects::PrivateMessage(pm.into());
265 let inbox = vec![recipient.shared_inbox_or_inbox()];
267 let delete = Delete::new(actor, deletable, recipient.id(), None, None, context)?;
268 send_lemmy_activity(context, delete, actor, inbox, true).await?;
270 let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, context)?;
271 send_lemmy_activity(context, undo, actor, inbox, true).await?;
276 pub enum DeletableObjects {
277 Community(ApubCommunity),
278 Comment(ApubComment),
280 PrivateMessage(ApubPrivateMessage),
283 impl DeletableObjects {
284 #[tracing::instrument(skip_all)]
285 pub(crate) async fn read_from_db(
287 context: &Data<LemmyContext>,
288 ) -> Result<DeletableObjects, LemmyError> {
289 if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
290 return Ok(DeletableObjects::Community(c));
292 if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
293 return Ok(DeletableObjects::Post(p));
295 if let Some(c) = ApubComment::read_from_id(ap_id.clone(), context).await? {
296 return Ok(DeletableObjects::Comment(c));
298 if let Some(p) = ApubPrivateMessage::read_from_id(ap_id.clone(), context).await? {
299 return Ok(DeletableObjects::PrivateMessage(p));
301 Err(diesel::NotFound.into())
304 pub(crate) fn id(&self) -> Url {
306 DeletableObjects::Community(c) => c.id(),
307 DeletableObjects::Comment(c) => c.ap_id.clone().into(),
308 DeletableObjects::Post(p) => p.ap_id.clone().into(),
309 DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
314 #[tracing::instrument(skip_all)]
315 pub(in crate::activities) async fn verify_delete_activity(
318 context: &Data<LemmyContext>,
319 ) -> Result<(), LemmyError> {
320 let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
322 DeletableObjects::Community(community) => {
323 verify_is_public(&activity.to, &[])?;
325 // can only do this check for local community, in remote case it would try to fetch the
326 // deleted community (which fails)
327 verify_person_in_community(&activity.actor, &community, context).await?;
329 // community deletion is always a mod (or admin) action
330 verify_mod_action(&activity.actor, activity.object.id(), community.id, context).await?;
332 DeletableObjects::Post(p) => {
333 verify_is_public(&activity.to, &[])?;
334 verify_delete_post_or_comment(
336 &p.ap_id.clone().into(),
337 &activity.community(context).await?,
343 DeletableObjects::Comment(c) => {
344 verify_is_public(&activity.to, &[])?;
345 verify_delete_post_or_comment(
347 &c.ap_id.clone().into(),
348 &activity.community(context).await?,
354 DeletableObjects::PrivateMessage(_) => {
355 verify_person(&activity.actor, context).await?;
356 verify_domains_match(activity.actor.inner(), activity.object.id())?;
362 #[tracing::instrument(skip_all)]
363 async fn verify_delete_post_or_comment(
364 actor: &ObjectId<ApubPerson>,
366 community: &ApubCommunity,
368 context: &Data<LemmyContext>,
369 ) -> Result<(), LemmyError> {
370 verify_person_in_community(actor, community, context).await?;
372 verify_mod_action(actor, object_id, community.id, context).await?;
374 // domain of post ap_id and post.creator ap_id are identical, so we just check the former
375 verify_domains_match(actor.inner(), object_id)?;
380 /// Write deletion or restoring of an object to the database, and send websocket message.
381 #[tracing::instrument(skip_all)]
382 async fn receive_delete_action(
384 actor: &ObjectId<ApubPerson>,
386 context: &Data<LemmyContext>,
387 ) -> Result<(), LemmyError> {
388 match DeletableObjects::read_from_db(object, context).await? {
389 DeletableObjects::Community(community) => {
391 let mod_: Person = actor.dereference(context).await?.deref().clone();
392 let object = DeletableObjects::Community(community.clone());
393 let c: Community = community.deref().deref().clone();
394 send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
397 let community = Community::update(
400 &CommunityUpdateForm::builder()
401 .deleted(Some(deleted))
406 .send_community_ws_message(
407 &UserOperationCrud::DeleteCommunity,
414 DeletableObjects::Post(post) => {
415 if deleted != post.deleted {
416 let deleted_post = Post::update(
419 &PostUpdateForm::builder().deleted(Some(deleted)).build(),
423 .send_post_ws_message(&UserOperationCrud::DeletePost, deleted_post.id, None, None)
427 DeletableObjects::Comment(comment) => {
428 if deleted != comment.deleted {
429 let deleted_comment = Comment::update(
432 &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
436 .send_comment_ws_message_simple(&UserOperationCrud::DeleteComment, deleted_comment.id)
440 DeletableObjects::PrivateMessage(pm) => {
441 let deleted_private_message = PrivateMessage::update(
444 &PrivateMessageUpdateForm::builder()
445 .deleted(Some(deleted))
452 &UserOperationCrud::DeletePrivateMessage,
453 deleted_private_message.id,