From 271785b7fbe824628d79187f33be3c2e75f42d1b Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 28 Oct 2021 13:46:48 +0200 Subject: [PATCH] When receiving activity, dont read community from cc (for pleroma compat and better verification) --- .../activities/comment/create_or_update.rs | 27 +++++++++-- .../apub/src/activities/community/add_mod.rs | 22 +++++++-- .../apub/src/activities/community/announce.rs | 41 ++++++++++++++-- .../src/activities/community/block_user.rs | 23 +++++++-- crates/apub/src/activities/community/mod.rs | 13 ++++- .../src/activities/community/remove_mod.rs | 23 +++++++-- .../activities/community/undo_block_user.rs | 18 +++++-- .../apub/src/activities/community/update.rs | 23 +++++++-- crates/apub/src/activities/deletion/delete.rs | 28 ++++++++++- crates/apub/src/activities/deletion/mod.rs | 5 +- .../src/activities/deletion/undo_delete.rs | 16 ++++++- .../apub/src/activities/following/accept.rs | 8 +--- .../apub/src/activities/following/follow.rs | 3 ++ crates/apub/src/activities/mod.rs | 47 ++++++------------- .../src/activities/post/create_or_update.rs | 23 +++++++-- .../private_message/create_or_update.rs | 4 +- crates/apub/src/activities/report.rs | 3 +- .../apub/src/activities/voting/undo_vote.rs | 19 +++++++- crates/apub/src/activities/voting/vote.rs | 35 ++++++++++++-- .../apub/src/collections/community_outbox.rs | 7 ++- crates/apub/src/http/community.rs | 18 +++++-- crates/apub/src/lib.rs | 35 +------------- crates/apub/src/objects/comment.rs | 33 +++++++++---- crates/apub/src/objects/community.rs | 20 ++------ crates/apub/src/objects/post.rs | 44 ++++++++++++----- crates/apub/src/objects/private_message.rs | 12 ++--- crates/apub_lib/src/traits.rs | 1 - crates/apub_lib_derive/src/lib.rs | 20 +------- 28 files changed, 386 insertions(+), 185 deletions(-) diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 537563e9b..f8469be92 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -2,8 +2,10 @@ use crate::{ activities::{ check_community_deleted_or_removed, comment::{collect_non_local_mentions, get_notif_recipients}, - community::{announce::AnnouncableActivities, send_to_community}, - extract_community, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -108,12 +110,11 @@ impl ActivityHandler for CreateOrUpdateComment { request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; - let community = extract_community(&self.cc, context, request_counter).await?; - let community_id = ObjectId::new(community.actor_id()); let post = self.object.get_parents(context, request_counter).await?.0; + let community = self.get_community(context, request_counter).await?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &community_id, context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_domains_match(self.actor.inner(), self.object.id_unchecked())?; check_community_deleted_or_removed(&community)?; check_post_deleted_or_removed(&post)?; @@ -144,6 +145,22 @@ impl ActivityHandler for CreateOrUpdateComment { } } +#[async_trait::async_trait(?Send)] +impl GetCommunity for CreateOrUpdateComment { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let post = self.object.get_parents(context, request_counter).await?.0; + let community = blocking(context.pool(), move |conn| { + Community::read(conn, post.community_id) + }) + .await??; + Ok(community.into()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index 6df6f8127..876fcc94e 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -1,6 +1,10 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + get_community_from_moderators_url, + send_to_community, + }, generate_activity_id, verify_activity, verify_add_remove_moderator_target, @@ -91,7 +95,8 @@ impl ActivityHandler for AddMod { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; verify_add_remove_moderator_target(&self.target, &self.cc[0])?; Ok(()) @@ -102,7 +107,7 @@ impl ActivityHandler for AddMod { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = self.cc[0].dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let new_mod = self.object.dereference(context, request_counter).await?; // If we had to refetch the community while parsing the activity, then the new mod has already @@ -126,3 +131,14 @@ impl ActivityHandler for AddMod { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for AddMod { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + get_community_from_moderators_url(&self.target, context, request_counter).await + } +} diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index deca2b73a..6165eb2f0 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -13,7 +13,6 @@ use crate::{ generate_activity_id, post::create_or_update::CreateOrUpdatePost, verify_activity, - verify_community, verify_is_public, voting::{undo_vote::UndoVote, vote::Vote}, }, @@ -23,7 +22,6 @@ use crate::{ insert_activity, objects::community::ApubCommunity, send_lemmy_activity, - CommunityType, }; use activitystreams::{ activity::kind::AnnounceType, @@ -35,6 +33,7 @@ use activitystreams::{ use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, + verify::verify_urls_match, }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; @@ -58,6 +57,41 @@ pub enum AnnouncableActivities { RemoveMod(RemoveMod), } +#[async_trait::async_trait(?Send)] +pub(crate) trait GetCommunity { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result; +} + +#[async_trait::async_trait(?Send)] +impl GetCommunity for AnnouncableActivities { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + use AnnouncableActivities::*; + let community = match self { + CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?, + CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?, + Vote(a) => a.get_community(context, request_counter).await?, + UndoVote(a) => a.get_community(context, request_counter).await?, + Delete(a) => a.get_community(context, request_counter).await?, + UndoDelete(a) => a.get_community(context, request_counter).await?, + UpdateCommunity(a) => a.get_community(context, request_counter).await?, + BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, + UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, + AddMod(a) => a.get_community(context, request_counter).await?, + RemoveMod(a) => a.get_community(context, request_counter).await?, + }; + verify_urls_match(self.actor(), &community.actor_id())?; + Ok(community) + } +} + #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct AnnounceActivity { @@ -85,7 +119,7 @@ impl AnnounceActivity { actor: ObjectId::new(community.actor_id()), to: vec![public()], object, - cc: vec![community.followers_url()], + cc: vec![community.followers_url.clone().into_inner()], kind: AnnounceType::Announce, id: generate_activity_id( &AnnounceType::Announce, @@ -109,7 +143,6 @@ impl ActivityHandler for AnnounceActivity { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_community(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs index 64a030c8b..35cde2e6e 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/community/block_user.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -44,6 +47,7 @@ pub struct BlockUserFromCommunity { to: Vec, pub(in crate::activities::community) object: ObjectId, cc: [ObjectId; 1], + target: ObjectId, #[serde(rename = "type")] kind: BlockType, id: Url, @@ -65,6 +69,7 @@ impl BlockUserFromCommunity { to: vec![public()], object: ObjectId::new(target.actor_id()), cc: [ObjectId::new(community.actor_id())], + target: ObjectId::new(community.actor_id()), kind: BlockType::Block, id: generate_activity_id( BlockType::Block, @@ -100,7 +105,8 @@ impl ActivityHandler for BlockUserFromCommunity { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; Ok(()) } @@ -110,7 +116,7 @@ impl ActivityHandler for BlockUserFromCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = self.cc[0].dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let blocked_user = self.object.dereference(context, request_counter).await?; let community_user_ban_form = CommunityPersonBanForm { @@ -138,3 +144,14 @@ impl ActivityHandler for BlockUserFromCommunity { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for BlockUserFromCommunity { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self.target.dereference(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index f69ee026e..9db9d8148 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -1,10 +1,10 @@ use crate::{ activities::community::announce::{AnnouncableActivities, AnnounceActivity}, check_is_apub_id_valid, + fetcher::object_id::ObjectId, insert_activity, objects::community::ApubCommunity, send_lemmy_activity, - CommunityType, }; use itertools::Itertools; use lemmy_apub_lib::traits::ActorType; @@ -61,3 +61,14 @@ pub(crate) async fn send_to_community( Ok(()) } + +async fn get_community_from_moderators_url( + moderators: &Url, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result { + let community_id = Url::parse(&moderators.to_string().replace("/moderators", ""))?; + ObjectId::new(community_id) + .dereference(context, request_counter) + .await +} diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index d1a5675a5..f08ae9646 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -1,6 +1,10 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + get_community_from_moderators_url, + send_to_community, + }, generate_activity_id, verify_activity, verify_add_remove_moderator_target, @@ -43,7 +47,6 @@ pub struct RemoveMod { cc: [ObjectId; 1], #[serde(rename = "type")] kind: RemoveType, - // if target is set, this is means remove mod from community pub(in crate::activities) target: Url, id: Url, #[serde(rename = "@context")] @@ -91,7 +94,8 @@ impl ActivityHandler for RemoveMod { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; verify_add_remove_moderator_target(&self.target, &self.cc[0])?; Ok(()) @@ -102,7 +106,7 @@ impl ActivityHandler for RemoveMod { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = self.cc[0].dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let remove_mod = self.object.dereference(context, request_counter).await?; let form = CommunityModeratorForm { @@ -117,3 +121,14 @@ impl ActivityHandler for RemoveMod { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for RemoveMod { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + get_community_from_moderators_url(&self.target, context, request_counter).await + } +} diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs index ee36d1ae3..c33c81e6e 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/community/undo_block_user.rs @@ -1,7 +1,7 @@ use crate::{ activities::{ community::{ - announce::AnnouncableActivities, + announce::{AnnouncableActivities, GetCommunity}, block_user::BlockUserFromCommunity, send_to_community, }, @@ -92,7 +92,8 @@ impl ActivityHandler for UndoBlockUserFromCommunity { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) @@ -103,7 +104,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = self.cc[0].dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let blocked_user = self .object .object @@ -123,3 +124,14 @@ impl ActivityHandler for UndoBlockUserFromCommunity { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for UndoBlockUserFromCommunity { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self.object.get_community(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index 6c78dbfb9..138374335 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -90,7 +93,8 @@ impl ActivityHandler for UpdateCommunity { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; Ok(()) } @@ -100,8 +104,7 @@ impl ActivityHandler for UpdateCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let cc = self.cc[0].clone(); - let community = cc.dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let updated_community = Group::from_apub_to_form( &self.object, @@ -135,3 +138,15 @@ impl ActivityHandler for UpdateCommunity { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for UpdateCommunity { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let cid = ObjectId::new(self.object.id.clone()); + cid.dereference(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 7593c5bf4..8797ed6b1 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, deletion::{ receive_delete_action, verify_delete_activity, @@ -245,3 +248,26 @@ pub(in crate::activities) async fn receive_remove_action( } Ok(()) } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for Delete { + async fn get_community( + &self, + context: &LemmyContext, + _request_counter: &mut i32, + ) -> Result { + let community_id = match DeletableObjects::read_from_db(&self.object, context).await? { + DeletableObjects::Community(c) => c.id, + DeletableObjects::Comment(c) => { + let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??; + post.community_id + } + DeletableObjects::Post(p) => p.community_id, + }; + let community = blocking(context.pool(), move |conn| { + Community::read(conn, community_id) + }) + .await??; + Ok(community.into()) + } +} diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index 4352afe87..f523ca71e 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -94,7 +94,7 @@ pub(in crate::activities) async fn verify_delete_activity( if c.local { // can only do this check for local community, in remote case it would try to fetch the // deleted community (which fails) - verify_person_in_community(&actor, community_id, context, request_counter).await?; + verify_person_in_community(&actor, &c, context, request_counter).await?; } // community deletion is always a mod (or admin) action verify_mod_action( @@ -140,7 +140,8 @@ async fn verify_delete_activity_post_or_comment( request_counter: &mut i32, ) -> Result<(), LemmyError> { let actor = ObjectId::new(activity.actor().clone()); - verify_person_in_community(&actor, community_id, context, request_counter).await?; + let community = community_id.dereference(context, request_counter).await?; + verify_person_in_community(&actor, &community, context, request_counter).await?; if is_mod_action { verify_mod_action(&actor, community_id, context, request_counter).await?; } else { diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index 87c8eae7f..d2816b114 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, deletion::{ delete::Delete, receive_delete_action, @@ -166,3 +169,14 @@ impl UndoDelete { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for UndoDelete { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self.object.get_community(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index 78d12b523..92f531842 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -1,10 +1,5 @@ use crate::{ - activities::{ - following::follow::FollowCommunity, - generate_activity_id, - verify_activity, - verify_community, - }, + activities::{following::follow::FollowCommunity, generate_activity_id, verify_activity}, context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, @@ -84,7 +79,6 @@ impl ActivityHandler for AcceptFollowCommunity { verify_activity(self, &context.settings())?; verify_urls_match(self.to[0].inner(), self.object.actor())?; verify_urls_match(self.actor(), self.object.to[0].inner())?; - verify_community(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 82d2d5408..4710f70f0 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -4,6 +4,7 @@ use crate::{ generate_activity_id, verify_activity, verify_person, + verify_person_in_community, }, context::lemmy_context, fetcher::object_id::ObjectId, @@ -97,6 +98,8 @@ impl ActivityHandler for FollowCommunity { verify_activity(self, &context.settings())?; verify_urls_match(self.to[0].inner(), self.object.inner())?; verify_person(&self.actor, context, request_counter).await?; + let community = self.to[0].dereference(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 14a67394e..b180b6f3c 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -1,5 +1,4 @@ use crate::{ - check_community_or_site_ban, check_is_apub_id_valid, fetcher::object_id::ObjectId, generate_moderators_url, @@ -10,11 +9,13 @@ use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{traits::ActivityFields, verify::verify_domains_match}; use lemmy_db_schema::source::community::Community; -use lemmy_db_views_actor::community_view::CommunityView; +use lemmy_db_views_actor::{ + community_person_ban_view::CommunityPersonBanView, + community_view::CommunityView, +}; use lemmy_utils::{settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; -use std::ops::Deref; use strum_macros::ToString; use url::{ParseError, Url}; use uuid::Uuid; @@ -48,44 +49,26 @@ async fn verify_person( Ok(()) } -pub(crate) async fn extract_community( - cc: &[Url], - context: &LemmyContext, - request_counter: &mut i32, -) -> Result { - let mut cc_iter = cc.iter(); - loop { - if let Some(cid) = cc_iter.next() { - let cid = ObjectId::new(cid.clone()); - if let Ok(c) = cid.dereference(context, request_counter).await { - break Ok(c); - } - } else { - return Err(anyhow!("No community found in cc").into()); - } - } -} - /// Fetches the person and community to verify their type, then checks if person is banned from site /// or community. pub(crate) async fn verify_person_in_community( person_id: &ObjectId, - community_id: &ObjectId, + community: &ApubCommunity, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = community_id.dereference(context, request_counter).await?; let person = person_id.dereference(context, request_counter).await?; - check_community_or_site_ban(person.deref(), community.id, context.pool()).await -} + if person.banned { + return Err(anyhow!("Person is banned from site").into()); + } + let person_id = person.id; + let community_id = community.id; + let is_banned = + move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok(); + if blocking(context.pool(), is_banned).await? { + return Err(anyhow!("Person is banned from community").into()); + } -/// Simply check that the url actually refers to a valid group. -async fn verify_community( - community_id: &ObjectId, - context: &LemmyContext, - request_counter: &mut i32, -) -> Result<(), LemmyError> { - community_id.dereference(context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index b3c0b6942..b18d100e0 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -1,7 +1,10 @@ use crate::{ activities::{ check_community_deleted_or_removed, - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -99,8 +102,8 @@ impl ActivityHandler for CreateOrUpdatePost { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - let community = self.cc[0].dereference(context, request_counter).await?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; check_community_deleted_or_removed(&community)?; match self.kind { @@ -148,3 +151,17 @@ impl ActivityHandler for CreateOrUpdatePost { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for CreateOrUpdatePost { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self + .object + .extract_community(context, request_counter) + .await + } +} diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs index d6552b37a..22f8edb8e 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -4,7 +4,7 @@ use crate::{ fetcher::object_id::ObjectId, objects::{ person::ApubPerson, - private_message::{ApubPrivateMessage, Note}, + private_message::{ApubPrivateMessage, ChatMessage}, }, send_lemmy_activity, }; @@ -29,7 +29,7 @@ pub struct CreateOrUpdatePrivateMessage { id: Url, actor: ObjectId, to: [ObjectId; 1], - object: Note, + object: ChatMessage, #[serde(rename = "type")] kind: CreateOrUpdateType, #[serde(flatten)] diff --git a/crates/apub/src/activities/report.rs b/crates/apub/src/activities/report.rs index d0b1f971c..24041b0ff 100644 --- a/crates/apub/src/activities/report.rs +++ b/crates/apub/src/activities/report.rs @@ -91,7 +91,8 @@ impl ActivityHandler for Report { request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.to[0], context, request_counter).await?; + let community = self.to[0].dereference(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index 1ac1d4d8d..6d63e9053 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -96,7 +99,8 @@ impl ActivityHandler for UndoVote { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_urls_match(self.actor(), self.object.actor())?; self.object.verify(context, request_counter).await?; Ok(()) @@ -119,3 +123,14 @@ impl ActivityHandler for UndoVote { } } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for UndoVote { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self.object.get_community(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 166ce7331..4d6697283 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -19,7 +22,11 @@ use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, }; -use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud}; +use lemmy_db_schema::{ + newtypes::CommunityId, + source::{community::Community, post::Post}, + traits::Crud, +}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; @@ -120,7 +127,8 @@ impl ActivityHandler for Vote { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; Ok(()) } @@ -137,3 +145,24 @@ impl ActivityHandler for Vote { } } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for Vote { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let object = self.object.dereference(context, request_counter).await?; + let cid = match object { + PostOrComment::Post(p) => p.community_id, + PostOrComment::Comment(c) => { + blocking(context.pool(), move |conn| Post::read(conn, c.post_id)) + .await?? + .community_id + } + }; + let community = blocking(context.pool(), move |conn| Community::read(conn, cid)).await??; + Ok(community.into()) + } +} diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index faf33849e..24465c95c 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -6,8 +6,11 @@ use crate::{ objects::{person::ApubPerson, post::ApubPost}, }; use activitystreams::{ - base::AnyBase, chrono::NaiveDateTime, collection::kind::OrderedCollectionType, - primitives::OneOrMany, url::Url, + base::AnyBase, + chrono::NaiveDateTime, + collection::kind::OrderedCollectionType, + primitives::OneOrMany, + url::Url, }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index a7e66d8a3..f39f7b6f0 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -1,9 +1,9 @@ use crate::{ activities::{ - community::announce::{AnnouncableActivities, AnnounceActivity}, - extract_community, + community::announce::{AnnouncableActivities, AnnounceActivity, GetCommunity}, following::{follow::FollowCommunity, undo::UndoFollowCommunity}, report::Report, + verify_person_in_community, }, collections::{ community_moderators::ApubCommunityModerators, @@ -27,7 +27,10 @@ use activitystreams::{ }; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use lemmy_api_common::blocking; -use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject}; +use lemmy_apub_lib::{ + traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, + verify::verify_domains_match, +}; use lemmy_db_schema::source::community::Community; use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; use lemmy_utils::LemmyError; @@ -92,12 +95,17 @@ pub(in crate::http) async fn receive_group_inbox( context: &LemmyContext, ) -> Result { let res = receive_activity(request, activity.clone(), context).await; - if let GroupInboxActivities::AnnouncableActivities(announcable) = activity.clone() { - let community = extract_community(&announcable.cc(), context, &mut 0).await?; + + if let GroupInboxActivities::AnnouncableActivities(announcable) = activity { + let community = announcable.get_community(context, &mut 0).await?; + let actor_id = ObjectId::new(announcable.actor().clone()); + verify_domains_match(&community.actor_id(), announcable.id_unchecked())?; + verify_person_in_community(&actor_id, &community, context, &mut 0).await?; if community.local { AnnounceActivity::send(announcable, &community, vec![], context).await?; } } + res } diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index a900ad3d1..3bf3ff253 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -17,12 +17,7 @@ use lemmy_apub_lib::{ traits::ActorType, webfinger::{webfinger_resolve_actor, WebfingerType}, }; -use lemmy_db_schema::{ - newtypes::{CommunityId, DbUrl}, - source::{activity::Activity, person::Person}, - DbPool, -}; -use lemmy_db_views_actor::community_person_ban_view::CommunityPersonBanView; +use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, DbPool}; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; use log::info; @@ -96,16 +91,6 @@ pub(crate) fn check_is_apub_id_valid( Ok(()) } -#[async_trait::async_trait(?Send)] -pub trait CommunityType { - fn followers_url(&self) -> Url; - async fn get_follower_inboxes( - &self, - pool: &DbPool, - settings: &Settings, - ) -> Result, LemmyError>; -} - pub enum EndpointType { Community, Person, @@ -211,24 +196,6 @@ where Ok(()) } -async fn check_community_or_site_ban( - person: &Person, - community_id: CommunityId, - pool: &DbPool, -) -> Result<(), LemmyError> { - if person.banned { - return Err(anyhow!("Person is banned from site").into()); - } - let person_id = person.id; - let is_banned = - move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok(); - if blocking(pool, is_banned).await? { - return Err(anyhow!("Person is banned from community").into()); - } - - Ok(()) -} - pub(crate) async fn send_lemmy_activity( context: &LemmyContext, activity: &T, diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 927127d8f..335605a6c 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -2,7 +2,13 @@ use crate::{ activities::{verify_is_public, verify_person_in_community}, context::lemmy_context, fetcher::object_id::ObjectId, - objects::{person::ApubPerson, post::ApubPost, tombstone::Tombstone, Source}, + objects::{ + community::ApubCommunity, + person::ApubPerson, + post::ApubPost, + tombstone::Tombstone, + Source, + }, PostOrComment, }; use activitystreams::{ @@ -121,22 +127,17 @@ impl Note { ) -> Result<(), LemmyError> { let (post, _parent_comment_id) = self.get_parents(context, request_counter).await?; let community_id = post.community_id; - let community = blocking(context.pool(), move |conn| { + let community: ApubCommunity = blocking(context.pool(), move |conn| { Community::read(conn, community_id) }) - .await??; + .await?? + .into(); if post.locked { return Err(anyhow!("Post is locked").into()); } verify_domains_match(self.attributed_to.inner(), &self.id)?; - verify_person_in_community( - &self.attributed_to, - &ObjectId::new(community.actor_id), - context, - request_counter, - ) - .await?; + verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; verify_is_public(&self.to)?; Ok(()) } @@ -247,6 +248,18 @@ impl ApubObject for ApubComment { .dereference(context, request_counter) .await?; let (post, parent_comment_id) = note.get_parents(context, request_counter).await?; + let community_id = post.community_id; + let community = blocking(context.pool(), move |conn| { + Community::read(conn, community_id) + }) + .await??; + verify_person_in_community( + ¬e.attributed_to, + &community.into(), + context, + request_counter, + ) + .await?; if post.locked { return Err(anyhow!("Post is locked").into()); } diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index e71addba5..a38e759fa 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -10,7 +10,6 @@ use crate::{ generate_moderators_url, generate_outbox_url, objects::{get_summary_from_string_or_source, tombstone::Tombstone, ImageObject, Source}, - CommunityType, }; use activitystreams::{ actor::{kind::GroupType, Endpoints}, @@ -55,7 +54,7 @@ pub struct Group { context: OneOrMany, #[serde(rename = "type")] kind: GroupType, - id: Url, + pub(crate) id: Url, /// username, set at account creation and can never be changed preferred_username: String, /// title (can be changed at any time) @@ -81,16 +80,12 @@ pub struct Group { } impl Group { - pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> { - verify_domains_match(&self.id, expected_domain)?; - Ok(&self.id) - } pub(crate) async fn from_apub_to_form( group: &Group, expected_domain: &Url, settings: &Settings, ) -> Result { - let actor_id = Some(group.id(expected_domain)?.clone().into()); + verify_domains_match(expected_domain, &group.id)?; let name = group.preferred_username.clone(); let title = group.name.clone(); let description = get_summary_from_string_or_source(&group.summary, &group.source); @@ -110,7 +105,7 @@ impl Group { updated: group.updated.map(|u| u.naive_local()), deleted: None, nsfw: Some(group.sensitive.unwrap_or(false)), - actor_id, + actor_id: Some(group.id.clone().into()), local: Some(false), private_key: None, public_key: Some(group.public_key.public_key_pem.clone()), @@ -283,14 +278,9 @@ impl ActorType for ApubCommunity { } } -#[async_trait::async_trait(?Send)] -impl CommunityType for Community { - fn followers_url(&self) -> Url { - self.followers_url.clone().into() - } - +impl ApubCommunity { /// For a given community, returns the inboxes of all followers. - async fn get_follower_inboxes( + pub(crate) async fn get_follower_inboxes( &self, pool: &DbPool, settings: &Settings, diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 8b0161883..f15bb5baf 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -1,8 +1,14 @@ use crate::{ - activities::{extract_community, verify_is_public, verify_person_in_community}, + activities::{verify_is_public, verify_person_in_community}, context::lemmy_context, fetcher::object_id::ObjectId, - objects::{person::ApubPerson, tombstone::Tombstone, ImageObject, Source}, + objects::{ + community::ApubCommunity, + person::ApubPerson, + tombstone::Tombstone, + ImageObject, + Source, + }, }; use activitystreams::{ base::AnyBase, @@ -11,10 +17,11 @@ use activitystreams::{ public, unparsed::Unparsed, }; +use anyhow::anyhow; use chrono::{DateTime, FixedOffset, NaiveDateTime}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ - traits::{ActorType, ApubObject}, + traits::ApubObject, values::{MediaTypeHtml, MediaTypeMarkdown}, verify::verify_domains_match, }; @@ -94,20 +101,32 @@ impl Page { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = extract_community(&self.to, context, request_counter).await?; + let community = self.extract_community(context, request_counter).await?; check_slurs(&self.name, &context.settings().slur_regex())?; verify_domains_match(self.attributed_to.inner(), &self.id.clone())?; - verify_person_in_community( - &self.attributed_to, - &ObjectId::new(community.actor_id()), - context, - request_counter, - ) - .await?; + verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; verify_is_public(&self.to.clone())?; Ok(()) } + + pub(crate) async fn extract_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let mut to_iter = self.to.iter(); + loop { + if let Some(cid) = to_iter.next() { + let cid = ObjectId::new(cid.clone()); + if let Ok(c) = cid.dereference(context, request_counter).await { + break Ok(c); + } + } else { + return Err(anyhow!("No community found in cc").into()); + } + } + } } #[derive(Clone, Debug)] @@ -223,7 +242,8 @@ impl ApubObject for ApubPost { .attributed_to .dereference(context, request_counter) .await?; - let community = extract_community(&page.to, context, request_counter).await?; + let community = page.extract_community(context, request_counter).await?; + verify_person_in_community(&page.attributed_to, &community, context, request_counter).await?; let thumbnail_url: Option = page.image.clone().map(|i| i.url); let (metadata_res, pictrs_thumbnail) = if let Some(url) = &page.url { diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 407ae8101..56200d1be 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -36,7 +36,7 @@ use url::Url; #[skip_serializing_none] #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct Note { +pub struct ChatMessage { #[serde(rename = "@context")] context: OneOrMany, r#type: ChatMessageType, @@ -58,7 +58,7 @@ pub enum ChatMessageType { ChatMessage, } -impl Note { +impl ChatMessage { pub(crate) fn id_unchecked(&self) -> &Url { &self.id } @@ -103,7 +103,7 @@ impl From for ApubPrivateMessage { #[async_trait::async_trait(?Send)] impl ApubObject for ApubPrivateMessage { type DataType = LemmyContext; - type ApubType = Note; + type ApubType = ChatMessage; type TombstoneType = Tombstone; fn last_refreshed_at(&self) -> Option { @@ -128,7 +128,7 @@ impl ApubObject for ApubPrivateMessage { unimplemented!() } - async fn to_apub(&self, context: &LemmyContext) -> Result { + async fn to_apub(&self, context: &LemmyContext) -> Result { let creator_id = self.creator_id; let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??; @@ -136,7 +136,7 @@ impl ApubObject for ApubPrivateMessage { let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; - let note = Note { + let note = ChatMessage { context: lemmy_context(), r#type: ChatMessageType::ChatMessage, id: self.ap_id.clone().into(), @@ -160,7 +160,7 @@ impl ApubObject for ApubPrivateMessage { } async fn from_apub( - note: &Note, + note: &ChatMessage, context: &LemmyContext, expected_domain: &Url, request_counter: &mut i32, diff --git a/crates/apub_lib/src/traits.rs b/crates/apub_lib/src/traits.rs index 2240946d4..8d819a27b 100644 --- a/crates/apub_lib/src/traits.rs +++ b/crates/apub_lib/src/traits.rs @@ -8,7 +8,6 @@ use url::Url; pub trait ActivityFields { fn id_unchecked(&self) -> &Url; fn actor(&self) -> &Url; - fn cc(&self) -> Vec; } #[async_trait::async_trait(?Send)] diff --git a/crates/apub_lib_derive/src/lib.rs b/crates/apub_lib_derive/src/lib.rs index 44185d14e..1f8d12a7d 100644 --- a/crates/apub_lib_derive/src/lib.rs +++ b/crates/apub_lib_derive/src/lib.rs @@ -145,36 +145,18 @@ pub fn derive_activity_fields(input: proc_macro::TokenStream) -> proc_macro::Tok let impl_actor = variants .iter() .map(|v| generate_match_arm(&name, v, "e! {a.actor()})); - let impl_cc = variants - .iter() - .map(|v| generate_match_arm(&name, v, "e! {a.cc()})); quote! { impl #impl_generics lemmy_apub_lib::traits::ActivityFields for #name #ty_generics #where_clause { fn id_unchecked(&self) -> &url::Url { match self { #(#impl_id)* } } fn actor(&self) -> &url::Url { match self { #(#impl_actor)* } } - fn cc(&self) -> Vec { match self { #(#impl_cc)* } } } } } - Data::Struct(s) => { - // check if the struct has a field "cc", and generate impl for cc() function depending on that - let has_cc = if let syn::Fields::Named(n) = s.fields { - n.named - .iter() - .any(|i| format!("{}", i.ident.as_ref().unwrap()) == "cc") - } else { - unimplemented!() - }; - let cc_impl = if has_cc { - quote! {self.cc.iter().map(|i| i.clone().into()).collect()} - } else { - quote! {vec![]} - }; + Data::Struct(_) => { quote! { impl #impl_generics lemmy_apub_lib::traits::ActivityFields for #name #ty_generics #where_clause { fn id_unchecked(&self) -> &url::Url { &self.id } fn actor(&self) -> &url::Url { &self.actor.inner() } - fn cc(&self) -> Vec { #cc_impl } } } }