From 5460c658a7add0b6a2dee16f5582593c4327b0e8 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Sun, 27 Jun 2021 16:54:24 +0200 Subject: [PATCH] convert inbox functions, add missing checks --- .../src/activities_new/comment/mod.rs | 1 + .../src/activities_new/community/delete.rs | 1 + .../src/activities_new/community/remove.rs | 1 + .../activities_new/community/undo_delete.rs | 1 + .../activities_new/community/undo_remove.rs | 1 + .../src/activities_new/community/update.rs | 3 +- .../{follow.rs => follow/accept.rs} | 42 ++----- .../src/activities_new/follow/follow.rs | 67 +++++++++++ .../src/activities_new/follow/mod.rs | 3 + .../src/activities_new/follow/undo.rs | 60 ++++++++++ .../src/activities_new/post/delete.rs | 1 - .../src/activities_new/post/remove.rs | 2 +- .../apub_receive/src/inbox/community_inbox.rs | 112 +++--------------- .../src/inbox/new_inbox_routing.rs | 4 +- 14 files changed, 165 insertions(+), 134 deletions(-) rename crates/apub_receive/src/activities_new/{follow.rs => follow/accept.rs} (59%) create mode 100644 crates/apub_receive/src/activities_new/follow/follow.rs create mode 100644 crates/apub_receive/src/activities_new/follow/mod.rs create mode 100644 crates/apub_receive/src/activities_new/follow/undo.rs diff --git a/crates/apub_receive/src/activities_new/comment/mod.rs b/crates/apub_receive/src/activities_new/comment/mod.rs index 33d85536..1de573fe 100644 --- a/crates/apub_receive/src/activities_new/comment/mod.rs +++ b/crates/apub_receive/src/activities_new/comment/mod.rs @@ -38,6 +38,7 @@ async fn get_notif_recipients( // Although mentions could be gotten from the post tags (they are included there), or the ccs, // Its much easier to scrape them from the comment body, since the API has to do that // anyway. + // TODO: for compatibility with other projects, it would be much better to read this from cc or tags let mentions = scrape_text_for_mentions(&comment.content); send_local_notifs(mentions, comment.clone(), actor, post, context.pool(), true).await } diff --git a/crates/apub_receive/src/activities_new/community/delete.rs b/crates/apub_receive/src/activities_new/community/delete.rs index efd01f83..1d95f298 100644 --- a/crates/apub_receive/src/activities_new/community/delete.rs +++ b/crates/apub_receive/src/activities_new/community/delete.rs @@ -34,6 +34,7 @@ pub struct DeleteCommunity { #[async_trait::async_trait(?Send)] impl VerifyActivity for Activity { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { + verify_domains_match(&self.inner.actor, self.id_unchecked())?; let object = self.inner.object.clone(); let community = blocking(context.pool(), move |conn| { Community::read_from_apub_id(conn, &object.into()) diff --git a/crates/apub_receive/src/activities_new/community/remove.rs b/crates/apub_receive/src/activities_new/community/remove.rs index 5ba99756..cc973e4b 100644 --- a/crates/apub_receive/src/activities_new/community/remove.rs +++ b/crates/apub_receive/src/activities_new/community/remove.rs @@ -26,6 +26,7 @@ pub struct RemoveCommunity { #[async_trait::async_trait(?Send)] impl VerifyActivity for Activity { async fn verify(&self, _context: &LemmyContext) -> Result<(), LemmyError> { + verify_domains_match(&self.inner.actor, self.id_unchecked())?; check_is_apub_id_valid(&self.inner.actor, false)?; verify_domains_match(&self.inner.actor, &self.inner.object)?; verify_domains_match(&self.inner.actor, &self.inner.cc[0]) diff --git a/crates/apub_receive/src/activities_new/community/undo_delete.rs b/crates/apub_receive/src/activities_new/community/undo_delete.rs index e46909e9..c4f4382e 100644 --- a/crates/apub_receive/src/activities_new/community/undo_delete.rs +++ b/crates/apub_receive/src/activities_new/community/undo_delete.rs @@ -38,6 +38,7 @@ pub struct UndoDeleteCommunity { #[async_trait::async_trait(?Send)] impl VerifyActivity for Activity { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { + verify_domains_match(&self.inner.actor, self.id_unchecked())?; let object = self.inner.object.inner.object.clone(); let community = blocking(context.pool(), move |conn| { Community::read_from_apub_id(conn, &object.into()) diff --git a/crates/apub_receive/src/activities_new/community/undo_remove.rs b/crates/apub_receive/src/activities_new/community/undo_remove.rs index e7330732..6566ae9a 100644 --- a/crates/apub_receive/src/activities_new/community/undo_remove.rs +++ b/crates/apub_receive/src/activities_new/community/undo_remove.rs @@ -26,6 +26,7 @@ pub struct UndoRemoveCommunity { #[async_trait::async_trait(?Send)] impl VerifyActivity for Activity { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { + verify_domains_match(&self.inner.actor, self.id_unchecked())?; check_is_apub_id_valid(&self.inner.actor, false)?; verify_domains_match(&self.inner.actor, &self.inner.object.inner.object)?; verify_domains_match(&self.inner.actor, &self.inner.cc[0])?; diff --git a/crates/apub_receive/src/activities_new/community/update.rs b/crates/apub_receive/src/activities_new/community/update.rs index 1b29a417..4533bde2 100644 --- a/crates/apub_receive/src/activities_new/community/update.rs +++ b/crates/apub_receive/src/activities_new/community/update.rs @@ -5,7 +5,7 @@ use crate::{ use activitystreams::{activity::kind::UpdateType, base::BaseExt}; use lemmy_api_common::blocking; use lemmy_apub::{check_is_apub_id_valid, objects::FromApubToForm, GroupExt}; -use lemmy_apub_lib::{PublicUrl, ReceiveActivity, VerifyActivity}; +use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity}; use lemmy_db_queries::{ApubObject, Crud}; use lemmy_db_schema::source::community::{Community, CommunityForm}; use lemmy_utils::LemmyError; @@ -28,6 +28,7 @@ pub struct UpdateCommunity { #[async_trait::async_trait(?Send)] impl VerifyActivity for Activity { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { + verify_domains_match(&self.inner.actor, self.id_unchecked())?; self.inner.object.id(self.inner.cc[0].as_str())?; check_is_apub_id_valid(&self.inner.actor, false)?; verify_is_community_mod(self.inner.actor.clone(), self.inner.cc[0].clone(), context).await diff --git a/crates/apub_receive/src/activities_new/follow.rs b/crates/apub_receive/src/activities_new/follow/accept.rs similarity index 59% rename from crates/apub_receive/src/activities_new/follow.rs rename to crates/apub_receive/src/activities_new/follow/accept.rs index 5be06354..f1daa37f 100644 --- a/crates/apub_receive/src/activities_new/follow.rs +++ b/crates/apub_receive/src/activities_new/follow/accept.rs @@ -1,9 +1,9 @@ -use crate::inbox::new_inbox_routing::Activity; -use activitystreams::activity::kind::{AcceptType, FollowType}; +use crate::{activities_new::follow::follow::FollowCommunity, inbox::new_inbox_routing::Activity}; +use activitystreams::activity::kind::AcceptType; use lemmy_api_common::blocking; -use lemmy_apub::fetcher::{ - community::get_or_fetch_and_upsert_community, - person::get_or_fetch_and_upsert_person, +use lemmy_apub::{ + 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, ReceiveActivity, VerifyActivity}; use lemmy_db_queries::Followable; @@ -12,35 +12,6 @@ use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct FollowCommunity { - actor: Url, - to: Url, - object: Url, - #[serde(rename = "type")] - kind: FollowType, -} - -#[async_trait::async_trait(?Send)] -impl VerifyActivity for Activity { - async fn verify(&self, _context: &LemmyContext) -> Result<(), LemmyError> { - todo!() - } -} - -#[async_trait::async_trait(?Send)] -impl ReceiveActivity for Activity { - async fn receive( - &self, - _context: &LemmyContext, - _request_counter: &mut i32, - ) -> Result<(), LemmyError> { - println!("receive follow"); - todo!() - } -} - #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct AcceptFollowCommunity { @@ -54,7 +25,8 @@ pub struct AcceptFollowCommunity { #[async_trait::async_trait(?Send)] impl VerifyActivity for Activity { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { - verify_domains_match(self.id_unchecked(), &self.inner.actor)?; + verify_domains_match(&self.inner.actor, self.id_unchecked())?; + check_is_apub_id_valid(&self.inner.actor, false)?; self.inner.object.verify(context).await } } diff --git a/crates/apub_receive/src/activities_new/follow/follow.rs b/crates/apub_receive/src/activities_new/follow/follow.rs new file mode 100644 index 00000000..3d3664d5 --- /dev/null +++ b/crates/apub_receive/src/activities_new/follow/follow.rs @@ -0,0 +1,67 @@ +use crate::inbox::new_inbox_routing::Activity; +use activitystreams::{ + activity::{kind::FollowType, Follow}, + base::{AnyBase, ExtendsExt}, +}; +use anyhow::Context; +use lemmy_api_common::blocking; +use lemmy_apub::{ + check_is_apub_id_valid, + fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, + CommunityType, +}; +use lemmy_apub_lib::{verify_domains_match, ReceiveActivity, VerifyActivity}; +use lemmy_db_queries::Followable; +use lemmy_db_schema::source::community::{CommunityFollower, CommunityFollowerForm}; +use lemmy_utils::{location_info, LemmyError}; +use lemmy_websocket::LemmyContext; +use url::Url; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct FollowCommunity { + actor: Url, + to: Url, + pub(in crate::activities_new::follow) object: Url, + #[serde(rename = "type")] + kind: FollowType, +} + +#[async_trait::async_trait(?Send)] +impl VerifyActivity for Activity { + async fn verify(&self, _context: &LemmyContext) -> Result<(), LemmyError> { + verify_domains_match(&self.inner.actor, self.id_unchecked())?; + verify_domains_match(&self.inner.to, &self.inner.object)?; + check_is_apub_id_valid(&self.inner.actor, false) + } +} + +#[async_trait::async_trait(?Send)] +impl ReceiveActivity for Activity { + async fn receive( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + let community = + get_or_fetch_and_upsert_community(&self.inner.object, context, request_counter).await?; + let person = + get_or_fetch_and_upsert_person(&self.inner.actor, context, request_counter).await?; + let community_follower_form = CommunityFollowerForm { + community_id: community.id, + person_id: person.id, + pending: false, + }; + + // This will fail if they're already a follower, but ignore the error. + blocking(&context.pool(), move |conn| { + CommunityFollower::follow(&conn, &community_follower_form).ok() + }) + .await?; + + // TODO: avoid the conversion and pass our own follow struct directly + let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(self)?)?; + let anybase = Follow::from_any_base(anybase)?.context(location_info!())?; + community.send_accept_follow(anybase, context).await + } +} diff --git a/crates/apub_receive/src/activities_new/follow/mod.rs b/crates/apub_receive/src/activities_new/follow/mod.rs new file mode 100644 index 00000000..050c3691 --- /dev/null +++ b/crates/apub_receive/src/activities_new/follow/mod.rs @@ -0,0 +1,3 @@ +pub mod accept; +pub mod follow; +pub mod undo; diff --git a/crates/apub_receive/src/activities_new/follow/undo.rs b/crates/apub_receive/src/activities_new/follow/undo.rs new file mode 100644 index 00000000..2bb93ec7 --- /dev/null +++ b/crates/apub_receive/src/activities_new/follow/undo.rs @@ -0,0 +1,60 @@ +use crate::{activities_new::follow::follow::FollowCommunity, inbox::new_inbox_routing::Activity}; +use activitystreams::activity::kind::UndoType; +use lemmy_api_common::blocking; +use lemmy_apub::{ + 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, ReceiveActivity, VerifyActivity}; +use lemmy_db_queries::Followable; +use lemmy_db_schema::source::community::{CommunityFollower, CommunityFollowerForm}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use url::Url; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct UndoFollowCommunity { + actor: Url, + to: Url, + object: Activity, + #[serde(rename = "type")] + kind: UndoType, +} + +#[async_trait::async_trait(?Send)] +impl VerifyActivity for Activity { + async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { + verify_domains_match(&self.inner.actor, self.id_unchecked())?; + verify_domains_match(&self.inner.to, &self.inner.object.inner.object)?; + check_is_apub_id_valid(&self.inner.actor, false)?; + self.inner.object.verify(context).await + } +} + +#[async_trait::async_trait(?Send)] +impl ReceiveActivity for Activity { + async fn receive( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + let community = + get_or_fetch_and_upsert_community(&self.inner.to, context, request_counter).await?; + let person = + get_or_fetch_and_upsert_person(&self.inner.actor, context, request_counter).await?; + + let community_follower_form = CommunityFollowerForm { + community_id: community.id, + person_id: person.id, + pending: false, + }; + + // This will fail if they aren't a follower, but ignore the error. + blocking(&context.pool(), move |conn| { + CommunityFollower::unfollow(&conn, &community_follower_form).ok() + }) + .await?; + Ok(()) + } +} diff --git a/crates/apub_receive/src/activities_new/post/delete.rs b/crates/apub_receive/src/activities_new/post/delete.rs index 647ea1b0..47b1fc97 100644 --- a/crates/apub_receive/src/activities_new/post/delete.rs +++ b/crates/apub_receive/src/activities_new/post/delete.rs @@ -36,7 +36,6 @@ impl ReceiveActivity for Activity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - // TODO: check that actor is from same instance as post (same for DeleteComment) let post = get_or_fetch_and_insert_post(&self.inner.object, context, request_counter).await?; let deleted_post = blocking(context.pool(), move |conn| { diff --git a/crates/apub_receive/src/activities_new/post/remove.rs b/crates/apub_receive/src/activities_new/post/remove.rs index 0fa0b255..f491feb7 100644 --- a/crates/apub_receive/src/activities_new/post/remove.rs +++ b/crates/apub_receive/src/activities_new/post/remove.rs @@ -39,7 +39,7 @@ impl ReceiveActivity for Activity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - // TODO: check that actor is instance mod if community is local (same for DeleteComment) + // TODO: check that actor is instance mod if community is local (same for RemoveComment) let post = get_or_fetch_and_insert_post(&self.inner.object, context, request_counter).await?; let removed_post = blocking(context.pool(), move |conn| { diff --git a/crates/apub_receive/src/inbox/community_inbox.rs b/crates/apub_receive/src/inbox/community_inbox.rs index c0368cb7..4d9a12bb 100644 --- a/crates/apub_receive/src/inbox/community_inbox.rs +++ b/crates/apub_receive/src/inbox/community_inbox.rs @@ -1,22 +1,18 @@ -use crate::{ - activities::receive::verify_activity_domains_valid, - inbox::{ - assert_activity_not_local, - get_activity_id, - inbox_verify_http_signature, - is_activity_already_known, - receive_for_community::{ - receive_add_for_community, - receive_block_user_for_community, - receive_remove_for_community, - receive_undo_for_community, - }, - verify_is_addressed_to_public, +use crate::inbox::{ + assert_activity_not_local, + get_activity_id, + inbox_verify_http_signature, + is_activity_already_known, + receive_for_community::{ + receive_add_for_community, + receive_block_user_for_community, + receive_remove_for_community, + receive_undo_for_community, }, + verify_is_addressed_to_public, }; use activitystreams::{ - activity::{kind::FollowType, ActorAndObject, Follow, Undo}, - base::AnyBase, + activity::{kind::FollowType, ActorAndObject}, prelude::*, }; use actix_web::{web, HttpRequest, HttpResponse}; @@ -29,11 +25,8 @@ use lemmy_apub::{ ActorType, CommunityType, }; -use lemmy_db_queries::{source::community::Community_, ApubObject, Followable}; -use lemmy_db_schema::source::{ - community::{Community, CommunityFollower, CommunityFollowerForm}, - person::Person, -}; +use lemmy_db_queries::{source::community::Community_, ApubObject}; +use lemmy_db_schema::source::{community::Community, person::Person}; use lemmy_utils::{location_info, LemmyError}; use lemmy_websocket::LemmyContext; use log::info; @@ -132,16 +125,7 @@ pub(crate) async fn community_receive_message( let actor_url = actor.actor_id(); let activity_kind = activity.kind().context(location_info!())?; let do_announce = match activity_kind { - CommunityValidTypes::Follow => { - Box::pin(handle_follow( - any_base.clone(), - person, - &to_community, - &context, - )) - .await?; - false - } + CommunityValidTypes::Follow => todo!(), CommunityValidTypes::Undo => { Box::pin(handle_undo( context, @@ -212,39 +196,11 @@ pub(crate) async fn community_receive_message( Ok(HttpResponse::Ok().finish()) } -/// Handle a follow request from a remote person, adding the person as follower and returning an -/// Accept activity. -async fn handle_follow( - activity: AnyBase, - person: Person, - community: &Community, - context: &LemmyContext, -) -> Result { - let follow = Follow::from_any_base(activity)?.context(location_info!())?; - verify_activity_domains_valid(&follow, &person.actor_id(), false)?; - - let community_follower_form = CommunityFollowerForm { - community_id: community.id, - person_id: person.id, - pending: false, - }; - - // This will fail if they're already a follower, but ignore the error. - blocking(&context.pool(), move |conn| { - CommunityFollower::follow(&conn, &community_follower_form).ok() - }) - .await?; - - community.send_accept_follow(follow, context).await?; - - Ok(HttpResponse::Ok().finish()) -} - async fn handle_undo( context: &LemmyContext, activity: CommunityAcceptedActivities, actor_url: Url, - to_community: &Community, + _to_community: &Community, request_counter: &mut i32, ) -> Result { let inner_kind = activity @@ -252,43 +208,9 @@ async fn handle_undo( .is_single_kind(&FollowType::Follow.to_string()); let any_base = activity.into_any_base()?; if inner_kind { - handle_undo_follow(any_base, actor_url, to_community, &context).await?; - Ok(false) + todo!() } else { receive_undo_for_community(context, any_base, None, &actor_url, request_counter).await?; Ok(true) } } - -/// Handle `Undo/Follow` from a person, removing the person from followers list. -async fn handle_undo_follow( - activity: AnyBase, - person_url: Url, - community: &Community, - context: &LemmyContext, -) -> Result<(), LemmyError> { - let undo = Undo::from_any_base(activity)?.context(location_info!())?; - verify_activity_domains_valid(&undo, &person_url, true)?; - - let object = undo.object().to_owned().one().context(location_info!())?; - let follow = Follow::from_any_base(object)?.context(location_info!())?; - verify_activity_domains_valid(&follow, &person_url, false)?; - - let person = blocking(&context.pool(), move |conn| { - Person::read_from_apub_id(&conn, &person_url.into()) - }) - .await??; - let community_follower_form = CommunityFollowerForm { - community_id: community.id, - person_id: person.id, - pending: false, - }; - - // This will fail if they aren't a follower, but ignore the error. - blocking(&context.pool(), move |conn| { - CommunityFollower::unfollow(&conn, &community_follower_form).ok() - }) - .await?; - - Ok(()) -} diff --git a/crates/apub_receive/src/inbox/new_inbox_routing.rs b/crates/apub_receive/src/inbox/new_inbox_routing.rs index 2a72137a..8f10f121 100644 --- a/crates/apub_receive/src/inbox/new_inbox_routing.rs +++ b/crates/apub_receive/src/inbox/new_inbox_routing.rs @@ -14,7 +14,7 @@ use crate::activities_new::{ undo_remove::UndoRemoveCommunity, update::UpdateCommunity, }, - follow::AcceptFollowCommunity, + follow::{accept::AcceptFollowCommunity, follow::FollowCommunity, undo::UndoFollowCommunity}, post::{ create::CreatePost, delete::DeletePost, @@ -63,7 +63,9 @@ impl Activity { // TODO: this is probably wrong, it contains all activities #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] pub enum PersonAcceptedActivitiesNew { + FollowCommunity(FollowCommunity), AcceptFollowCommunity(AcceptFollowCommunity), + UndoFollowCommunity(UndoFollowCommunity), CreatePrivateMessage(CreatePrivateMessage), UpdatePrivateMessage(UpdatePrivateMessage), DeletePrivateMessage(DeletePrivateMessage),