X-Git-Url: http://these/git/?a=blobdiff_plain;f=crates%2Fapub%2Fsrc%2Factivities%2Fdeletion%2Fdelete.rs;h=06f7463ae0f12dfe163677153e726aaf846da746;hb=3471f3533cb724b2cf6953d563aadfcc9f66c1d2;hp=d377a6696bdf731945dc93f6694e23527022b090;hpb=353a1fe0a0622898560cdb3eb96f59b9df140610;p=lemmy.git diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index d377a669..06f7463a 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -1,159 +1,163 @@ use crate::{ activities::{ - comment::send_websocket_message as send_comment_message, - community::send_websocket_message as send_community_message, - post::send_websocket_message as send_post_message, - verify_activity, - verify_mod_action, - verify_person_in_community, + deletion::{receive_delete_action, verify_delete_activity, DeletableObjects}, + generate_activity_id, }, - fetcher::{ - community::get_or_fetch_and_upsert_community, - objects::get_or_fetch_and_insert_post_or_comment, - person::get_or_fetch_and_upsert_person, - }, - ActorType, - CommunityType, - PostOrComment, + insert_received_activity, + objects::person::ApubPerson, + protocol::{activities::deletion::delete::Delete, IdOrNestedObject}, }; -use activitystreams::activity::kind::DeleteType; -use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler}; -use lemmy_db_queries::{ - source::{comment::Comment_, community::Community_, post::Post_}, - Crud, +use activitypub_federation::{config::Data, kinds::activity::DeleteType, traits::ActivityHandler}; +use lemmy_api_common::{context::LemmyContext, utils::sanitize_html_opt}; +use lemmy_db_schema::{ + source::{ + comment::{Comment, CommentUpdateForm}, + community::{Community, CommunityUpdateForm}, + moderator::{ + ModRemoveComment, + ModRemoveCommentForm, + ModRemoveCommunity, + ModRemoveCommunityForm, + ModRemovePost, + ModRemovePostForm, + }, + post::{Post, PostUpdateForm}, + }, + traits::Crud, }; -use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; -use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_utils::error::{LemmyError, LemmyErrorType}; use url::Url; -/// This is very confusing, because there are four distinct cases to handle: -/// - user deletes their post -/// - user deletes their comment -/// - remote community mod deletes local community -/// - remote community deletes itself (triggered by a mod) -/// -/// TODO: we should probably change how community deletions work to simplify this. Probably by -/// wrapping it in an announce just like other activities, instead of having the community send it. -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DeletePostCommentOrCommunity { - to: PublicUrl, - pub(in crate::activities::deletion) object: Url, - cc: [Url; 1], - #[serde(rename = "type")] - kind: DeleteType, - #[serde(flatten)] - common: ActivityCommonFields, -} +#[async_trait::async_trait] +impl ActivityHandler for Delete { + type DataType = LemmyContext; + type Error = LemmyError; -#[async_trait::async_trait(?Send)] -impl ActivityHandler for DeletePostCommentOrCommunity { - async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - let object_community = - get_or_fetch_and_upsert_community(&self.object, context, request_counter).await; - // deleting a community (set counter 0 to only fetch from local db) - if object_community.is_ok() { - verify_mod_action(&self.common.actor, self.object.clone(), context).await?; - } - // deleting a post or comment - else { - verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter) - .await?; - let object_creator = - get_post_or_comment_actor_id(&self.object, context, request_counter).await?; - verify_urls_match(&self.common.actor, &object_creator)?; - } - Ok(()) + fn id(&self) -> &Url { + &self.id } - async fn receive( - self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let object_community = - get_or_fetch_and_upsert_community(&self.object, context, request_counter).await; - // deleting a community - if let Ok(community) = object_community { - if community.local { - // repeat these checks just to be sure - verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter) - .await?; - verify_mod_action(&self.common.actor, self.object.clone(), context).await?; - let mod_ = - get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; - community.send_delete(mod_, context).await?; - } - let deleted_community = blocking(context.pool(), move |conn| { - Community::update_deleted(conn, community.id, true) - }) - .await??; + fn actor(&self) -> &Url { + self.actor.inner() + } + + #[tracing::instrument(skip_all)] + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + insert_received_activity(&self.id, context).await?; + verify_delete_activity(self, self.summary.is_some(), context).await?; + Ok(()) + } - send_community_message( - deleted_community.id, - UserOperationCrud::DeleteCommunity, + #[tracing::instrument(skip_all)] + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + if let Some(reason) = self.summary { + // We set reason to empty string if it doesn't exist, to distinguish between delete and + // remove. Here we change it back to option, so we don't write it to db. + let reason = if reason.is_empty() { + None + } else { + Some(reason) + }; + receive_remove_action( + &self.actor.dereference(context).await?, + self.object.id(), + reason, context, ) .await - } - // deleting a post or comment - else { - match get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await? { - PostOrComment::Post(post) => { - let deleted_post = blocking(context.pool(), move |conn| { - Post::update_deleted(conn, post.id, true) - }) - .await??; - send_post_message(deleted_post.id, UserOperationCrud::EditPost, context).await - } - PostOrComment::Comment(comment) => { - let deleted_comment = blocking(context.pool(), move |conn| { - Comment::update_deleted(conn, comment.id, true) - }) - .await??; - send_comment_message( - deleted_comment.id, - vec![], - UserOperationCrud::EditComment, - context, - ) - .await - } - } + } else { + receive_delete_action(self.object.id(), &self.actor, true, context).await } } +} - fn common(&self) -> &ActivityCommonFields { - &self.common +impl Delete { + pub(in crate::activities::deletion) fn new( + actor: &ApubPerson, + object: DeletableObjects, + to: Url, + community: Option<&Community>, + summary: Option, + context: &Data, + ) -> Result { + let id = generate_activity_id( + DeleteType::Delete, + &context.settings().get_protocol_and_hostname(), + )?; + let cc: Option = community.map(|c| c.actor_id.clone().into()); + Ok(Delete { + actor: actor.actor_id.clone().into(), + to: vec![to], + object: IdOrNestedObject::Id(object.id()), + cc: cc.into_iter().collect(), + kind: DeleteType::Delete, + summary, + id, + audience: community.map(|c| c.actor_id.clone().into()), + }) } } -async fn get_post_or_comment_actor_id( +#[tracing::instrument(skip_all)] +pub(in crate::activities) async fn receive_remove_action( + actor: &ApubPerson, object: &Url, - context: &LemmyContext, - request_counter: &mut i32, -) -> Result { - let actor_id = - match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? { - PostOrComment::Post(post) => { - let creator_id = post.creator_id; - blocking(context.pool(), move |conn| Person::read(conn, creator_id)) - .await?? - .actor_id() - } - PostOrComment::Comment(comment) => { - let creator_id = comment.creator_id; - blocking(context.pool(), move |conn| Person::read(conn, creator_id)) - .await?? - .actor_id() + reason: Option, + context: &Data, +) -> Result<(), LemmyError> { + let reason = sanitize_html_opt(&reason); + + match DeletableObjects::read_from_db(object, context).await? { + DeletableObjects::Community(community) => { + if community.local { + return Err(LemmyErrorType::OnlyLocalAdminCanRemoveCommunity)?; } - }; - Ok(actor_id) + let form = ModRemoveCommunityForm { + mod_person_id: actor.id, + community_id: community.id, + removed: Some(true), + reason, + expires: None, + }; + ModRemoveCommunity::create(&mut context.pool(), &form).await?; + Community::update( + &mut context.pool(), + community.id, + &CommunityUpdateForm::builder().removed(Some(true)).build(), + ) + .await?; + } + DeletableObjects::Post(post) => { + let form = ModRemovePostForm { + mod_person_id: actor.id, + post_id: post.id, + removed: Some(true), + reason, + }; + ModRemovePost::create(&mut context.pool(), &form).await?; + Post::update( + &mut context.pool(), + post.id, + &PostUpdateForm::builder().removed(Some(true)).build(), + ) + .await?; + } + DeletableObjects::Comment(comment) => { + let form = ModRemoveCommentForm { + mod_person_id: actor.id, + comment_id: comment.id, + removed: Some(true), + reason, + }; + ModRemoveComment::create(&mut context.pool(), &form).await?; + Comment::update( + &mut context.pool(), + comment.id, + &CommentUpdateForm::builder().removed(Some(true)).build(), + ) + .await?; + } + DeletableObjects::PrivateMessage(_) => unimplemented!(), + } + Ok(()) }