From 99f55a462766b4869ea65b503c710365873e5ca4 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Sat, 10 Jul 2021 04:11:21 +0200 Subject: [PATCH] fix post/comment create/update, rework voting --- crates/apub/src/fetcher/objects.rs | 24 ++- crates/apub/src/objects/comment.rs | 15 +- .../src/activities/comment/create.rs | 36 ++++- .../src/activities/comment/like.rs | 46 ------ .../src/activities/comment/mod.rs | 80 +-------- .../src/activities/comment/remove.rs | 2 +- .../src/activities/comment/undo_dislike.rs | 51 ------ .../src/activities/comment/undo_remove.rs | 2 +- .../src/activities/comment/update.rs | 28 +++- .../src/activities/community/add_mod.rs | 2 +- .../src/activities/community/announce.rs | 52 +++--- .../src/activities/community/block_user.rs | 2 +- .../src/activities/community/remove_mod.rs | 2 +- .../activities/community/undo_block_user.rs | 2 +- crates/apub_receive/src/activities/mod.rs | 35 +++- .../src/activities/post/create.rs | 27 +++- .../src/activities/post/dislike.rs | 46 ------ .../apub_receive/src/activities/post/mod.rs | 65 +------- .../src/activities/post/remove.rs | 2 +- .../src/activities/post/undo_like.rs | 50 ------ .../src/activities/post/undo_remove.rs | 2 +- .../src/activities/post/update.rs | 62 +++---- .../{comment => post_or_comment}/dislike.rs | 28 ++-- .../{post => post_or_comment}/like.rs | 28 ++-- .../src/activities/post_or_comment/mod.rs | 5 + .../{post => post_or_comment}/undo_dislike.rs | 26 +-- .../{comment => post_or_comment}/undo_like.rs | 26 +-- .../src/activities/post_or_comment/voting.rs | 152 ++++++++++++++++++ crates/apub_receive/src/http/inbox_enums.rs | 54 +++---- crates/apub_receive/src/http/mod.rs | 27 +--- scripts/compilation_benchmark.sh | 6 +- 31 files changed, 462 insertions(+), 523 deletions(-) delete mode 100644 crates/apub_receive/src/activities/comment/like.rs delete mode 100644 crates/apub_receive/src/activities/comment/undo_dislike.rs delete mode 100644 crates/apub_receive/src/activities/post/dislike.rs delete mode 100644 crates/apub_receive/src/activities/post/undo_like.rs rename crates/apub_receive/src/activities/{comment => post_or_comment}/dislike.rs (53%) rename crates/apub_receive/src/activities/{post => post_or_comment}/like.rs (52%) create mode 100644 crates/apub_receive/src/activities/post_or_comment/mod.rs rename crates/apub_receive/src/activities/{post => post_or_comment}/undo_dislike.rs (54%) rename crates/apub_receive/src/activities/{comment => post_or_comment}/undo_like.rs (54%) create mode 100644 crates/apub_receive/src/activities/post_or_comment/voting.rs diff --git a/crates/apub/src/fetcher/objects.rs b/crates/apub/src/fetcher/objects.rs index 41bc075a0..af8a59f76 100644 --- a/crates/apub/src/fetcher/objects.rs +++ b/crates/apub/src/fetcher/objects.rs @@ -1,4 +1,10 @@ -use crate::{fetcher::fetch::fetch_remote_object, objects::FromApub, NoteExt, PageExt}; +use crate::{ + fetcher::fetch::fetch_remote_object, + objects::FromApub, + NoteExt, + PageExt, + PostOrComment, +}; use anyhow::anyhow; use diesel::result::Error::NotFound; use lemmy_api_common::blocking; @@ -89,3 +95,19 @@ pub async fn get_or_fetch_and_insert_comment( Err(e) => Err(e.into()), } } + +pub async fn get_or_fetch_and_insert_post_or_comment( + ap_id: &Url, + context: &LemmyContext, + recursion_counter: &mut i32, +) -> Result { + Ok( + match get_or_fetch_and_insert_post(ap_id, context, recursion_counter).await { + Ok(p) => PostOrComment::Post(Box::new(p)), + Err(_) => { + let c = get_or_fetch_and_insert_comment(ap_id, context, recursion_counter).await?; + PostOrComment::Comment(Box::new(c)) + } + }, + ) +} diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 361da4629..7b181eff4 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -123,17 +123,7 @@ impl FromApub for Comment { let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; check_object_for_community_or_site_ban(note, post.community_id, context, request_counter) .await?; - if post.locked { - // This is not very efficient because a comment gets inserted just to be deleted right - // afterwards, but it seems to be the easiest way to implement it. - blocking(context.pool(), move |conn| { - Comment::delete(conn, comment.id) - }) - .await??; - Err(anyhow!("Post is locked").into()) - } else { - Ok(comment) - } + Ok(comment) } } @@ -174,6 +164,9 @@ impl FromApubToForm for CommentForm { request_counter, )) .await?; + if post.locked { + return Err(anyhow!("Post is locked").into()); + } // The 2nd item, if it exists, is the parent comment apub_id // For deeply nested comments, FromApub automatically gets called recursively diff --git a/crates/apub_receive/src/activities/comment/create.rs b/crates/apub_receive/src/activities/comment/create.rs index 4259a956d..498bfe39e 100644 --- a/crates/apub_receive/src/activities/comment/create.rs +++ b/crates/apub_receive/src/activities/comment/create.rs @@ -1,7 +1,16 @@ -use crate::activities::comment::{get_notif_recipients, send_websocket_message}; +use crate::activities::{ + comment::{get_notif_recipients, send_websocket_message}, + verify_activity, + verify_person_in_community, +}; use activitystreams::{activity::kind::CreateType, base::BaseExt}; -use lemmy_apub::{check_is_apub_id_valid, objects::FromApub, NoteExt}; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; +use lemmy_apub::{objects::FromApub, NoteExt}; +use lemmy_apub_lib::{ + verify_domains_match_opt, + ActivityCommonFields, + ActivityHandlerNew, + PublicUrl, +}; use lemmy_db_schema::source::comment::Comment; use lemmy_utils::LemmyError; use lemmy_websocket::{LemmyContext, UserOperationCrud}; @@ -21,10 +30,21 @@ pub struct CreateComment { #[async_trait::async_trait(?Send)] impl ActivityHandlerNew for CreateComment { - async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - self.object.id(self.common.actor.as_str())?; - check_is_apub_id_valid(&self.common.actor, false) + async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + dbg!("1"); + verify_activity(self.common())?; + dbg!("2"); + verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?; + dbg!("3"); + verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?; + dbg!("4"); + // TODO: should add a check that the correct community is in cc (probably needs changes to + // comment deserialization) + Ok(()) } async fn receive( @@ -32,6 +52,7 @@ impl ActivityHandlerNew for CreateComment { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { + dbg!("5"); let comment = Comment::from_apub( &self.object, context, @@ -40,6 +61,7 @@ impl ActivityHandlerNew for CreateComment { false, ) .await?; + dbg!("6"); let recipients = get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?; send_websocket_message( diff --git a/crates/apub_receive/src/activities/comment/like.rs b/crates/apub_receive/src/activities/comment/like.rs deleted file mode 100644 index e8c9956dc..000000000 --- a/crates/apub_receive/src/activities/comment/like.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::activities::comment::like_or_dislike_comment; -use activitystreams::activity::kind::LikeType; -use lemmy_apub::check_is_apub_id_valid; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use url::Url; - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct LikeComment { - to: PublicUrl, - pub(in crate::activities::comment) object: Url, - cc: [Url; 1], - #[serde(rename = "type")] - kind: LikeType, - #[serde(flatten)] - common: ActivityCommonFields, -} - -#[async_trait::async_trait(?Send)] -impl ActivityHandlerNew for LikeComment { - async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - check_is_apub_id_valid(&self.common.actor, false) - } - - async fn receive( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - like_or_dislike_comment( - 1, - &self.common.actor, - &self.object, - context, - request_counter, - ) - .await - } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } -} diff --git a/crates/apub_receive/src/activities/comment/mod.rs b/crates/apub_receive/src/activities/comment/mod.rs index 27a11256f..bf6850e22 100644 --- a/crates/apub_receive/src/activities/comment/mod.rs +++ b/crates/apub_receive/src/activities/comment/mod.rs @@ -1,30 +1,20 @@ use lemmy_api_common::{blocking, comment::CommentResponse, send_local_notifs}; -use lemmy_apub::fetcher::{ - objects::get_or_fetch_and_insert_comment, - person::get_or_fetch_and_upsert_person, -}; -use lemmy_db_queries::{Crud, Likeable}; +use lemmy_apub::fetcher::person::get_or_fetch_and_upsert_person; +use lemmy_db_queries::Crud; use lemmy_db_schema::{ - source::{ - comment::{Comment, CommentLike, CommentLikeForm}, - post::Post, - }, + source::{comment::Comment, post::Post}, CommentId, LocalUserId, }; use lemmy_db_views::comment_view::CommentView; use lemmy_utils::{utils::scrape_text_for_mentions, LemmyError}; -use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation}; +use lemmy_websocket::{messages::SendComment, LemmyContext}; use url::Url; pub mod create; pub mod delete; -pub mod dislike; -pub mod like; pub mod remove; pub mod undo_delete; -pub mod undo_dislike; -pub mod undo_like; pub mod undo_remove; pub mod update; @@ -49,7 +39,9 @@ async fn get_notif_recipients( // TODO: in many call sites we are setting an empty vec for recipient_ids, we should get the actual // recipient actors from somewhere -async fn send_websocket_message( +pub(crate) async fn send_websocket_message< + OP: ToString + Send + lemmy_websocket::OperationType + 'static, +>( comment_id: CommentId, recipient_ids: Vec, op: OP, @@ -75,61 +67,3 @@ async fn send_websocket_message Result<(), LemmyError> { - let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; - let comment = get_or_fetch_and_insert_comment(object, context, request_counter).await?; - - let comment_id = comment.id; - let like_form = CommentLikeForm { - comment_id, - post_id: comment.post_id, - person_id: actor.id, - score, - }; - let person_id = actor.id; - blocking(context.pool(), move |conn| { - CommentLike::remove(conn, person_id, comment_id)?; - CommentLike::like(conn, &like_form) - }) - .await??; - - send_websocket_message( - comment_id, - vec![], - UserOperation::CreateCommentLike, - context, - ) - .await -} - -async fn undo_like_or_dislike_comment( - actor: &Url, - object: &Url, - context: &LemmyContext, - request_counter: &mut i32, -) -> Result<(), LemmyError> { - let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; - let comment = get_or_fetch_and_insert_comment(object, context, request_counter).await?; - - let comment_id = comment.id; - let person_id = actor.id; - blocking(context.pool(), move |conn| { - CommentLike::remove(conn, person_id, comment_id) - }) - .await??; - - send_websocket_message( - comment.id, - vec![], - UserOperation::CreateCommentLike, - context, - ) - .await -} diff --git a/crates/apub_receive/src/activities/comment/remove.rs b/crates/apub_receive/src/activities/comment/remove.rs index b5d995ec6..53e69b658 100644 --- a/crates/apub_receive/src/activities/comment/remove.rs +++ b/crates/apub_receive/src/activities/comment/remove.rs @@ -26,7 +26,7 @@ impl ActivityHandlerNew for RemoveComment { async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { verify_domains_match(&self.common.actor, self.common.id_unchecked())?; check_is_apub_id_valid(&self.common.actor, false)?; - verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await + verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await } async fn receive( diff --git a/crates/apub_receive/src/activities/comment/undo_dislike.rs b/crates/apub_receive/src/activities/comment/undo_dislike.rs deleted file mode 100644 index b15cb445a..000000000 --- a/crates/apub_receive/src/activities/comment/undo_dislike.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::activities::comment::{dislike::DislikeComment, undo_like_or_dislike_comment}; -use activitystreams::activity::kind::UndoType; -use lemmy_apub::check_is_apub_id_valid; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use url::Url; - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct UndoDislikeComment { - to: PublicUrl, - object: DislikeComment, - cc: [Url; 1], - #[serde(rename = "type")] - kind: UndoType, - #[serde(flatten)] - common: ActivityCommonFields, -} - -#[async_trait::async_trait(?Send)] -impl ActivityHandlerNew for UndoDislikeComment { - async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - verify_domains_match(&self.common.actor, &self.object.object)?; - check_is_apub_id_valid(&self.common.actor, false)?; - self.object.verify(context, request_counter).await - } - - async fn receive( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - undo_like_or_dislike_comment( - &self.common.actor, - &self.object.object, - context, - request_counter, - ) - .await - } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } -} diff --git a/crates/apub_receive/src/activities/comment/undo_remove.rs b/crates/apub_receive/src/activities/comment/undo_remove.rs index cd48ec338..444f3ce40 100644 --- a/crates/apub_receive/src/activities/comment/undo_remove.rs +++ b/crates/apub_receive/src/activities/comment/undo_remove.rs @@ -33,7 +33,7 @@ impl ActivityHandlerNew for UndoRemoveComment { ) -> Result<(), LemmyError> { verify_domains_match(&self.common.actor, self.common.id_unchecked())?; check_is_apub_id_valid(&self.common.actor, false)?; - verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; + verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; self.object.verify(context, request_counter).await } diff --git a/crates/apub_receive/src/activities/comment/update.rs b/crates/apub_receive/src/activities/comment/update.rs index f13ae9fc7..69a1d936e 100644 --- a/crates/apub_receive/src/activities/comment/update.rs +++ b/crates/apub_receive/src/activities/comment/update.rs @@ -1,7 +1,16 @@ -use crate::activities::comment::{get_notif_recipients, send_websocket_message}; +use crate::activities::{ + comment::{get_notif_recipients, send_websocket_message}, + verify_activity, + verify_person_in_community, +}; use activitystreams::{activity::kind::UpdateType, base::BaseExt}; -use lemmy_apub::{check_is_apub_id_valid, objects::FromApub, NoteExt}; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; +use lemmy_apub::{objects::FromApub, NoteExt}; +use lemmy_apub_lib::{ + verify_domains_match_opt, + ActivityCommonFields, + ActivityHandlerNew, + PublicUrl, +}; use lemmy_db_schema::source::comment::Comment; use lemmy_utils::LemmyError; use lemmy_websocket::{LemmyContext, UserOperationCrud}; @@ -21,10 +30,15 @@ pub struct UpdateComment { #[async_trait::async_trait(?Send)] impl ActivityHandlerNew for UpdateComment { - async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - self.object.id(self.common.actor.as_str())?; - check_is_apub_id_valid(&self.common.actor, false) + async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_activity(self.common())?; + verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?; + verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?; + Ok(()) } async fn receive( diff --git a/crates/apub_receive/src/activities/community/add_mod.rs b/crates/apub_receive/src/activities/community/add_mod.rs index 368d563ad..eb79b197f 100644 --- a/crates/apub_receive/src/activities/community/add_mod.rs +++ b/crates/apub_receive/src/activities/community/add_mod.rs @@ -32,7 +32,7 @@ impl ActivityHandlerNew for AddMod { verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.target, &self.cc[0])?; check_is_apub_id_valid(&self.common.actor, false)?; - verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; + verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; verify_add_remove_moderator_target(&self.target, self.cc[0].clone()) } diff --git a/crates/apub_receive/src/activities/community/announce.rs b/crates/apub_receive/src/activities/community/announce.rs index ca0a8edad..5626928b5 100644 --- a/crates/apub_receive/src/activities/community/announce.rs +++ b/crates/apub_receive/src/activities/community/announce.rs @@ -1,14 +1,17 @@ +use activitystreams::activity::kind::AnnounceType; +use lemmy_apub_lib::{ActivityCommonFields, ActivityHandlerNew, PublicUrl}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; +use url::Url; + use crate::{ activities::{ comment::{ create::CreateComment, delete::DeleteComment, - dislike::DislikeComment, - like::LikeComment, remove::RemoveComment, undo_delete::UndoDeleteComment, - undo_dislike::UndoDislikeComment, - undo_like::UndoLikeComment, undo_remove::UndoRemoveComment, update::UpdateComment, }, @@ -16,48 +19,42 @@ use crate::{ post::{ create::CreatePost, delete::DeletePost, - dislike::DislikePost, - like::LikePost, remove::RemovePost, undo_delete::UndoDeletePost, - undo_dislike::UndoDislikePost, - undo_like::UndoLikePost, undo_remove::UndoRemovePost, update::UpdatePost, }, + post_or_comment::{ + dislike::DislikePostOrComment, + like::LikePostOrComment, + undo_dislike::UndoDislikePostOrComment, + undo_like::UndoLikePostOrComment, + }, + verify_activity, + verify_community, }, http::is_activity_already_known, }; -use activitystreams::activity::kind::RemoveType; -use lemmy_apub::check_is_apub_id_valid; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use url::Url; #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandlerNew)] +#[serde(untagged)] pub enum AnnouncableActivities { CreateComment(CreateComment), UpdateComment(UpdateComment), - LikeComment(LikeComment), - DislikeComment(DislikeComment), - UndoLikeComment(UndoLikeComment), - UndoDislikeComment(UndoDislikeComment), DeleteComment(DeleteComment), UndoDeleteComment(UndoDeleteComment), RemoveComment(RemoveComment), UndoRemoveComment(UndoRemoveComment), CreatePost(CreatePost), UpdatePost(UpdatePost), - LikePost(LikePost), - DislikePost(DislikePost), DeletePost(DeletePost), UndoDeletePost(UndoDeletePost), RemovePost(RemovePost), UndoRemovePost(UndoRemovePost), - UndoLikePost(UndoLikePost), - UndoDislikePost(UndoDislikePost), + LikePostOrComment(LikePostOrComment), + DislikePostOrComment(DislikePostOrComment), + UndoLikePostOrComment(UndoLikePostOrComment), + UndoDislikePostOrComment(UndoDislikePostOrComment), BlockUserFromCommunity(BlockUserFromCommunity), UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), } @@ -69,7 +66,7 @@ pub struct AnnounceActivity { object: AnnouncableActivities, cc: [Url; 1], #[serde(rename = "type")] - kind: RemoveType, + kind: AnnounceType, #[serde(flatten)] common: ActivityCommonFields, } @@ -81,10 +78,9 @@ impl ActivityHandlerNew for AnnounceActivity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - verify_domains_match(&self.common.actor, &self.cc[0])?; - check_is_apub_id_valid(&self.common.actor, false)?; - self.object.verify(context, request_counter).await + verify_activity(self.common())?; + verify_community(&self.common.actor, context, request_counter).await?; + Ok(()) } async fn receive( diff --git a/crates/apub_receive/src/activities/community/block_user.rs b/crates/apub_receive/src/activities/community/block_user.rs index 0b29fe907..2c2676bfe 100644 --- a/crates/apub_receive/src/activities/community/block_user.rs +++ b/crates/apub_receive/src/activities/community/block_user.rs @@ -34,7 +34,7 @@ impl ActivityHandlerNew for BlockUserFromCommunity { async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { verify_domains_match(&self.common.actor, self.common.id_unchecked())?; check_is_apub_id_valid(&self.common.actor, false)?; - verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await + verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await } async fn receive( diff --git a/crates/apub_receive/src/activities/community/remove_mod.rs b/crates/apub_receive/src/activities/community/remove_mod.rs index 69f874603..f06c51d2a 100644 --- a/crates/apub_receive/src/activities/community/remove_mod.rs +++ b/crates/apub_receive/src/activities/community/remove_mod.rs @@ -32,7 +32,7 @@ impl ActivityHandlerNew for RemoveMod { verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.target, &self.cc[0])?; check_is_apub_id_valid(&self.common.actor, false)?; - verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; + verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; verify_add_remove_moderator_target(&self.target, self.cc[0].clone()) } diff --git a/crates/apub_receive/src/activities/community/undo_block_user.rs b/crates/apub_receive/src/activities/community/undo_block_user.rs index f71765c6f..69f7f20b8 100644 --- a/crates/apub_receive/src/activities/community/undo_block_user.rs +++ b/crates/apub_receive/src/activities/community/undo_block_user.rs @@ -33,7 +33,7 @@ impl ActivityHandlerNew for UndoBlockUserFromCommunity { ) -> Result<(), LemmyError> { verify_domains_match(&self.common.actor, self.common.id_unchecked())?; check_is_apub_id_valid(&self.common.actor, false)?; - verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; + verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; self.object.verify(context, request_counter).await } diff --git a/crates/apub_receive/src/activities/mod.rs b/crates/apub_receive/src/activities/mod.rs index eccb5a914..d4801374a 100644 --- a/crates/apub_receive/src/activities/mod.rs +++ b/crates/apub_receive/src/activities/mod.rs @@ -1,12 +1,16 @@ use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub::{ + check_community_or_site_ban, check_is_apub_id_valid, fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, }; use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields}; use lemmy_db_queries::ApubObject; -use lemmy_db_schema::source::{community::Community, person::Person}; +use lemmy_db_schema::{ + source::{community::Community, person::Person}, + DbUrl, +}; use lemmy_db_views_actor::community_view::CommunityView; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; @@ -16,6 +20,7 @@ pub mod comment; pub mod community; pub mod following; pub mod post; +pub mod post_or_comment; pub mod private_message; /// Checks that the specified Url actually identifies a Person (by fetching it), and that the person @@ -32,6 +37,29 @@ async fn verify_person( Ok(()) } +/// Fetches the person and community to verify their type, then checks if person is banned from site +/// or community. +async fn verify_person_in_community( + person_id: &Url, + cc: &[Url], + context: &LemmyContext, + request_counter: &mut i32, +) -> Result { + let person = get_or_fetch_and_upsert_person(person_id, context, request_counter).await?; + let mut cc_iter = cc.iter(); + let community: Community = loop { + if let Some(cid) = cc_iter.next() { + if let Ok(c) = get_or_fetch_and_upsert_community(cid, context, request_counter).await { + break c; + } + } else { + return Err(anyhow!("No community found in cc").into()); + } + }; + check_community_or_site_ban(&person, community.id, context.pool()).await?; + Ok(community) +} + /// Simply check that the url actually refers to a valid group. async fn verify_community( community_id: &Url, @@ -49,7 +77,7 @@ fn verify_activity(common: &ActivityCommonFields) -> Result<(), LemmyError> { } async fn verify_mod_action( - actor_id: Url, + actor_id: &Url, activity_cc: Url, context: &LemmyContext, ) -> Result<(), LemmyError> { @@ -59,8 +87,9 @@ async fn verify_mod_action( .await??; if community.local { + let actor_id: DbUrl = actor_id.clone().into(); let actor = blocking(context.pool(), move |conn| { - Person::read_from_apub_id(conn, &actor_id.into()) + Person::read_from_apub_id(conn, &actor_id) }) .await??; diff --git a/crates/apub_receive/src/activities/post/create.rs b/crates/apub_receive/src/activities/post/create.rs index ffe9fccb1..ef7069aa7 100644 --- a/crates/apub_receive/src/activities/post/create.rs +++ b/crates/apub_receive/src/activities/post/create.rs @@ -1,13 +1,21 @@ -use crate::activities::post::send_websocket_message; +use crate::activities::{ + post::send_websocket_message, + verify_activity, + verify_person_in_community, +}; use activitystreams::{activity::kind::CreateType, base::BaseExt}; use lemmy_apub::{ - check_is_apub_id_valid, fetcher::person::get_or_fetch_and_upsert_person, objects::FromApub, ActorType, PageExt, }; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; +use lemmy_apub_lib::{ + verify_domains_match_opt, + ActivityCommonFields, + ActivityHandlerNew, + PublicUrl, +}; use lemmy_db_schema::source::post::Post; use lemmy_utils::LemmyError; use lemmy_websocket::{LemmyContext, UserOperationCrud}; @@ -27,10 +35,15 @@ pub struct CreatePost { #[async_trait::async_trait(?Send)] impl ActivityHandlerNew for CreatePost { - async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { - verify_domains_match(self.common.id_unchecked(), &self.common.actor)?; - self.object.id(self.common.actor.as_str())?; - check_is_apub_id_valid(&self.common.actor, false) + async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_activity(self.common())?; + verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?; + verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?; + Ok(()) } async fn receive( diff --git a/crates/apub_receive/src/activities/post/dislike.rs b/crates/apub_receive/src/activities/post/dislike.rs deleted file mode 100644 index f8b730e28..000000000 --- a/crates/apub_receive/src/activities/post/dislike.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::activities::post::like_or_dislike_post; -use activitystreams::activity::kind::DislikeType; -use lemmy_apub::check_is_apub_id_valid; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use url::Url; - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DislikePost { - to: PublicUrl, - pub(in crate::activities::post) object: Url, - cc: [Url; 1], - #[serde(rename = "type")] - kind: DislikeType, - #[serde(flatten)] - common: ActivityCommonFields, -} - -#[async_trait::async_trait(?Send)] -impl ActivityHandlerNew for DislikePost { - async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - check_is_apub_id_valid(&self.common.actor, false) - } - - async fn receive( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - like_or_dislike_post( - -1, - &self.common.actor, - &self.object, - context, - request_counter, - ) - .await - } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } -} diff --git a/crates/apub_receive/src/activities/post/mod.rs b/crates/apub_receive/src/activities/post/mod.rs index fe6fc51c2..20860d211 100644 --- a/crates/apub_receive/src/activities/post/mod.rs +++ b/crates/apub_receive/src/activities/post/mod.rs @@ -1,30 +1,19 @@ use lemmy_api_common::{blocking, post::PostResponse}; -use lemmy_apub::fetcher::{ - objects::get_or_fetch_and_insert_post, - person::get_or_fetch_and_upsert_person, -}; -use lemmy_db_queries::Likeable; -use lemmy_db_schema::{ - source::post::{PostLike, PostLikeForm}, - PostId, -}; +use lemmy_db_schema::PostId; use lemmy_db_views::post_view::PostView; use lemmy_utils::LemmyError; -use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation}; -use url::Url; +use lemmy_websocket::{messages::SendPost, LemmyContext}; pub mod create; pub mod delete; -pub mod dislike; -pub mod like; pub mod remove; pub mod undo_delete; -pub mod undo_dislike; -pub mod undo_like; pub mod undo_remove; pub mod update; -async fn send_websocket_message( +pub(crate) async fn send_websocket_message< + OP: ToString + Send + lemmy_websocket::OperationType + 'static, +>( post_id: PostId, op: OP, context: &LemmyContext, @@ -44,47 +33,3 @@ async fn send_websocket_message Result<(), LemmyError> { - let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; - let post = get_or_fetch_and_insert_post(object, context, request_counter).await?; - - let post_id = post.id; - let like_form = PostLikeForm { - post_id: post.id, - person_id: actor.id, - score, - }; - let person_id = actor.id; - blocking(context.pool(), move |conn| { - PostLike::remove(conn, person_id, post_id)?; - PostLike::like(conn, &like_form) - }) - .await??; - - send_websocket_message(post.id, UserOperation::CreatePostLike, context).await -} - -async fn undo_like_or_dislike_post( - actor: &Url, - object: &Url, - context: &LemmyContext, - request_counter: &mut i32, -) -> Result<(), LemmyError> { - let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; - let post = get_or_fetch_and_insert_post(object, context, request_counter).await?; - - let post_id = post.id; - let person_id = actor.id; - blocking(context.pool(), move |conn| { - PostLike::remove(conn, person_id, post_id) - }) - .await??; - send_websocket_message(post.id, UserOperation::CreatePostLike, context).await -} diff --git a/crates/apub_receive/src/activities/post/remove.rs b/crates/apub_receive/src/activities/post/remove.rs index 4b7afd30a..85f63bebd 100644 --- a/crates/apub_receive/src/activities/post/remove.rs +++ b/crates/apub_receive/src/activities/post/remove.rs @@ -26,7 +26,7 @@ impl ActivityHandlerNew for RemovePost { async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { verify_domains_match(&self.common.actor, self.common.id_unchecked())?; check_is_apub_id_valid(&self.common.actor, false)?; - verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await + verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await } async fn receive( diff --git a/crates/apub_receive/src/activities/post/undo_like.rs b/crates/apub_receive/src/activities/post/undo_like.rs deleted file mode 100644 index f256f4f4a..000000000 --- a/crates/apub_receive/src/activities/post/undo_like.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::activities::post::{like::LikePost, undo_like_or_dislike_post}; -use activitystreams::activity::kind::UndoType; -use lemmy_apub::check_is_apub_id_valid; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use url::Url; - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct UndoLikePost { - to: PublicUrl, - object: LikePost, - cc: [Url; 1], - #[serde(rename = "type")] - kind: UndoType, - #[serde(flatten)] - common: ActivityCommonFields, -} -#[async_trait::async_trait(?Send)] -impl ActivityHandlerNew for UndoLikePost { - async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - verify_domains_match(&self.common.actor, &self.object.object)?; - check_is_apub_id_valid(&self.common.actor, false)?; - self.object.verify(context, request_counter).await - } - - async fn receive( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - undo_like_or_dislike_post( - &self.common.actor, - &self.object.object, - context, - request_counter, - ) - .await - } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } -} diff --git a/crates/apub_receive/src/activities/post/undo_remove.rs b/crates/apub_receive/src/activities/post/undo_remove.rs index a21ca9892..d2dd27555 100644 --- a/crates/apub_receive/src/activities/post/undo_remove.rs +++ b/crates/apub_receive/src/activities/post/undo_remove.rs @@ -33,7 +33,7 @@ impl ActivityHandlerNew for UndoRemovePost { ) -> Result<(), LemmyError> { verify_domains_match(&self.common.actor, self.common.id_unchecked())?; check_is_apub_id_valid(&self.common.actor, false)?; - verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; + verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; self.object.verify(context, request_counter).await } diff --git a/crates/apub_receive/src/activities/post/update.rs b/crates/apub_receive/src/activities/post/update.rs index cb1a476a6..502792a0a 100644 --- a/crates/apub_receive/src/activities/post/update.rs +++ b/crates/apub_receive/src/activities/post/update.rs @@ -1,19 +1,26 @@ -use crate::activities::post::send_websocket_message; +use crate::activities::{ + post::send_websocket_message, + verify_activity, + verify_mod_action, + verify_person_in_community, +}; use activitystreams::{activity::kind::UpdateType, base::BaseExt}; use anyhow::Context; use lemmy_api_common::blocking; use lemmy_apub::{ - check_is_apub_id_valid, objects::{FromApub, FromApubToForm}, + ActorType, PageExt, }; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; -use lemmy_db_queries::{ApubObject, Crud}; +use lemmy_apub_lib::{ + verify_domains_match_opt, + ActivityCommonFields, + ActivityHandlerNew, + PublicUrl, +}; +use lemmy_db_queries::ApubObject; use lemmy_db_schema::{ - source::{ - community::Community, - post::{Post, PostForm}, - }, + source::post::{Post, PostForm}, DbUrl, }; use lemmy_utils::{location_info, LemmyError}; @@ -34,17 +41,16 @@ pub struct UpdatePost { #[async_trait::async_trait(?Send)] impl ActivityHandlerNew for UpdatePost { - async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - self.object.id(self.common.actor.as_str())?; - check_is_apub_id_valid(&self.common.actor, false) - } - - async fn receive( + async fn verify( &self, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_activity(self.common())?; + let community = + verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?; + verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?; + let temp_post = PostForm::from_apub( &self.object, context, @@ -53,36 +59,32 @@ impl ActivityHandlerNew for UpdatePost { false, ) .await?; - let post_id: DbUrl = temp_post.ap_id.context(location_info!())?; let old_post = blocking(context.pool(), move |conn| { Post::read_from_apub_id(conn, &post_id) }) .await??; - - // If sticked or locked state was changed, make sure the actor is a mod let stickied = temp_post.stickied.context(location_info!())?; let locked = temp_post.locked.context(location_info!())?; - let mut mod_action_allowed = false; if (stickied != old_post.stickied) || (locked != old_post.locked) { - let community = blocking(context.pool(), move |conn| { - Community::read(conn, old_post.community_id) - }) - .await??; - // Only check mod status if the community is local, otherwise we trust that it was sent correctly. - if community.local { - // TODO - //verify_mod_activity(&update, announce, &community, context).await?; - } - mod_action_allowed = true; + verify_mod_action(&self.common.actor, community.actor_id(), context).await?; } + Ok(()) + } + + async fn receive( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { let post = Post::from_apub( &self.object, context, self.common.actor.clone(), request_counter, - mod_action_allowed, + // TODO: we already check here if the mod action is valid, can remove that check param + true, ) .await?; diff --git a/crates/apub_receive/src/activities/comment/dislike.rs b/crates/apub_receive/src/activities/post_or_comment/dislike.rs similarity index 53% rename from crates/apub_receive/src/activities/comment/dislike.rs rename to crates/apub_receive/src/activities/post_or_comment/dislike.rs index d80b19757..98bbbd4cb 100644 --- a/crates/apub_receive/src/activities/comment/dislike.rs +++ b/crates/apub_receive/src/activities/post_or_comment/dislike.rs @@ -1,16 +1,19 @@ -use crate::activities::comment::like_or_dislike_comment; +use crate::activities::{ + post_or_comment::voting::receive_like_or_dislike, + verify_activity, + verify_person_in_community, +}; use activitystreams::activity::kind::DislikeType; -use lemmy_apub::check_is_apub_id_valid; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; +use lemmy_apub_lib::{ActivityCommonFields, ActivityHandlerNew, PublicUrl}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] -pub struct DislikeComment { +pub struct DislikePostOrComment { to: PublicUrl, - pub(in crate::activities::comment) object: Url, + pub(in crate::activities) object: Url, cc: [Url; 1], #[serde(rename = "type")] kind: DislikeType, @@ -19,10 +22,15 @@ pub struct DislikeComment { } #[async_trait::async_trait(?Send)] -impl ActivityHandlerNew for DislikeComment { - async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - check_is_apub_id_valid(&self.common.actor, false) +impl ActivityHandlerNew for DislikePostOrComment { + async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_activity(self.common())?; + verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?; + Ok(()) } async fn receive( @@ -30,7 +38,7 @@ impl ActivityHandlerNew for DislikeComment { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - like_or_dislike_comment( + receive_like_or_dislike( -1, &self.common.actor, &self.object, diff --git a/crates/apub_receive/src/activities/post/like.rs b/crates/apub_receive/src/activities/post_or_comment/like.rs similarity index 52% rename from crates/apub_receive/src/activities/post/like.rs rename to crates/apub_receive/src/activities/post_or_comment/like.rs index 8cf22b2ae..8b6046d3f 100644 --- a/crates/apub_receive/src/activities/post/like.rs +++ b/crates/apub_receive/src/activities/post_or_comment/like.rs @@ -1,16 +1,19 @@ -use crate::activities::post::like_or_dislike_post; +use crate::activities::{ + post_or_comment::voting::receive_like_or_dislike, + verify_activity, + verify_person_in_community, +}; use activitystreams::activity::kind::LikeType; -use lemmy_apub::check_is_apub_id_valid; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; +use lemmy_apub_lib::{ActivityCommonFields, ActivityHandlerNew, PublicUrl}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] -pub struct LikePost { +pub struct LikePostOrComment { to: PublicUrl, - pub(in crate::activities::post) object: Url, + pub(in crate::activities::post_or_comment) object: Url, cc: [Url; 1], #[serde(rename = "type")] kind: LikeType, @@ -19,10 +22,15 @@ pub struct LikePost { } #[async_trait::async_trait(?Send)] -impl ActivityHandlerNew for LikePost { - async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - check_is_apub_id_valid(&self.common.actor, false) +impl ActivityHandlerNew for LikePostOrComment { + async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_activity(self.common())?; + verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?; + Ok(()) } async fn receive( @@ -30,7 +38,7 @@ impl ActivityHandlerNew for LikePost { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - like_or_dislike_post( + receive_like_or_dislike( 1, &self.common.actor, &self.object, diff --git a/crates/apub_receive/src/activities/post_or_comment/mod.rs b/crates/apub_receive/src/activities/post_or_comment/mod.rs new file mode 100644 index 000000000..cedbe3b49 --- /dev/null +++ b/crates/apub_receive/src/activities/post_or_comment/mod.rs @@ -0,0 +1,5 @@ +pub mod dislike; +pub mod like; +pub mod undo_dislike; +pub mod undo_like; +mod voting; diff --git a/crates/apub_receive/src/activities/post/undo_dislike.rs b/crates/apub_receive/src/activities/post_or_comment/undo_dislike.rs similarity index 54% rename from crates/apub_receive/src/activities/post/undo_dislike.rs rename to crates/apub_receive/src/activities/post_or_comment/undo_dislike.rs index d9a0c6de5..72eb23749 100644 --- a/crates/apub_receive/src/activities/post/undo_dislike.rs +++ b/crates/apub_receive/src/activities/post_or_comment/undo_dislike.rs @@ -1,16 +1,19 @@ -use crate::activities::post::{dislike::DislikePost, undo_like_or_dislike_post}; +use crate::activities::{ + post_or_comment::{dislike::DislikePostOrComment, voting::receive_undo_like_or_dislike}, + verify_activity, + verify_person_in_community, +}; use activitystreams::activity::kind::UndoType; -use lemmy_apub::check_is_apub_id_valid; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; +use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] -pub struct UndoDislikePost { +pub struct UndoDislikePostOrComment { to: PublicUrl, - object: DislikePost, + object: DislikePostOrComment, cc: [Url; 1], #[serde(rename = "type")] kind: UndoType, @@ -19,16 +22,17 @@ pub struct UndoDislikePost { } #[async_trait::async_trait(?Send)] -impl ActivityHandlerNew for UndoDislikePost { +impl ActivityHandlerNew for UndoDislikePostOrComment { async fn verify( &self, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - verify_domains_match(&self.common.actor, &self.object.object)?; - check_is_apub_id_valid(&self.common.actor, false)?; - self.object.verify(context, request_counter).await + verify_activity(self.common())?; + verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?; + verify_urls_match(&self.common.actor, &self.object.common().actor)?; + self.object.verify(context, request_counter).await?; + Ok(()) } async fn receive( @@ -36,7 +40,7 @@ impl ActivityHandlerNew for UndoDislikePost { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - undo_like_or_dislike_post( + receive_undo_like_or_dislike( &self.common.actor, &self.object.object, context, diff --git a/crates/apub_receive/src/activities/comment/undo_like.rs b/crates/apub_receive/src/activities/post_or_comment/undo_like.rs similarity index 54% rename from crates/apub_receive/src/activities/comment/undo_like.rs rename to crates/apub_receive/src/activities/post_or_comment/undo_like.rs index 3892485e6..62a2ab8f2 100644 --- a/crates/apub_receive/src/activities/comment/undo_like.rs +++ b/crates/apub_receive/src/activities/post_or_comment/undo_like.rs @@ -1,16 +1,19 @@ -use crate::activities::comment::{like::LikeComment, undo_like_or_dislike_comment}; +use crate::activities::{ + post_or_comment::{like::LikePostOrComment, voting::receive_undo_like_or_dislike}, + verify_activity, + verify_person_in_community, +}; use activitystreams::activity::kind::UndoType; -use lemmy_apub::check_is_apub_id_valid; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; +use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] -pub struct UndoLikeComment { +pub struct UndoLikePostOrComment { to: PublicUrl, - object: LikeComment, + object: LikePostOrComment, cc: [Url; 1], #[serde(rename = "type")] kind: UndoType, @@ -19,16 +22,17 @@ pub struct UndoLikeComment { } #[async_trait::async_trait(?Send)] -impl ActivityHandlerNew for UndoLikeComment { +impl ActivityHandlerNew for UndoLikePostOrComment { async fn verify( &self, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_domains_match(&self.common.actor, self.common.id_unchecked())?; - verify_domains_match(&self.common.actor, &self.object.object)?; - check_is_apub_id_valid(&self.common.actor, false)?; - self.object.verify(context, request_counter).await + verify_activity(self.common())?; + verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?; + verify_urls_match(&self.common.actor, &self.object.common().actor)?; + self.object.verify(context, request_counter).await?; + Ok(()) } async fn receive( @@ -36,7 +40,7 @@ impl ActivityHandlerNew for UndoLikeComment { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - undo_like_or_dislike_comment( + receive_undo_like_or_dislike( &self.common.actor, &self.object.object, context, diff --git a/crates/apub_receive/src/activities/post_or_comment/voting.rs b/crates/apub_receive/src/activities/post_or_comment/voting.rs new file mode 100644 index 000000000..d575ba503 --- /dev/null +++ b/crates/apub_receive/src/activities/post_or_comment/voting.rs @@ -0,0 +1,152 @@ +use crate::activities::{ + comment::send_websocket_message as send_comment_websocket_message, + post::send_websocket_message as send_post_websocket_message, +}; +use lemmy_api_common::blocking; +use lemmy_apub::{ + fetcher::{ + objects::get_or_fetch_and_insert_post_or_comment, + person::get_or_fetch_and_upsert_person, + }, + PostOrComment, +}; +use lemmy_db_queries::Likeable; +use lemmy_db_schema::source::{ + comment::{Comment, CommentLike, CommentLikeForm}, + post::{Post, PostLike, PostLikeForm}, +}; +use lemmy_utils::LemmyError; +use lemmy_websocket::{LemmyContext, UserOperation}; +use std::ops::Deref; +use url::Url; + +pub(in crate::activities::post_or_comment) async fn receive_like_or_dislike( + score: i16, + actor: &Url, + object: &Url, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result<(), LemmyError> { + match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? { + PostOrComment::Post(p) => { + like_or_dislike_post(score, actor, p.deref(), context, request_counter).await + } + PostOrComment::Comment(c) => { + like_or_dislike_comment(score, actor, c.deref(), context, request_counter).await + } + } +} + +async fn like_or_dislike_comment( + score: i16, + actor: &Url, + comment: &Comment, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result<(), LemmyError> { + let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; + + let comment_id = comment.id; + let like_form = CommentLikeForm { + comment_id, + post_id: comment.post_id, + person_id: actor.id, + score, + }; + let person_id = actor.id; + blocking(context.pool(), move |conn| { + CommentLike::remove(conn, person_id, comment_id)?; + CommentLike::like(conn, &like_form) + }) + .await??; + + send_comment_websocket_message( + comment_id, + vec![], + UserOperation::CreateCommentLike, + context, + ) + .await +} + +async fn like_or_dislike_post( + score: i16, + actor: &Url, + post: &Post, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result<(), LemmyError> { + let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; + + let post_id = post.id; + let like_form = PostLikeForm { + post_id: post.id, + person_id: actor.id, + score, + }; + let person_id = actor.id; + blocking(context.pool(), move |conn| { + PostLike::remove(conn, person_id, post_id)?; + PostLike::like(conn, &like_form) + }) + .await??; + + send_post_websocket_message(post.id, UserOperation::CreatePostLike, context).await +} + +pub(in crate::activities::post_or_comment) async fn receive_undo_like_or_dislike( + actor: &Url, + object: &Url, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result<(), LemmyError> { + match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? { + PostOrComment::Post(p) => { + undo_like_or_dislike_post(actor, p.deref(), context, request_counter).await + } + PostOrComment::Comment(c) => { + undo_like_or_dislike_comment(actor, c.deref(), context, request_counter).await + } + } +} + +async fn undo_like_or_dislike_comment( + actor: &Url, + comment: &Comment, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result<(), LemmyError> { + let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; + + let comment_id = comment.id; + let person_id = actor.id; + blocking(context.pool(), move |conn| { + CommentLike::remove(conn, person_id, comment_id) + }) + .await??; + + send_comment_websocket_message( + comment.id, + vec![], + UserOperation::CreateCommentLike, + context, + ) + .await +} + +async fn undo_like_or_dislike_post( + actor: &Url, + post: &Post, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result<(), LemmyError> { + let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; + + let post_id = post.id; + let person_id = actor.id; + blocking(context.pool(), move |conn| { + PostLike::remove(conn, person_id, post_id) + }) + .await??; + send_post_websocket_message(post.id, UserOperation::CreatePostLike, context).await +} diff --git a/crates/apub_receive/src/http/inbox_enums.rs b/crates/apub_receive/src/http/inbox_enums.rs index 68bf16f14..4516ea9de 100644 --- a/crates/apub_receive/src/http/inbox_enums.rs +++ b/crates/apub_receive/src/http/inbox_enums.rs @@ -2,12 +2,8 @@ use crate::activities::{ comment::{ create::CreateComment, delete::DeleteComment, - dislike::DislikeComment, - like::LikeComment, remove::RemoveComment, undo_delete::UndoDeleteComment, - undo_dislike::UndoDislikeComment, - undo_like::UndoLikeComment, undo_remove::UndoRemoveComment, update::UpdateComment, }, @@ -27,15 +23,17 @@ use crate::activities::{ post::{ create::CreatePost, delete::DeletePost, - dislike::DislikePost, - like::LikePost, remove::RemovePost, undo_delete::UndoDeletePost, - undo_dislike::UndoDislikePost, - undo_like::UndoLikePost, undo_remove::UndoRemovePost, update::UpdatePost, }, + post_or_comment::{ + dislike::DislikePostOrComment, + like::LikePostOrComment, + undo_dislike::UndoDislikePostOrComment, + undo_like::UndoLikePostOrComment, + }, private_message::{ create::CreatePrivateMessage, delete::DeletePrivateMessage, @@ -66,24 +64,20 @@ pub enum GroupInboxActivities { UndoFollowCommunity(UndoFollowCommunity), CreateComment(CreateComment), UpdateComment(UpdateComment), - LikeComment(LikeComment), - DislikeComment(DislikeComment), - UndoLikeComment(UndoLikeComment), - UndoDislikeComment(UndoDislikeComment), DeleteComment(DeleteComment), UndoDeleteComment(UndoDeleteComment), RemoveComment(RemoveComment), UndoRemoveComment(UndoRemoveComment), CreatePost(CreatePost), UpdatePost(UpdatePost), - LikePost(LikePost), - DislikePost(DislikePost), DeletePost(DeletePost), UndoDeletePost(UndoDeletePost), RemovePost(RemovePost), UndoRemovePost(UndoRemovePost), - UndoLikePost(UndoLikePost), - UndoDislikePost(UndoDislikePost), + LikePostOrComment(LikePostOrComment), + DislikePostOrComment(DislikePostOrComment), + UndoLikePostOrComment(UndoLikePostOrComment), + UndoDislikePostOrComment(UndoDislikePostOrComment), UpdateCommunity(Box), DeleteCommunity(DeleteCommunity), RemoveCommunity(RemoveCommunity), @@ -98,36 +92,25 @@ pub enum GroupInboxActivities { #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandlerNew)] #[serde(untagged)] pub enum SharedInboxActivities { - // received by person - AcceptFollowCommunity(AcceptFollowCommunity), - CreatePrivateMessage(CreatePrivateMessage), - UpdatePrivateMessage(UpdatePrivateMessage), - DeletePrivateMessage(DeletePrivateMessage), - UndoDeletePrivateMessage(UndoDeletePrivateMessage), - AnnounceActivity(Box), // received by group FollowCommunity(FollowCommunity), UndoFollowCommunity(UndoFollowCommunity), CreateComment(CreateComment), UpdateComment(UpdateComment), - LikeComment(LikeComment), - DislikeComment(DislikeComment), - UndoLikeComment(UndoLikeComment), - UndoDislikeComment(UndoDislikeComment), DeleteComment(DeleteComment), UndoDeleteComment(UndoDeleteComment), RemoveComment(RemoveComment), UndoRemoveComment(UndoRemoveComment), CreatePost(CreatePost), UpdatePost(UpdatePost), - LikePost(LikePost), - DislikePost(DislikePost), DeletePost(DeletePost), UndoDeletePost(UndoDeletePost), RemovePost(RemovePost), UndoRemovePost(UndoRemovePost), - UndoLikePost(UndoLikePost), - UndoDislikePost(UndoDislikePost), + LikePostOrComment(LikePostOrComment), + DislikePostOrComment(DislikePostOrComment), + UndoDislikePostOrComment(UndoDislikePostOrComment), + UndoLikePostOrComment(UndoLikePostOrComment), UpdateCommunity(Box), DeleteCommunity(DeleteCommunity), RemoveCommunity(RemoveCommunity), @@ -137,4 +120,13 @@ pub enum SharedInboxActivities { UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), AddMod(AddMod), RemoveMod(RemoveMod), + // received by person + AcceptFollowCommunity(AcceptFollowCommunity), + // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably + // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt. + CreatePrivateMessage(CreatePrivateMessage), + UpdatePrivateMessage(UpdatePrivateMessage), + DeletePrivateMessage(DeletePrivateMessage), + UndoDeletePrivateMessage(UndoDeletePrivateMessage), + AnnounceActivity(Box), } diff --git a/crates/apub_receive/src/http/mod.rs b/crates/apub_receive/src/http/mod.rs index aeb8999db..4275fbb8e 100644 --- a/crates/apub_receive/src/http/mod.rs +++ b/crates/apub_receive/src/http/mod.rs @@ -1,7 +1,4 @@ -use crate::activities::{ - following::accept::AcceptFollowCommunity, - post::{create::CreatePost, like::LikePost}, -}; +use crate::http::inbox_enums::SharedInboxActivities; use actix_web::{ body::Body, web, @@ -20,12 +17,11 @@ use lemmy_apub::{ insert_activity, APUB_JSON_CONTENT_TYPE, }; -use lemmy_apub_lib::{ActivityCommonFields, ActivityHandlerNew}; +use lemmy_apub_lib::ActivityHandlerNew; use lemmy_db_queries::{source::activity::Activity_, DbPool}; use lemmy_db_schema::source::activity::Activity; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; -use log::debug; use serde::{Deserialize, Serialize}; use std::{fmt::Debug, io::Read}; use url::Url; @@ -36,21 +32,13 @@ pub mod inbox_enums; pub mod person; pub mod post; -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ActivityHandlerNew)] -#[serde(untagged)] -enum Ac { - CreatePost(CreatePost), - LikePost(LikePost), - AcceptFollowCommunity(AcceptFollowCommunity), -} - pub async fn shared_inbox( request: HttpRequest, payload: Payload, context: web::Data, ) -> Result { let unparsed = payload_to_string(payload).await?; - receive_activity::(request, &unparsed, context).await + receive_activity::(request, &unparsed, context).await } async fn payload_to_string(mut payload: Payload) -> Result { @@ -70,22 +58,23 @@ async fn receive_activity<'a, T>( where T: ActivityHandlerNew + Clone + Deserialize<'a> + Serialize + std::fmt::Debug + Send + 'static, { - debug!("Received activity {}", activity); - let activity = serde_json::from_str::(activity)?; + let activity = serde_json::from_str::(activity); + dbg!(&activity); + let activity = activity?; let activity_data = activity.common(); // TODO: which order to check things? // Do nothing if we received the same activity before if is_activity_already_known(context.pool(), activity_data.id_unchecked()).await? { return Ok(HttpResponse::Ok().finish()); } - assert_activity_not_local(&activity)?; - check_is_apub_id_valid(&activity_data.actor, false)?; let request_counter = &mut 0; let actor = get_or_fetch_and_upsert_actor(&activity_data.actor, &context, request_counter).await?; verify_signature(&request, &actor.public_key().context(location_info!())?)?; activity.verify(&context, request_counter).await?; + assert_activity_not_local(&activity)?; + check_is_apub_id_valid(&activity_data.actor, false)?; // Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen // if we receive the same activity twice in very quick succession. diff --git a/scripts/compilation_benchmark.sh b/scripts/compilation_benchmark.sh index 6d454795d..af355734b 100755 --- a/scripts/compilation_benchmark.sh +++ b/scripts/compilation_benchmark.sh @@ -8,8 +8,8 @@ for ((i=0; i < times; i++)) ; do echo "cargo clean" # to benchmark incremental compilation time, do a full build with the same compiler version first, # and use the following clean command: - #cargo clean -p lemmy_utils - cargo clean + cargo clean -p lemmy_utils + #cargo clean echo "cargo build" start=$(date +%s.%N) RUSTC_WRAPPER='' cargo build -q @@ -20,4 +20,4 @@ done average=$(bc <<< "scale=0; $duration / $times") -echo "Average compilation time over $times runs is $average seconds" \ No newline at end of file +echo "Average compilation time over $times runs is $average seconds"