From b3a5b4eb820b7d431bb9748ef1797bc9407caacb Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 17 Mar 2021 18:12:37 +0100 Subject: [PATCH] Refactor activitypub code --- crates/api/src/community.rs | 9 +- crates/apub/src/activities/receive/comment.rs | 3 +- .../apub/src/activities/receive/community.rs | 59 +--------- crates/apub/src/activities/receive/post.rs | 3 +- crates/apub/src/activities/send/community.rs | 106 +++++++----------- crates/apub/src/activities/send/user.rs | 44 +------- crates/apub/src/activity_queue.rs | 1 + crates/apub/src/inbox/community_inbox.rs | 1 + .../apub/src/inbox/receive_for_community.rs | 16 ++- crates/apub/src/inbox/user_inbox.rs | 41 ++++++- crates/apub/src/lib.rs | 82 ++++++++------ 11 files changed, 158 insertions(+), 207 deletions(-) diff --git a/crates/api/src/community.rs b/crates/api/src/community.rs index bb341294b..0bb64f373 100644 --- a/crates/api/src/community.rs +++ b/crates/api/src/community.rs @@ -10,13 +10,14 @@ use actix_web::web::Data; use anyhow::Context; use lemmy_api_structs::{blocking, community::*}; use lemmy_apub::{ - activities::send::community::{send_add_mod, send_remove_mod}, generate_apub_endpoint, generate_followers_url, generate_inbox_url, generate_shared_inbox_url, ActorType, + CommunityType, EndpointType, + UserType, }; use lemmy_db_queries::{ diesel_option_overwrite_to_url, @@ -745,9 +746,11 @@ impl Perform for AddModToCommunity { }) .await??; if data.added { - send_add_mod(user, updated_mod, community, context).await?; + community.send_add_mod(&user, updated_mod, context).await?; } else { - send_remove_mod(user, updated_mod, community, context).await?; + community + .send_remove_mod(&user, updated_mod, context) + .await?; } // Note: in case a remote mod is added, this returns the old moderators list, it will only get diff --git a/crates/apub/src/activities/receive/comment.rs b/crates/apub/src/activities/receive/comment.rs index bc1507931..07f25eec7 100644 --- a/crates/apub/src/activities/receive/comment.rs +++ b/crates/apub/src/activities/receive/comment.rs @@ -1,6 +1,6 @@ use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, NoteExt}; use activitystreams::{ - activity::{ActorAndObjectRefExt, Create, Dislike, Like, Remove, Update}, + activity::{ActorAndObjectRefExt, Create, Dislike, Like, Update}, base::ExtendsExt, }; use anyhow::Context; @@ -221,7 +221,6 @@ pub(crate) async fn receive_delete_comment( pub(crate) async fn receive_remove_comment( context: &LemmyContext, - _remove: Remove, comment: Comment, ) -> Result<(), LemmyError> { let removed_comment = blocking(context.pool(), move |conn| { diff --git a/crates/apub/src/activities/receive/community.rs b/crates/apub/src/activities/receive/community.rs index cf85ad106..48f6b295d 100644 --- a/crates/apub/src/activities/receive/community.rs +++ b/crates/apub/src/activities/receive/community.rs @@ -1,19 +1,9 @@ -use crate::{ - activities::receive::verify_activity_domains_valid, - inbox::verify_is_addressed_to_public, -}; -use activitystreams::{ - activity::{ActorAndObjectRefExt, Delete, Remove, Undo}, - base::{AnyBase, ExtendsExt}, -}; -use anyhow::Context; use lemmy_api_structs::{blocking, community::CommunityResponse}; -use lemmy_db_queries::{source::community::Community_, ApubObject}; +use lemmy_db_queries::source::community::Community_; use lemmy_db_schema::source::community::Community; use lemmy_db_views_actor::community_view::CommunityView; -use lemmy_utils::{location_info, LemmyError}; +use lemmy_utils::LemmyError; use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation}; -use url::Url; pub(crate) async fn receive_delete_community( context: &LemmyContext, @@ -45,23 +35,8 @@ pub(crate) async fn receive_delete_community( pub(crate) async fn receive_remove_community( context: &LemmyContext, - activity: AnyBase, - expected_domain: &Url, + community: Community, ) -> Result<(), LemmyError> { - let remove = Remove::from_any_base(activity)?.context(location_info!())?; - verify_activity_domains_valid(&remove, expected_domain, true)?; - verify_is_addressed_to_public(&remove)?; - - let community_uri = remove - .object() - .to_owned() - .single_xsd_any_uri() - .context(location_info!())?; - let community = blocking(context.pool(), move |conn| { - Community::read_from_apub_id(conn, &community_uri.into()) - }) - .await??; - let removed_community = blocking(context.pool(), move |conn| { Community::update_removed(conn, community.id, true) }) @@ -88,16 +63,8 @@ pub(crate) async fn receive_remove_community( pub(crate) async fn receive_undo_delete_community( context: &LemmyContext, - undo: Undo, community: Community, - expected_domain: &Url, ) -> Result<(), LemmyError> { - verify_is_addressed_to_public(&undo)?; - let inner = undo.object().to_owned().one().context(location_info!())?; - let delete = Delete::from_any_base(inner)?.context(location_info!())?; - verify_activity_domains_valid(&delete, expected_domain, true)?; - verify_is_addressed_to_public(&delete)?; - let deleted_community = blocking(context.pool(), move |conn| { Community::update_deleted(conn, community.id, false) }) @@ -124,26 +91,8 @@ pub(crate) async fn receive_undo_delete_community( pub(crate) async fn receive_undo_remove_community( context: &LemmyContext, - undo: Undo, - expected_domain: &Url, + community: Community, ) -> Result<(), LemmyError> { - verify_is_addressed_to_public(&undo)?; - - let inner = undo.object().to_owned().one().context(location_info!())?; - let remove = Remove::from_any_base(inner)?.context(location_info!())?; - verify_activity_domains_valid(&remove, &expected_domain, true)?; - verify_is_addressed_to_public(&remove)?; - - let community_uri = remove - .object() - .to_owned() - .single_xsd_any_uri() - .context(location_info!())?; - let community = blocking(context.pool(), move |conn| { - Community::read_from_apub_id(conn, &community_uri.into()) - }) - .await??; - let removed_community = blocking(context.pool(), move |conn| { Community::update_removed(conn, community.id, false) }) diff --git a/crates/apub/src/activities/receive/post.rs b/crates/apub/src/activities/receive/post.rs index 33b2ce67c..b0582e60e 100644 --- a/crates/apub/src/activities/receive/post.rs +++ b/crates/apub/src/activities/receive/post.rs @@ -6,7 +6,7 @@ use crate::{ PageExt, }; use activitystreams::{ - activity::{Announce, Create, Dislike, Like, Remove, Update}, + activity::{Announce, Create, Dislike, Like, Update}, prelude::*, }; use anyhow::Context; @@ -216,7 +216,6 @@ pub(crate) async fn receive_delete_post( pub(crate) async fn receive_remove_post( context: &LemmyContext, - _remove: Remove, post: Post, ) -> Result<(), LemmyError> { let removed_post = blocking(context.pool(), move |conn| { diff --git a/crates/apub/src/activities/send/community.rs b/crates/apub/src/activities/send/community.rs index ff445ac60..c6c90d502 100644 --- a/crates/apub/src/activities/send/community.rs +++ b/crates/apub/src/activities/send/community.rs @@ -6,6 +6,7 @@ use crate::{ fetcher::user::get_or_fetch_and_upsert_user, generate_moderators_url, ActorType, + CommunityType, }; use activitystreams::{ activity::{ @@ -56,23 +57,10 @@ impl ActorType for Community { .unwrap_or_else(|| self.inbox_url.to_owned()) .into() } +} - async fn send_follow( - &self, - _follow_actor_id: &Url, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn send_unfollow( - &self, - _follow_actor_id: &Url, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - +#[async_trait::async_trait(?Send)] +impl CommunityType for Community { /// As a local community, accept the follow request from a remote user. async fn send_accept_follow( &self, @@ -177,7 +165,7 @@ impl ActorType for Community { .set_many_contexts(lemmy_context()?) .set_id(generate_activity_id(AnnounceType::Announce)?) .set_to(public()) - .set_many_ccs(vec![self.followers_url.clone().into_inner()]); + .set_many_ccs(vec![self.actor_id()]); send_to_community_followers(announce, self, context).await?; @@ -204,58 +192,46 @@ impl ActorType for Community { Ok(inboxes) } -} -pub async fn send_add_mod( - actor: User_, - added_mod: User_, - community: Community, - context: &LemmyContext, -) -> Result<(), LemmyError> { - let mut add = Add::new( - actor.actor_id.clone().into_inner(), - added_mod.actor_id.into_inner(), - ); - add - .set_many_contexts(lemmy_context()?) - .set_id(generate_activity_id(AddType::Add)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]) - .set_target(generate_moderators_url(&community.actor_id)?.into_inner()); + async fn send_add_mod( + &self, + actor: &User_, + added_mod: User_, + context: &LemmyContext, + ) -> Result<(), LemmyError> { + let mut add = Add::new( + actor.actor_id.clone().into_inner(), + added_mod.actor_id.into_inner(), + ); + add + .set_many_contexts(lemmy_context()?) + .set_id(generate_activity_id(AddType::Add)?) + .set_to(public()) + .set_many_ccs(vec![self.actor_id()]) + .set_target(generate_moderators_url(&self.actor_id)?.into_inner()); - if community.local { - community - .send_announce(add.into_any_base()?, context) - .await?; - } else { - send_to_community(add, &actor, &community, context).await?; + send_to_community(add, actor, self, context).await?; + Ok(()) } - Ok(()) -} -pub async fn send_remove_mod( - actor: User_, - removed_mod: User_, - community: Community, - context: &LemmyContext, -) -> Result<(), LemmyError> { - let mut remove = Remove::new( - actor.actor_id.clone().into_inner(), - removed_mod.actor_id.into_inner(), - ); - remove - .set_many_contexts(lemmy_context()?) - .set_id(generate_activity_id(RemoveType::Remove)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]) - .set_target(generate_moderators_url(&community.actor_id)?.into_inner()); + async fn send_remove_mod( + &self, + actor: &User_, + removed_mod: User_, + context: &LemmyContext, + ) -> Result<(), LemmyError> { + let mut remove = Remove::new( + actor.actor_id.clone().into_inner(), + removed_mod.actor_id.into_inner(), + ); + remove + .set_many_contexts(lemmy_context()?) + .set_id(generate_activity_id(RemoveType::Remove)?) + .set_to(public()) + .set_many_ccs(vec![self.actor_id()]) + .set_target(generate_moderators_url(&self.actor_id)?.into_inner()); - if community.local { - community - .send_announce(remove.into_any_base()?, context) - .await?; - } else { - send_to_community(remove, &actor, &community, context).await?; + send_to_community(remove, &actor, self, context).await?; + Ok(()) } - Ok(()) } diff --git a/crates/apub/src/activities/send/user.rs b/crates/apub/src/activities/send/user.rs index 1dc62e0bc..a0778c086 100644 --- a/crates/apub/src/activities/send/user.rs +++ b/crates/apub/src/activities/send/user.rs @@ -3,6 +3,7 @@ use crate::{ activity_queue::send_activity_single_dest, extensions::context::lemmy_context, ActorType, + UserType, }; use activitystreams::{ activity::{ @@ -10,11 +11,11 @@ use activitystreams::{ Follow, Undo, }, - base::{AnyBase, BaseExt, ExtendsExt}, + base::{BaseExt, ExtendsExt}, object::ObjectExt, }; use lemmy_api_structs::blocking; -use lemmy_db_queries::{ApubObject, DbPool, Followable}; +use lemmy_db_queries::{ApubObject, Followable}; use lemmy_db_schema::source::{ community::{Community, CommunityFollower, CommunityFollowerForm}, user::User_, @@ -47,7 +48,10 @@ impl ActorType for User_ { .unwrap_or_else(|| self.inbox_url.to_owned()) .into() } +} +#[async_trait::async_trait(?Send)] +impl UserType for User_ { /// As a given local user, send out a follow request to a remote community. async fn send_follow( &self, @@ -110,40 +114,4 @@ impl ActorType for User_ { send_activity_single_dest(undo, self, community.inbox_url.into(), context).await?; Ok(()) } - - async fn send_accept_follow( - &self, - _follow: Follow, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn send_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn send_undo_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn send_remove(&self, _context: &LemmyContext) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn send_undo_remove(&self, _context: &LemmyContext) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn send_announce( - &self, - _activity: AnyBase, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn get_follower_inboxes(&self, _pool: &DbPool) -> Result, LemmyError> { - unimplemented!() - } } diff --git a/crates/apub/src/activity_queue.rs b/crates/apub/src/activity_queue.rs index f607dafe0..9cdc38864 100644 --- a/crates/apub/src/activity_queue.rs +++ b/crates/apub/src/activity_queue.rs @@ -3,6 +3,7 @@ use crate::{ extensions::signatures::sign_and_send, insert_activity, ActorType, + CommunityType, APUB_JSON_CONTENT_TYPE, }; use activitystreams::{ diff --git a/crates/apub/src/inbox/community_inbox.rs b/crates/apub/src/inbox/community_inbox.rs index 1495c4bbc..cd1c775bd 100644 --- a/crates/apub/src/inbox/community_inbox.rs +++ b/crates/apub/src/inbox/community_inbox.rs @@ -20,6 +20,7 @@ use crate::{ }, insert_activity, ActorType, + CommunityType, }; use activitystreams::{ activity::{kind::FollowType, ActorAndObject, Follow, Undo}, diff --git a/crates/apub/src/inbox/receive_for_community.rs b/crates/apub/src/inbox/receive_for_community.rs index 78c4107a1..cd8f32bbc 100644 --- a/crates/apub/src/inbox/receive_for_community.rs +++ b/crates/apub/src/inbox/receive_for_community.rs @@ -35,10 +35,13 @@ use crate::{ objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post}, user::get_or_fetch_and_upsert_user, }, + find_object_by_id, find_post_or_comment_by_id, generate_moderators_url, inbox::verify_is_addressed_to_public, ActorType, + CommunityType, + Object, PostOrComment, }; use activitystreams::{ @@ -254,8 +257,8 @@ pub(in crate::inbox) async fn receive_remove_for_community( .context(location_info!())?; match find_post_or_comment_by_id(context, object).await { - Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, *p).await, - Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await, + Ok(PostOrComment::Post(p)) => receive_remove_post(context, *p).await, + Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, *c).await, // if we dont have the object, no need to do anything Err(_) => Ok(()), } @@ -600,9 +603,12 @@ where .map(|o| o.id()) .flatten() .context(location_info!())?; - let original_id = match find_post_or_comment_by_id(context, object_id.to_owned()).await? { - PostOrComment::Post(p) => p.ap_id.into_inner(), - PostOrComment::Comment(c) => c.ap_id.into_inner(), + let original_id = match find_object_by_id(context, object_id.to_owned()).await? { + Object::Post(p) => p.ap_id.into_inner(), + Object::Comment(c) => c.ap_id.into_inner(), + Object::Community(c) => c.actor_id(), + Object::User(u) => u.actor_id(), + Object::PrivateMessage(p) => p.ap_id.into_inner(), }; if actor_id.domain() != original_id.domain() { let community = extract_community_from_cc(activity, context).await?; diff --git a/crates/apub/src/inbox/user_inbox.rs b/crates/apub/src/inbox/user_inbox.rs index 2ea2e1d19..3d16403bc 100644 --- a/crates/apub/src/inbox/user_inbox.rs +++ b/crates/apub/src/inbox/user_inbox.rs @@ -41,7 +41,7 @@ use crate::{ ActorType, }; use activitystreams::{ - activity::{Accept, ActorAndObject, Announce, Create, Delete, Follow, Undo, Update}, + activity::{Accept, ActorAndObject, Announce, Create, Delete, Follow, Remove, Undo, Update}, base::AnyBase, prelude::*, }; @@ -165,7 +165,7 @@ pub(crate) async fn user_receive_message( receive_delete(context, any_base, &actor_url, request_counter).await? } UserValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?, - UserValidTypes::Remove => receive_remove_community(&context, any_base, &actor_url).await?, + UserValidTypes::Remove => receive_remove(context, any_base, &actor_url).await?, }; // TODO: would be logical to move websocket notification code here @@ -370,13 +370,31 @@ async fn receive_delete( } } +async fn receive_remove( + context: &LemmyContext, + any_base: AnyBase, + expected_domain: &Url, +) -> Result<(), LemmyError> { + let remove = Remove::from_any_base(any_base.clone())?.context(location_info!())?; + verify_activity_domains_valid(&remove, expected_domain, true)?; + let object_uri = remove + .object() + .to_owned() + .single_xsd_any_uri() + .context(location_info!())?; + let community = blocking(context.pool(), move |conn| { + Community::read_from_apub_id(conn, &object_uri.into()) + }) + .await??; + receive_remove_community(&context, community).await +} + async fn receive_undo( context: &LemmyContext, any_base: AnyBase, expected_domain: &Url, request_counter: &mut i32, ) -> Result<(), LemmyError> { - use CommunityOrPrivateMessage::*; let undo = Undo::from_any_base(any_base)?.context(location_info!())?; verify_activity_domains_valid(&undo, expected_domain, true)?; @@ -391,15 +409,28 @@ async fn receive_undo( .to_owned() .single_xsd_any_uri() .context(location_info!())?; + use CommunityOrPrivateMessage::*; match find_community_or_private_message_by_id(context, object_uri).await? { - Community(c) => receive_undo_delete_community(context, undo, c, expected_domain).await, + Community(c) => receive_undo_delete_community(context, c).await, PrivateMessage(p) => { receive_undo_delete_private_message(context, undo, expected_domain, p, request_counter) .await } } } - Some("Remove") => receive_undo_remove_community(context, undo, expected_domain).await, + Some("Remove") => { + let remove = Remove::from_any_base(inner_activity)?.context(location_info!())?; + let object_uri = remove + .object() + .to_owned() + .single_xsd_any_uri() + .context(location_info!())?; + let community = blocking(context.pool(), move |conn| { + Community::read_from_apub_id(conn, &object_uri.into()) + }) + .await??; + receive_undo_remove_community(context, community).await + } _ => receive_unhandled_activity(undo), } } diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index 307a8c8c0..ec5c6d943 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -152,38 +152,6 @@ pub trait ActorType { fn public_key(&self) -> Option; fn private_key(&self) -> Option; - async fn send_follow( - &self, - follow_actor_id: &Url, - context: &LemmyContext, - ) -> Result<(), LemmyError>; - async fn send_unfollow( - &self, - follow_actor_id: &Url, - context: &LemmyContext, - ) -> Result<(), LemmyError>; - - async fn send_accept_follow( - &self, - follow: Follow, - context: &LemmyContext, - ) -> Result<(), LemmyError>; - - async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>; - async fn send_undo_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>; - - async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>; - async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>; - - async fn send_announce( - &self, - activity: AnyBase, - context: &LemmyContext, - ) -> Result<(), LemmyError>; - - /// For a given community, returns the inboxes of all followers. - async fn get_follower_inboxes(&self, pool: &DbPool) -> Result, LemmyError>; - fn get_shared_inbox_or_inbox_url(&self) -> Url; /// Outbox URL is not generally used by Lemmy, so it can be generated on the fly (but only for @@ -207,6 +175,55 @@ pub trait ActorType { } } +#[async_trait::async_trait(?Send)] +pub trait CommunityType { + async fn get_follower_inboxes(&self, pool: &DbPool) -> Result, LemmyError>; + async fn send_accept_follow( + &self, + follow: Follow, + context: &LemmyContext, + ) -> Result<(), LemmyError>; + + async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>; + async fn send_undo_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>; + + async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>; + async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>; + + async fn send_announce( + &self, + activity: AnyBase, + context: &LemmyContext, + ) -> Result<(), LemmyError>; + + async fn send_add_mod( + &self, + actor: &User_, + added_mod: User_, + context: &LemmyContext, + ) -> Result<(), LemmyError>; + async fn send_remove_mod( + &self, + actor: &User_, + removed_mod: User_, + context: &LemmyContext, + ) -> Result<(), LemmyError>; +} + +#[async_trait::async_trait(?Send)] +pub trait UserType { + async fn send_follow( + &self, + follow_actor_id: &Url, + context: &LemmyContext, + ) -> Result<(), LemmyError>; + async fn send_unfollow( + &self, + follow_actor_id: &Url, + context: &LemmyContext, + ) -> Result<(), LemmyError>; +} + pub enum EndpointType { Community, User, @@ -319,6 +336,7 @@ pub(crate) async fn find_post_or_comment_by_id( Err(NotFound.into()) } +#[derive(Debug)] pub(crate) enum Object { Comment(Comment), Post(Post),