lemmy/crates/apub_receive/src/activities/deletion/delete.rs
Nutomic c7de1fcf24
Apub inbox rewrite (#1652)
* start to implement apub inbox routing lib

* got something that almost works

* it compiles!

* implemented some more

* move library code to separate crate (most of it)

* convert private message handlers

* convert all comment receivers (except undo comment)

* convert post receiver

* add verify trait

* convert community receivers

* add cc field for all activities which i forgot before

* convert inbox functions, add missing checks

* convert undo like/dislike receivers

* convert undo_delete and undo_remove receivers

* move block/unblock activities

* convert remaining activity receivers

* reimplement http signature verification and other checks

* also use actor type for routing, VerifyActivity and SendActivity traits

* cleanup and restructure apub_receive code

* wip: try to fix activity routing

* implement a (very bad) derive macro for activityhandler

* working activity routing!

* rework pm verify(), fix tests and confirm manually

also remove inbox username check which was broken

* rework following verify(), fix tests and test manually

* fix post/comment create/update, rework voting

* Rewrite remove/delete post/comment, fix tests, test manually

* Rework and fix (un)block user, announce, update post

* some code cleanup

* rework delete/remove activity receivers (still quite messy)

* rewrite, test and fix add/remove mod, update community handlers

* add docs for ActivityHandler derive macro

* dont try to compile macro comments
2021-07-17 12:08:46 -04:00

159 lines
5.2 KiB
Rust

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,
};
use activitystreams::activity::kind::DeleteType;
use lemmy_api_common::blocking;
use lemmy_apub::{
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,
};
use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler, PublicUrl};
use lemmy_db_queries::{
source::{comment::Comment_, community::Community_, post::Post_},
Crud,
};
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
use lemmy_utils::LemmyError;
use lemmy_websocket::{LemmyContext, UserOperationCrud};
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(?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, 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(())
}
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, 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??;
send_community_message(
deleted_community.id,
UserOperationCrud::DeleteCommunity,
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
}
}
}
}
fn common(&self) -> &ActivityCommonFields {
&self.common
}
}
async fn get_post_or_comment_actor_id(
object: &Url,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<Url, LemmyError> {
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()
}
};
Ok(actor_id)
}