From 20cddf5e81da48f083f3ead6b90c74b4d431e3b6 Mon Sep 17 00:00:00 2001 From: "Aode (lion)" Date: Mon, 15 Nov 2021 18:58:15 -0600 Subject: [PATCH] Support mastodon deletes --- crates/api_crud/src/comment/delete.rs | 60 +++++++++---------- crates/api_crud/src/community/delete.rs | 6 +- crates/api_crud/src/post/delete.rs | 6 +- .../activities/deletion/delete_page.json | 5 +- .../activities/deletion/remove_note.json | 5 +- .../activities/deletion/undo_delete_page.json | 5 +- .../activities/deletion/undo_remove_note.json | 5 +- crates/apub/src/activities/deletion/delete.rs | 27 +++++---- crates/apub/src/activities/deletion/mod.rs | 33 ++++++---- .../src/activities/deletion/undo_delete.rs | 10 ++-- crates/apub/src/activities/mod.rs | 2 +- crates/apub/src/objects/comment.rs | 5 +- crates/apub/src/objects/community.rs | 5 +- crates/apub/src/objects/post.rs | 5 +- .../protocol/activities/deletion/delete.rs | 5 +- .../src/protocol/activities/deletion/mod.rs | 2 +- crates/apub/src/protocol/objects/tombstone.rs | 11 ++-- 17 files changed, 104 insertions(+), 93 deletions(-) diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs index f55119f72..7e920882c 100644 --- a/crates/api_crud/src/comment/delete.rs +++ b/crates/api_crud/src/comment/delete.rs @@ -7,7 +7,7 @@ use lemmy_api_common::{ get_local_user_view_from_jwt, is_mod_or_admin, }; -use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove}; +use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove, DeletableObjects}; use lemmy_db_schema::{ source::{ comment::Comment, @@ -69,20 +69,6 @@ impl PerformCrud for DeleteComment { .await? .map_err(|e| ApiError::err("couldnt_update_comment", e))?; - // Send the apub message - let community = blocking(context.pool(), move |conn| { - Community::read(conn, orig_comment.post.community_id) - }) - .await??; - send_apub_delete( - &local_user_view.person.clone().into(), - &community.clone().into(), - updated_comment.ap_id.clone().into(), - deleted, - context, - ) - .await?; - let post_id = updated_comment.post_id; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let recipient_ids = send_local_notifs( @@ -95,6 +81,20 @@ impl PerformCrud for DeleteComment { ) .await?; + // Send the apub message + let community = blocking(context.pool(), move |conn| { + Community::read(conn, orig_comment.post.community_id) + }) + .await??; + send_apub_delete( + &local_user_view.person.clone().into(), + &community.clone().into(), + DeletableObjects::Comment(Box::new(updated_comment.into())), + deleted, + context, + ) + .await?; + send_comment_ws_message( data.comment_id, UserOperationCrud::DeleteComment, @@ -162,21 +162,6 @@ impl PerformCrud for RemoveComment { }) .await??; - // Send the apub message - let community = blocking(context.pool(), move |conn| { - Community::read(conn, orig_comment.post.community_id) - }) - .await??; - send_apub_remove( - &local_user_view.person.clone().into(), - &community.into(), - updated_comment.ap_id.clone().into(), - data.reason.clone().unwrap_or_else(|| "".to_string()), - removed, - context, - ) - .await?; - let post_id = updated_comment.post_id; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let recipient_ids = send_local_notifs( @@ -189,6 +174,21 @@ impl PerformCrud for RemoveComment { ) .await?; + // Send the apub message + let community = blocking(context.pool(), move |conn| { + Community::read(conn, orig_comment.post.community_id) + }) + .await??; + send_apub_remove( + &local_user_view.person.clone().into(), + &community.into(), + DeletableObjects::Comment(Box::new(updated_comment.into())), + data.reason.clone().unwrap_or_else(|| "".to_string()), + removed, + context, + ) + .await?; + send_comment_ws_message( data.comment_id, UserOperationCrud::RemoveComment, diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs index bf59e786f..aec451926 100644 --- a/crates/api_crud/src/community/delete.rs +++ b/crates/api_crud/src/community/delete.rs @@ -1,7 +1,7 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt, is_admin}; -use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove}; +use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove, DeletableObjects}; use lemmy_db_schema::{ source::{ community::Community, @@ -51,7 +51,7 @@ impl PerformCrud for DeleteCommunity { send_apub_delete( &local_user_view.person.clone().into(), &updated_community.clone().into(), - updated_community.actor_id.clone().into(), + DeletableObjects::Community(Box::new(updated_community.into())), deleted, context, ) @@ -111,7 +111,7 @@ impl PerformCrud for RemoveCommunity { send_apub_remove( &local_user_view.person.clone().into(), &updated_community.clone().into(), - updated_community.actor_id.clone().into(), + DeletableObjects::Community(Box::new(updated_community.into())), data.reason.clone().unwrap_or_else(|| "".to_string()), removed, context, diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs index af6999348..a974feb12 100644 --- a/crates/api_crud/src/post/delete.rs +++ b/crates/api_crud/src/post/delete.rs @@ -8,7 +8,7 @@ use lemmy_api_common::{ is_mod_or_admin, post::*, }; -use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove}; +use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove, DeletableObjects}; use lemmy_db_schema::{ source::{ community::Community, @@ -70,7 +70,7 @@ impl PerformCrud for DeletePost { send_apub_delete( &local_user_view.person.clone().into(), &community.into(), - updated_post.ap_id.into(), + DeletableObjects::Post(Box::new(updated_post.into())), deleted, context, ) @@ -146,7 +146,7 @@ impl PerformCrud for RemovePost { send_apub_remove( &local_user_view.person.clone().into(), &community.into(), - updated_post.ap_id.into(), + DeletableObjects::Post(Box::new(updated_post.into())), data.reason.clone().unwrap_or_else(|| "".to_string()), removed, context, diff --git a/crates/apub/assets/lemmy/activities/deletion/delete_page.json b/crates/apub/assets/lemmy/activities/deletion/delete_page.json index 8dd26a109..6b2aba37c 100644 --- a/crates/apub/assets/lemmy/activities/deletion/delete_page.json +++ b/crates/apub/assets/lemmy/activities/deletion/delete_page.json @@ -3,7 +3,10 @@ "to": [ "https://www.w3.org/ns/activitystreams#Public" ], - "object": "http://ds9.lemmy.ml/post/1", + "object": { + "id": "http://ds9.lemmy.ml/post/1", + "type": "Tombstone" + }, "cc": [ "http://enterprise.lemmy.ml/c/main" ], diff --git a/crates/apub/assets/lemmy/activities/deletion/remove_note.json b/crates/apub/assets/lemmy/activities/deletion/remove_note.json index 8ea354044..07ca4e5cf 100644 --- a/crates/apub/assets/lemmy/activities/deletion/remove_note.json +++ b/crates/apub/assets/lemmy/activities/deletion/remove_note.json @@ -3,7 +3,10 @@ "to": [ "https://www.w3.org/ns/activitystreams#Public" ], - "object": "http://ds9.lemmy.ml/comment/1", + "object": { + "id": "http://ds9.lemmy.ml/comment/1", + "type": "Tombstone" + }, "cc": [ "http://enterprise.lemmy.ml/c/main" ], diff --git a/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json b/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json index 9f824fa1f..d2d5533f0 100644 --- a/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json +++ b/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json @@ -8,7 +8,10 @@ "to": [ "https://www.w3.org/ns/activitystreams#Public" ], - "object": "http://ds9.lemmy.ml/post/1", + "object": { + "id": "http://ds9.lemmy.ml/post/1", + "type": "Tombstone" + }, "cc": [ "http://enterprise.lemmy.ml/c/main" ], diff --git a/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json b/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json index 413cf16b4..e4c9ee26f 100644 --- a/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json +++ b/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json @@ -8,7 +8,10 @@ "to": [ "https://www.w3.org/ns/activitystreams#Public" ], - "object": "http://ds9.lemmy.ml/comment/1", + "object": { + "id": "http://ds9.lemmy.ml/comment/1", + "type": "Tombstone" + }, "cc": [ "http://enterprise.lemmy.ml/c/main" ], diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index cec8ab9a3..b976a1b46 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -50,11 +50,11 @@ impl ActivityHandler for Delete { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_is_public(&self.to, &self.cc)?; + verify_is_public(&self.to, &[])?; verify_activity(&self.id, self.actor.inner(), &context.settings())?; let community = self.get_community(context, request_counter).await?; verify_delete_activity( - &self.object, + &self.object.id, &self.actor, &community, self.summary.is_some(), @@ -78,9 +78,16 @@ impl ActivityHandler for Delete { } else { Some(reason) }; - receive_remove_action(&self.actor, &self.object, reason, context, request_counter).await + receive_remove_action( + &self.actor, + &self.object.id, + reason, + context, + request_counter, + ) + .await } else { - receive_delete_action(&self.object, &self.actor, true, context, request_counter).await + receive_delete_action(&self.object.id, &self.actor, true, context, request_counter).await } } } @@ -88,16 +95,14 @@ impl ActivityHandler for Delete { impl Delete { pub(in crate::activities::deletion) fn new( actor: &ApubPerson, - community: &ApubCommunity, - object_id: Url, + object: DeletableObjects, summary: Option, context: &LemmyContext, ) -> Result { Ok(Delete { actor: ObjectId::new(actor.actor_id()), to: vec![public()], - object: object_id, - cc: vec![community.actor_id()], + object: object.to_tombstone()?, kind: DeleteType::Delete, summary, id: generate_activity_id( @@ -110,11 +115,11 @@ impl Delete { pub(in crate::activities::deletion) async fn send( actor: &ApubPerson, community: &ApubCommunity, - object_id: Url, + object: DeletableObjects, summary: Option, context: &LemmyContext, ) -> Result<(), LemmyError> { - let delete = Delete::new(actor, community, object_id, summary, context)?; + let delete = Delete::new(actor, object, summary, context)?; let delete_id = delete.id.clone(); let activity = AnnouncableActivities::Delete(delete); @@ -201,7 +206,7 @@ impl GetCommunity for Delete { context: &LemmyContext, _request_counter: &mut i32, ) -> Result { - let community_id = match DeletableObjects::read_from_db(&self.object, context).await? { + let community_id = match DeletableObjects::read_from_db(&self.object.id, context).await? { DeletableObjects::Community(c) => c.id, DeletableObjects::Comment(c) => { let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??; diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index ddd607a7c..ab2e7a0ba 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -1,14 +1,13 @@ use crate::{ activities::{verify_mod_action, verify_person_in_community}, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, - protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete}, + protocol::{ + activities::deletion::{delete::Delete, undo_delete::UndoDelete}, + objects::tombstone::Tombstone, + }, }; use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - object_id::ObjectId, - traits::{ActorType, ApubObject}, - verify::verify_domains_match, -}; +use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject, verify::verify_domains_match}; use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post}; use lemmy_utils::LemmyError; use lemmy_websocket::{ @@ -24,14 +23,14 @@ pub mod undo_delete; pub async fn send_apub_delete( actor: &ApubPerson, community: &ApubCommunity, - object_id: Url, + object: DeletableObjects, deleted: bool, context: &LemmyContext, ) -> Result<(), LemmyError> { if deleted { - Delete::send(actor, community, object_id, None, context).await + Delete::send(actor, community, object, None, context).await } else { - UndoDelete::send(actor, community, object_id, None, context).await + UndoDelete::send(actor, community, object, None, context).await } } @@ -40,15 +39,15 @@ pub async fn send_apub_delete( pub async fn send_apub_remove( actor: &ApubPerson, community: &ApubCommunity, - object_id: Url, + object: DeletableObjects, reason: String, removed: bool, context: &LemmyContext, ) -> Result<(), LemmyError> { if removed { - Delete::send(actor, community, object_id, Some(reason), context).await + Delete::send(actor, community, object, Some(reason), context).await } else { - UndoDelete::send(actor, community, object_id, Some(reason), context).await + UndoDelete::send(actor, community, object, Some(reason), context).await } } @@ -74,6 +73,14 @@ impl DeletableObjects { } Err(diesel::NotFound.into()) } + + pub(crate) fn to_tombstone(&self) -> Result { + match self { + DeletableObjects::Community(c) => c.to_tombstone(), + DeletableObjects::Comment(c) => c.to_tombstone(), + DeletableObjects::Post(p) => p.to_tombstone(), + } + } } pub(in crate::activities) async fn verify_delete_activity( @@ -153,7 +160,7 @@ async fn receive_delete_action( DeletableObjects::Community(community) => { if community.local { let mod_ = actor.dereference(context, request_counter).await?; - let object = community.actor_id(); + let object = DeletableObjects::Community(community.clone()); send_apub_delete(&mod_, &community.clone(), object, true, context).await?; } diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index c6e5ddfc0..28ad0f5a3 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -40,7 +40,7 @@ impl ActivityHandler for UndoDelete { self.object.verify(context, request_counter).await?; let community = self.get_community(context, request_counter).await?; verify_delete_activity( - &self.object.object, + &self.object.object.id, &self.actor, &community, self.object.summary.is_some(), @@ -57,10 +57,10 @@ impl ActivityHandler for UndoDelete { request_counter: &mut i32, ) -> Result<(), LemmyError> { if self.object.summary.is_some() { - UndoDelete::receive_undo_remove_action(&self.object.object, context).await + UndoDelete::receive_undo_remove_action(&self.object.object.id, context).await } else { receive_delete_action( - &self.object.object, + &self.object.object.id, &self.actor, false, context, @@ -75,11 +75,11 @@ impl UndoDelete { pub(in crate::activities::deletion) async fn send( actor: &ApubPerson, community: &ApubCommunity, - object_id: Url, + object: DeletableObjects, summary: Option, context: &LemmyContext, ) -> Result<(), LemmyError> { - let object = Delete::new(actor, community, object_id, summary, context)?; + let object = Delete::new(actor, object, summary, context)?; let id = generate_activity_id( UndoType::Undo, diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index b75d37e45..cac5d6e1e 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -117,7 +117,7 @@ fn verify_add_remove_moderator_target( } pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> Result<(), LemmyError> { - if !to.contains(&public()) && !cc.contains(&public()) { + if ![to, cc].iter().any(|set| set.contains(&public())) { return Err(anyhow!("Object is not public").into()); } Ok(()) diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 406838181..a3840e10a 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -132,10 +132,7 @@ impl ApubObject for ApubComment { } fn to_tombstone(&self) -> Result { - Ok(Tombstone::new( - NoteType::Note, - self.updated.unwrap_or(self.published), - )) + Ok(Tombstone::new(self.ap_id.clone().into())) } async fn verify( diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index 87fca8d35..f37d82d8c 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -118,10 +118,7 @@ impl ApubObject for ApubCommunity { } fn to_tombstone(&self) -> Result { - Ok(Tombstone::new( - GroupType::Group, - self.updated.unwrap_or(self.published), - )) + Ok(Tombstone::new(self.actor_id())) } async fn verify( diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 4e34fc88e..44dbe73bd 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -128,10 +128,7 @@ impl ApubObject for ApubPost { } fn to_tombstone(&self) -> Result { - Ok(Tombstone::new( - PageType::Page, - self.updated.unwrap_or(self.published), - )) + Ok(Tombstone::new(self.ap_id.clone().into())) } async fn verify( diff --git a/crates/apub/src/protocol/activities/deletion/delete.rs b/crates/apub/src/protocol/activities/deletion/delete.rs index 7e2275153..26aee276e 100644 --- a/crates/apub/src/protocol/activities/deletion/delete.rs +++ b/crates/apub/src/protocol/activities/deletion/delete.rs @@ -1,4 +1,4 @@ -use crate::objects::person::ApubPerson; +use crate::{objects::person::ApubPerson, protocol::objects::tombstone::Tombstone}; use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed}; use lemmy_apub_lib::object_id::ObjectId; use serde::{Deserialize, Serialize}; @@ -11,8 +11,7 @@ use url::Url; pub struct Delete { pub(crate) actor: ObjectId, pub(crate) to: Vec, - pub(crate) object: Url, - pub(crate) cc: Vec, + pub(crate) object: Tombstone, #[serde(rename = "type")] pub(crate) kind: DeleteType, /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user diff --git a/crates/apub/src/protocol/activities/deletion/mod.rs b/crates/apub/src/protocol/activities/deletion/mod.rs index c77492a32..647c3d507 100644 --- a/crates/apub/src/protocol/activities/deletion/mod.rs +++ b/crates/apub/src/protocol/activities/deletion/mod.rs @@ -11,7 +11,7 @@ mod tests { #[actix_rt::test] #[serial] - async fn test_parse_lemmy_voting() { + async fn test_parse_lemmy_deletion() { test_parse_lemmy_item::("assets/lemmy/activities/deletion/remove_note.json"); test_parse_lemmy_item::("assets/lemmy/activities/deletion/delete_page.json"); diff --git a/crates/apub/src/protocol/objects/tombstone.rs b/crates/apub/src/protocol/objects/tombstone.rs index 2746c5ae6..152066cb5 100644 --- a/crates/apub/src/protocol/objects/tombstone.rs +++ b/crates/apub/src/protocol/objects/tombstone.rs @@ -1,25 +1,22 @@ use activitystreams::object::kind::TombstoneType; -use chrono::{DateTime, FixedOffset, NaiveDateTime}; -use lemmy_utils::utils::convert_datetime; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +use url::Url; #[skip_serializing_none] #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Tombstone { + pub(crate) id: Url, #[serde(rename = "type")] kind: TombstoneType, - former_type: String, - deleted: DateTime, } impl Tombstone { - pub fn new(former_type: T, updated_time: NaiveDateTime) -> Tombstone { + pub fn new(id: Url) -> Tombstone { Tombstone { + id, kind: TombstoneType::Tombstone, - former_type: former_type.to_string(), - deleted: convert_datetime(updated_time), } } }