diff --git a/Cargo.lock b/Cargo.lock index 3c46bc3d1..75a1a1c70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1746,17 +1746,13 @@ dependencies = [ "anyhow", "async-trait", "awc", - "background-jobs", - "base64 0.13.0", "bcrypt", "chrono", "diesel", "futures", "http", "http-signature-normalization-actix", - "http-signature-normalization-reqwest", "itertools", - "lazy_static", "lemmy_api_common", "lemmy_apub_lib", "lemmy_db_queries", @@ -1766,7 +1762,6 @@ dependencies = [ "lemmy_utils", "lemmy_websocket", "log", - "openssl", "percent-encoding", "rand 0.8.4", "reqwest", @@ -1787,14 +1782,23 @@ name = "lemmy_apub_lib" version = "0.13.0" dependencies = [ "activitystreams", + "actix-web", "anyhow", "async-trait", + "background-jobs", + "base64 0.13.0", + "http", + "http-signature-normalization-actix", + "http-signature-normalization-reqwest", + "lazy_static", "lemmy_apub_lib_derive", "lemmy_utils", "log", + "openssl", "reqwest", "serde", "serde_json", + "sha2", "url", ] @@ -1927,6 +1931,7 @@ dependencies = [ "lemmy_api_common", "lemmy_api_crud", "lemmy_apub", + "lemmy_apub_lib", "lemmy_db_queries", "lemmy_db_schema", "lemmy_db_views", diff --git a/Cargo.toml b/Cargo.toml index 69677a453..199c732ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ lemmy_api = { version = "=0.13.0", path = "./crates/api" } lemmy_api_crud = { version = "=0.13.0", path = "./crates/api_crud" } lemmy_apub = { version = "=0.13.0", path = "./crates/apub" } +lemmy_apub_lib = { version = "=0.13.0", path = "./crates/apub_lib" } lemmy_utils = { version = "=0.13.0", path = "./crates/utils" } lemmy_db_schema = { version = "=0.13.0", path = "./crates/db_schema" } lemmy_db_queries = { version = "=0.13.0", path = "./crates/db_queries" } diff --git a/api_tests/prepare-drone-federation-test.sh b/api_tests/prepare-drone-federation-test.sh index 7a8fb2bd8..6e08ad359 100755 --- a/api_tests/prepare-drone-federation-test.sh +++ b/api_tests/prepare-drone-federation-test.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -export LEMMY_TEST_SEND_SYNC=1 +export APUB_TESTING_SEND_SYNC=1 export RUST_BACKTRACE=1 export RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_queries=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" diff --git a/crates/apub/Cargo.toml b/crates/apub/Cargo.toml index c9e2a49d9..2d49d53ce 100644 --- a/crates/apub/Cargo.toml +++ b/crates/apub/Cargo.toml @@ -36,11 +36,8 @@ strum = "0.21.0" strum_macros = "0.21.1" url = { version = "2.2.2", features = ["serde"] } percent-encoding = "2.1.0" -openssl = "0.10.36" http = "0.2.5" http-signature-normalization-actix = { version = "0.5.0-beta.10", default-features = false, features = ["server", "sha-2"] } -http-signature-normalization-reqwest = { version = "0.2.0", default-features = false, features = ["sha-2"] } -base64 = "0.13.0" tokio = "1.12.0" futures = "0.3.17" itertools = "0.10.1" @@ -49,7 +46,5 @@ sha2 = "0.9.8" async-trait = "0.1.51" anyhow = "1.0.44" thiserror = "1.0.29" -background-jobs = "0.10.0" reqwest = { version = "0.11.4", features = ["json"] } -lazy_static = "1.4.0" diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 646a14f12..c225300f3 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -1,24 +1,22 @@ use crate::{ activities::{ comment::{collect_non_local_mentions, get_notif_recipients}, - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, extract_community, generate_activity_id, verify_activity, verify_person_in_community, CreateOrUpdateType, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, objects::{comment::Note, FromApub, ToApub}, - ActorType, }; use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, verify::verify_domains_match, }; @@ -81,7 +79,7 @@ impl CreateOrUpdateComment { }; let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update); - send_to_community_new(activity, &id, actor, &community, maa.inboxes, context).await + send_to_community(activity, &id, actor, &community, maa.inboxes, context).await } } diff --git a/crates/apub/src/activities/comment/mod.rs b/crates/apub/src/activities/comment/mod.rs index 9e91d2f1b..d24ff35de 100644 --- a/crates/apub/src/activities/comment/mod.rs +++ b/crates/apub/src/activities/comment/mod.rs @@ -1,4 +1,4 @@ -use crate::{fetcher::object_id::ObjectId, ActorType}; +use crate::fetcher::object_id::ObjectId; use activitystreams::{ base::BaseExt, link::{LinkExt, Mention}, @@ -6,7 +6,7 @@ use activitystreams::{ use anyhow::anyhow; use itertools::Itertools; use lemmy_api_common::{blocking, send_local_notifs}; -use lemmy_apub_lib::webfinger::WebfingerResponse; +use lemmy_apub_lib::{traits::ActorType, webfinger::WebfingerResponse}; use lemmy_db_queries::{Crud, DbPool}; use lemmy_db_schema::{ source::{comment::Comment, community::Community, person::Person, post::Post}, @@ -68,7 +68,7 @@ pub async fn collect_non_local_mentions( let parent_creator = get_comment_parent_creator(context.pool(), comment).await?; let mut addressed_ccs = vec![community.actor_id(), parent_creator.actor_id()]; // Note: dont include community inbox here, as we send to it separately with `send_to_community()` - let mut inboxes = vec![parent_creator.get_shared_inbox_or_inbox_url()]; + let mut inboxes = vec![parent_creator.shared_inbox_or_inbox_url()]; // Add the mention tag let mut tags = Vec::new(); @@ -88,7 +88,7 @@ pub async fn collect_non_local_mentions( addressed_ccs.push(actor_id.to_owned().to_string().parse()?); let mention_person = actor_id.dereference(context, &mut 0).await?; - inboxes.push(mention_person.get_shared_inbox_or_inbox_url()); + inboxes.push(mention_person.shared_inbox_or_inbox_url()); let mut mention_tag = Mention::new(); mention_tag diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index d3785f4a4..6544976b5 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -1,17 +1,15 @@ use crate::{ activities::{ - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, verify_add_remove_moderator_target, verify_mod_action, verify_person_in_community, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, generate_moderators_url, - ActorType, }; use activitystreams::{ activity::kind::AddType, @@ -22,7 +20,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, }; use lemmy_db_queries::{source::community::CommunityModerator_, Joinable}; @@ -76,8 +74,8 @@ impl AddMod { }; let activity = AnnouncableActivities::AddMod(add); - let inboxes = vec![added_mod.get_shared_inbox_or_inbox_url()]; - send_to_community_new(activity, &id, actor, community, inboxes, context).await + let inboxes = vec![added_mod.shared_inbox_or_inbox_url()]; + send_to_community(activity, &id, actor, community, inboxes, context).await } } diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 93f015e74..ec72b8429 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -17,12 +17,11 @@ use crate::{ verify_community, voting::{undo_vote::UndoVote, vote::Vote}, }, - activity_queue::send_activity_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, http::is_activity_already_known, insert_activity, - ActorType, + send_lemmy_activity, CommunityType, }; use activitystreams::{ @@ -33,7 +32,7 @@ use activitystreams::{ }; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, }; use lemmy_db_schema::source::community::Community; @@ -97,7 +96,7 @@ impl AnnounceActivity { unparsed: Default::default(), }; let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?; - send_activity_new(context, &announce, &announce.id, community, inboxes, false).await + send_lemmy_activity(context, &announce, &announce.id, community, inboxes, false).await } } diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs index 0009b0697..f01c4133b 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/community/block_user.rs @@ -1,15 +1,13 @@ use crate::{ activities::{ - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, verify_mod_action, verify_person_in_community, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, }; use activitystreams::{ activity::kind::BlockType, @@ -20,7 +18,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, }; use lemmy_db_queries::{Bannable, Followable}; @@ -87,8 +85,8 @@ impl BlockUserFromCommunity { let block_id = block.id.clone(); let activity = AnnouncableActivities::BlockUserFromCommunity(block); - let inboxes = vec![target.get_shared_inbox_or_inbox_url()]; - send_to_community_new(activity, &block_id, actor, community, inboxes, context).await + let inboxes = vec![target.shared_inbox_or_inbox_url()]; + send_to_community(activity, &block_id, actor, community, inboxes, context).await } } diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 642f83645..dd6ccb7ff 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -1,5 +1,12 @@ -use crate::{check_is_apub_id_valid, CommunityType}; +use crate::{ + activities::community::announce::{AnnouncableActivities, AnnounceActivity}, + check_is_apub_id_valid, + insert_activity, + send_lemmy_activity, + CommunityType, +}; use itertools::Itertools; +use lemmy_apub_lib::traits::ActorType; use lemmy_db_schema::source::community::Community; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; @@ -33,3 +40,24 @@ async fn list_community_follower_inboxes( .collect(), ) } + +pub(crate) async fn send_to_community( + activity: AnnouncableActivities, + activity_id: &Url, + actor: &T, + community: &Community, + additional_inboxes: Vec, + context: &LemmyContext, +) -> Result<(), LemmyError> { + // if this is a local community, we need to do an announce from the community instead + if community.local { + insert_activity(activity_id, activity.clone(), true, false, context.pool()).await?; + AnnounceActivity::send(activity, community, additional_inboxes, context).await?; + } else { + let mut inboxes = additional_inboxes; + inboxes.push(community.shared_inbox_or_inbox_url()); + send_lemmy_activity(context, &activity, activity_id, actor, inboxes, false).await?; + } + + Ok(()) +} diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index b58fdd2ca..34db3c443 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -1,6 +1,6 @@ use crate::{ activities::{ - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, deletion::{delete::receive_remove_action, verify_delete_activity}, generate_activity_id, verify_activity, @@ -8,11 +8,9 @@ use crate::{ verify_mod_action, verify_person_in_community, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, generate_moderators_url, - ActorType, }; use activitystreams::{ activity::kind::RemoveType, @@ -23,7 +21,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, }; use lemmy_db_queries::Joinable; @@ -78,8 +76,8 @@ impl RemoveMod { }; let activity = AnnouncableActivities::RemoveMod(remove); - let inboxes = vec![removed_mod.get_shared_inbox_or_inbox_url()]; - send_to_community_new(activity, &id, actor, community, inboxes, context).await + let inboxes = vec![removed_mod.shared_inbox_or_inbox_url()]; + send_to_community(activity, &id, actor, community, inboxes, context).await } } diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs index a47a5b728..1aadc9634 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/community/undo_block_user.rs @@ -1,15 +1,17 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, block_user::BlockUserFromCommunity}, + community::{ + announce::AnnouncableActivities, + block_user::BlockUserFromCommunity, + send_to_community, + }, generate_activity_id, verify_activity, verify_mod_action, verify_person_in_community, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, }; use activitystreams::{ activity::kind::UndoType, @@ -20,7 +22,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, }; use lemmy_db_queries::Bannable; @@ -74,8 +76,8 @@ impl UndoBlockUserFromCommunity { }; let activity = AnnouncableActivities::UndoBlockUserFromCommunity(undo); - let inboxes = vec![target.get_shared_inbox_or_inbox_url()]; - send_to_community_new(activity, &id, actor, community, inboxes, context).await + let inboxes = vec![target.shared_inbox_or_inbox_url()]; + send_to_community(activity, &id, actor, community, inboxes, context).await } } diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index 2e412f326..b71dcc71b 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -1,16 +1,14 @@ use crate::{ activities::{ - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, verify_mod_action, verify_person_in_community, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::Group, ToApub}, - ActorType, }; use activitystreams::{ activity::kind::UpdateType, @@ -21,7 +19,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, }; use lemmy_db_queries::Crud; @@ -75,7 +73,7 @@ impl UpdateCommunity { }; let activity = AnnouncableActivities::UpdateCommunity(Box::new(update)); - send_to_community_new(activity, &id, actor, community, vec![], context).await + send_to_community(activity, &id, actor, community, vec![], context).await } } diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 27338c390..5c0051d79 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -1,6 +1,6 @@ use crate::{ activities::{ - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, deletion::{ receive_delete_action, verify_delete_activity, @@ -10,10 +10,8 @@ use crate::{ generate_activity_id, verify_activity, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, }; use activitystreams::{ activity::kind::DeleteType, @@ -25,7 +23,7 @@ use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, }; use lemmy_db_queries::{ @@ -171,7 +169,7 @@ impl Delete { let delete_id = delete.id.clone(); let activity = AnnouncableActivities::Delete(delete); - send_to_community_new(activity, &delete_id, actor, community, vec![], context).await + send_to_community(activity, &delete_id, actor, community, vec![], context).await } } diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index 3dee47639..7f5dfe386 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -5,12 +5,11 @@ use crate::{ verify_person_in_community, }, fetcher::object_id::ObjectId, - ActorType, }; use diesel::PgConnection; use lemmy_api_common::blocking; use lemmy_apub_lib::{ - traits::{ActivityFields, ApubObject}, + traits::{ActivityFields, ActorType, ApubObject}, verify::verify_domains_match, }; use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_}; diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index e2bf36b38..37c2afdcb 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -1,6 +1,6 @@ use crate::{ activities::{ - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, deletion::{ delete::Delete, receive_delete_action, @@ -11,10 +11,8 @@ use crate::{ generate_activity_id, verify_activity, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, }; use activitystreams::{ activity::kind::UndoType, @@ -26,7 +24,7 @@ use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, }; use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_}; @@ -129,7 +127,7 @@ impl UndoDelete { }; let activity = AnnouncableActivities::UndoDelete(undo); - send_to_community_new(activity, &id, actor, community, vec![], context).await + send_to_community(activity, &id, actor, community, vec![], context).await } pub(in crate::activities) async fn receive_undo_remove_action( diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index 4ad3ea34e..b70b4b219 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -5,10 +5,9 @@ use crate::{ verify_activity, verify_community, }, - activity_queue::send_activity_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, + send_lemmy_activity, }; use activitystreams::{ activity::kind::AcceptType, @@ -19,7 +18,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, verify::verify_urls_match, }; use lemmy_db_queries::Followable; @@ -72,9 +71,10 @@ impl AcceptFollowCommunity { unparsed: Default::default(), }; let inbox = vec![person.inbox_url.into()]; - send_activity_new(context, &accept, &accept.id, &community, inbox, true).await + send_lemmy_activity(context, &accept, &accept.id, &community, inbox, true).await } } + /// Handle accepted follows #[async_trait::async_trait(?Send)] impl ActivityHandler for AcceptFollowCommunity { diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 35c3307c9..7e0640a1b 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -5,10 +5,9 @@ use crate::{ verify_activity, verify_person, }, - activity_queue::send_activity_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, + send_lemmy_activity, }; use activitystreams::{ activity::kind::FollowType, @@ -19,7 +18,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, verify::verify_urls_match, }; use lemmy_db_queries::Followable; @@ -84,7 +83,7 @@ impl FollowCommunity { let follow = FollowCommunity::new(actor, community, context)?; let inbox = vec![community.inbox_url.clone().into()]; - send_activity_new(context, &follow, &follow.id, actor, inbox, true).await + send_lemmy_activity(context, &follow, &follow.id, actor, inbox, true).await } } diff --git a/crates/apub/src/activities/following/undo.rs b/crates/apub/src/activities/following/undo.rs index 8a3581c51..0c4e4a8bc 100644 --- a/crates/apub/src/activities/following/undo.rs +++ b/crates/apub/src/activities/following/undo.rs @@ -5,10 +5,9 @@ use crate::{ verify_activity, verify_person, }, - activity_queue::send_activity_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, + send_lemmy_activity, }; use activitystreams::{ activity::kind::UndoType, @@ -19,7 +18,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, verify::verify_urls_match, }; use lemmy_db_queries::Followable; @@ -66,8 +65,8 @@ impl UndoFollowCommunity { context: lemmy_context(), unparsed: Default::default(), }; - let inbox = vec![community.get_shared_inbox_or_inbox_url()]; - send_activity_new(context, &undo, &undo.id, actor, inbox, true).await + let inbox = vec![community.shared_inbox_or_inbox_url()]; + send_lemmy_activity(context, &undo, &undo.id, actor, inbox, true).await } } diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 0da5d8878..5c775f801 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -22,7 +22,6 @@ pub mod deletion; pub mod following; pub mod post; pub mod private_message; -pub mod send; pub mod undo_remove; pub mod voting; diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index a2dab8dd0..8065cc879 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -1,24 +1,22 @@ use crate::{ activities::{ - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, verify_mod_action, verify_person_in_community, CreateOrUpdateType, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, objects::{post::Page, FromApub, ToApub}, - ActorType, }; use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, verify::{verify_domains_match, verify_urls_match}, }; @@ -74,7 +72,7 @@ impl CreateOrUpdatePost { }; let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update)); - send_to_community_new(activity, &id, actor, &community, vec![], context).await + send_to_community(activity, &id, actor, &community, vec![], context).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 8e212813c..a5f14d52f 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -1,16 +1,15 @@ use crate::{ activities::{generate_activity_id, verify_activity, verify_person, CreateOrUpdateType}, - activity_queue::send_activity_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, objects::{private_message::Note, FromApub, ToApub}, - ActorType, + send_lemmy_activity, }; use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, verify::verify_domains_match, }; use lemmy_db_queries::Crud; @@ -59,8 +58,8 @@ impl CreateOrUpdatePrivateMessage { kind, unparsed: Default::default(), }; - let inbox = vec![recipient.get_shared_inbox_or_inbox_url()]; - send_activity_new(context, &create_or_update, &id, actor, inbox, true).await + let inbox = vec![recipient.shared_inbox_or_inbox_url()]; + send_lemmy_activity(context, &create_or_update, &id, actor, inbox, true).await } } #[async_trait::async_trait(?Send)] diff --git a/crates/apub/src/activities/private_message/delete.rs b/crates/apub/src/activities/private_message/delete.rs index aeb8b0ec3..865cc785b 100644 --- a/crates/apub/src/activities/private_message/delete.rs +++ b/crates/apub/src/activities/private_message/delete.rs @@ -1,9 +1,8 @@ use crate::{ activities::{generate_activity_id, verify_activity, verify_person}, - activity_queue::send_activity_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, + send_lemmy_activity, }; use activitystreams::{ activity::kind::DeleteType, @@ -14,7 +13,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, verify::verify_domains_match, }; use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud}; @@ -69,8 +68,8 @@ impl DeletePrivateMessage { let recipient_id = pm.recipient_id; let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; - let inbox = vec![recipient.get_shared_inbox_or_inbox_url()]; - send_activity_new(context, &delete, &delete_id, actor, inbox, true).await + let inbox = vec![recipient.shared_inbox_or_inbox_url()]; + send_lemmy_activity(context, &delete, &delete_id, actor, inbox, true).await } } diff --git a/crates/apub/src/activities/private_message/undo_delete.rs b/crates/apub/src/activities/private_message/undo_delete.rs index 8983d06da..e609b1e44 100644 --- a/crates/apub/src/activities/private_message/undo_delete.rs +++ b/crates/apub/src/activities/private_message/undo_delete.rs @@ -5,10 +5,9 @@ use crate::{ verify_activity, verify_person, }, - activity_queue::send_activity_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, + send_lemmy_activity, }; use activitystreams::{ activity::kind::UndoType, @@ -19,7 +18,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, verify::{verify_domains_match, verify_urls_match}, }; use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud}; @@ -68,8 +67,8 @@ impl UndoDeletePrivateMessage { context: lemmy_context(), unparsed: Default::default(), }; - let inbox = vec![recipient.get_shared_inbox_or_inbox_url()]; - send_activity_new(context, &undo, &id, actor, inbox, true).await + let inbox = vec![recipient.shared_inbox_or_inbox_url()]; + send_lemmy_activity(context, &undo, &id, actor, inbox, true).await } } diff --git a/crates/apub/src/activities/send/community.rs b/crates/apub/src/activities/send/community.rs deleted file mode 100644 index 7f9c19e8c..000000000 --- a/crates/apub/src/activities/send/community.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::{check_is_apub_id_valid, ActorType, CommunityType}; -use itertools::Itertools; -use lemmy_api_common::blocking; -use lemmy_db_queries::DbPool; -use lemmy_db_schema::source::community::Community; -use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; -use lemmy_utils::{settings::structs::Settings, LemmyError}; -use url::Url; - -impl ActorType for Community { - fn is_local(&self) -> bool { - self.local - } - fn actor_id(&self) -> Url { - self.actor_id.to_owned().into() - } - fn name(&self) -> String { - self.name.clone() - } - fn public_key(&self) -> Option { - self.public_key.to_owned() - } - fn private_key(&self) -> Option { - self.private_key.to_owned() - } - - fn get_shared_inbox_or_inbox_url(&self) -> Url { - self - .shared_inbox_url - .clone() - .unwrap_or_else(|| self.inbox_url.to_owned()) - .into() - } -} - -#[async_trait::async_trait(?Send)] -impl CommunityType for Community { - fn followers_url(&self) -> Url { - self.followers_url.clone().into() - } - - /// For a given community, returns the inboxes of all followers. - async fn get_follower_inboxes( - &self, - pool: &DbPool, - settings: &Settings, - ) -> Result, LemmyError> { - let id = self.id; - - let follows = blocking(pool, move |conn| { - CommunityFollowerView::for_community(conn, id) - }) - .await??; - let inboxes = follows - .into_iter() - .filter(|f| !f.follower.local) - .map(|f| f.follower.shared_inbox_url.unwrap_or(f.follower.inbox_url)) - .map(|i| i.into_inner()) - .unique() - // Don't send to blocked instances - .filter(|inbox| check_is_apub_id_valid(inbox, false, settings).is_ok()) - .collect(); - - Ok(inboxes) - } -} diff --git a/crates/apub/src/activities/send/mod.rs b/crates/apub/src/activities/send/mod.rs deleted file mode 100644 index a01cddd6d..000000000 --- a/crates/apub/src/activities/send/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod community; -pub(crate) mod person; diff --git a/crates/apub/src/activities/send/person.rs b/crates/apub/src/activities/send/person.rs deleted file mode 100644 index cb53bd6a9..000000000 --- a/crates/apub/src/activities/send/person.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::ActorType; -use lemmy_db_schema::source::person::Person; -use url::Url; - -impl ActorType for Person { - fn is_local(&self) -> bool { - self.local - } - fn actor_id(&self) -> Url { - self.actor_id.to_owned().into_inner() - } - fn name(&self) -> String { - self.name.clone() - } - - fn public_key(&self) -> Option { - self.public_key.to_owned() - } - - fn private_key(&self) -> Option { - self.private_key.to_owned() - } - - fn get_shared_inbox_or_inbox_url(&self) -> Url { - self - .shared_inbox_url - .clone() - .unwrap_or_else(|| self.inbox_url.to_owned()) - .into() - } -} diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index cbc523bd4..d59db4a4d 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -1,6 +1,6 @@ use crate::{ activities::{ - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, verify_person_in_community, @@ -10,10 +10,8 @@ use crate::{ vote::{Vote, VoteType}, }, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, PostOrComment, }; use activitystreams::{ @@ -25,7 +23,7 @@ use activitystreams::{ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, verify::verify_urls_match, }; @@ -85,7 +83,7 @@ impl UndoVote { unparsed: Default::default(), }; let activity = AnnouncableActivities::UndoVote(undo_vote); - send_to_community_new(activity, &id, actor, &community, vec![], context).await + send_to_community(activity, &id, actor, &community, vec![], context).await } } diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 936826616..a68631759 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -1,15 +1,13 @@ use crate::{ activities::{ - community::announce::AnnouncableActivities, + community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, verify_person_in_community, voting::{vote_comment, vote_post}, }, - activity_queue::send_to_community_new, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, - ActorType, PostOrComment, }; use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; @@ -17,7 +15,7 @@ use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler}, + traits::{ActivityFields, ActivityHandler, ActorType}, values::PublicUrl, }; use lemmy_db_queries::Crud; @@ -110,7 +108,7 @@ impl Vote { let vote_id = vote.id.clone(); let activity = AnnouncableActivities::Vote(vote); - send_to_community_new(activity, &vote_id, actor, &community, vec![], context).await + send_to_community(activity, &vote_id, actor, &community, vec![], context).await } } diff --git a/crates/apub/src/extensions/context.rs b/crates/apub/src/context.rs similarity index 100% rename from crates/apub/src/extensions/context.rs rename to crates/apub/src/context.rs diff --git a/crates/apub/src/extensions/mod.rs b/crates/apub/src/extensions/mod.rs deleted file mode 100644 index 7c58789a9..000000000 --- a/crates/apub/src/extensions/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod context; -pub mod signatures; diff --git a/crates/apub/src/fetcher/fetch.rs b/crates/apub/src/fetcher/fetch.rs index 29c7f9df6..95b2f55fc 100644 --- a/crates/apub/src/fetcher/fetch.rs +++ b/crates/apub/src/fetcher/fetch.rs @@ -1,5 +1,6 @@ -use crate::{check_is_apub_id_valid, APUB_JSON_CONTENT_TYPE}; +use crate::check_is_apub_id_valid; use anyhow::anyhow; +use lemmy_apub_lib::APUB_JSON_CONTENT_TYPE; use lemmy_utils::{request::retry, settings::structs::Settings, LemmyError}; use log::info; use reqwest::Client; diff --git a/crates/apub/src/fetcher/mod.rs b/crates/apub/src/fetcher/mod.rs index 0ef72b25b..b1b9e9ad4 100644 --- a/crates/apub/src/fetcher/mod.rs +++ b/crates/apub/src/fetcher/mod.rs @@ -5,8 +5,9 @@ pub mod object_id; pub mod post_or_comment; pub mod search; -use crate::{fetcher::object_id::ObjectId, ActorType}; +use crate::fetcher::object_id::ObjectId; use chrono::NaiveDateTime; +use lemmy_apub_lib::traits::ActorType; use lemmy_db_schema::{ naive_now, source::{community::Community, person::Person}, diff --git a/crates/apub/src/fetcher/object_id.rs b/crates/apub/src/fetcher/object_id.rs index 6abdb48f4..69d7c0289 100644 --- a/crates/apub/src/fetcher/object_id.rs +++ b/crates/apub/src/fetcher/object_id.rs @@ -1,12 +1,11 @@ use crate::{ fetcher::{deletable_apub_object::DeletableApubObject, should_refetch_actor}, objects::FromApub, - APUB_JSON_CONTENT_TYPE, }; use anyhow::anyhow; use diesel::{NotFound, PgConnection}; use lemmy_api_common::blocking; -use lemmy_apub_lib::traits::ApubObject; +use lemmy_apub_lib::{traits::ApubObject, APUB_JSON_CONTENT_TYPE}; use lemmy_db_queries::DbPool; use lemmy_db_schema::DbUrl; use lemmy_utils::{request::retry, settings::structs::Settings, LemmyError}; diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index dcf02c6ea..119dfe263 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -4,8 +4,9 @@ use crate::{ extract_community, following::{follow::FollowCommunity, undo::UndoFollowCommunity}, }, - extensions::context::lemmy_context, + context::lemmy_context, generate_moderators_url, + generate_outbox_url, http::{ create_apub_response, create_apub_tombstone_response, @@ -13,7 +14,6 @@ use crate::{ receive_activity, }, objects::ToApub, - ActorType, }; use activitystreams::{ base::{AnyBase, BaseExt}, @@ -147,7 +147,7 @@ pub(crate) async fn get_apub_community_outbox( collection .set_many_items(activities) .set_many_contexts(lemmy_context()) - .set_id(community.get_outbox_url()?) + .set_id(generate_outbox_url(&community.actor_id)?.into()) .set_total_items(len as u64); Ok(create_apub_response(&collection)) } diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index 9d0508341..f9c0d2740 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -1,13 +1,11 @@ use crate::{ check_is_apub_id_valid, - extensions::signatures::verify_signature, fetcher::get_or_fetch_and_upsert_actor, http::{ community::{receive_group_inbox, GroupInboxActivities}, person::{receive_person_inbox, PersonInboxActivities}, }, insert_activity, - APUB_JSON_CONTENT_TYPE, }; use actix_web::{ body::Body, @@ -22,7 +20,9 @@ use http::StatusCode; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, + signatures::verify_signature, traits::{ActivityFields, ActivityHandler}, + APUB_JSON_CONTENT_TYPE, }; use lemmy_db_queries::{source::activity::Activity_, DbPool}; use lemmy_db_schema::source::activity::Activity; diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index 5c234c09c..e84ab2de0 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -8,7 +8,8 @@ use crate::{ undo_delete::UndoDeletePrivateMessage, }, }, - extensions::context::lemmy_context, + context::lemmy_context, + generate_outbox_url, http::{ create_apub_response, create_apub_tombstone_response, @@ -16,7 +17,6 @@ use crate::{ receive_activity, }, objects::ToApub, - ActorType, }; use activitystreams::{ base::BaseExt, @@ -105,7 +105,7 @@ pub(crate) async fn get_apub_person_outbox( collection .set_many_items(Vec::::new()) .set_many_contexts(lemmy_context()) - .set_id(person.get_outbox_url()?) + .set_id(generate_outbox_url(&person.actor_id)?.into()) .set_total_items(0_u64); Ok(create_apub_response(&collection)) } diff --git a/crates/apub/src/http/routes.rs b/crates/apub/src/http/routes.rs index b17b42559..7adb0ca9f 100644 --- a/crates/apub/src/http/routes.rs +++ b/crates/apub/src/http/routes.rs @@ -1,23 +1,21 @@ -use crate::{ - http::{ - comment::get_apub_comment, - community::{ - community_inbox, - get_apub_community_followers, - get_apub_community_http, - get_apub_community_inbox, - get_apub_community_moderators, - get_apub_community_outbox, - }, - get_activity, - person::{get_apub_person_http, get_apub_person_inbox, get_apub_person_outbox, person_inbox}, - post::get_apub_post, - shared_inbox, +use crate::http::{ + comment::get_apub_comment, + community::{ + community_inbox, + get_apub_community_followers, + get_apub_community_http, + get_apub_community_inbox, + get_apub_community_moderators, + get_apub_community_outbox, }, - APUB_JSON_CONTENT_TYPE, + get_activity, + person::{get_apub_person_http, get_apub_person_inbox, get_apub_person_outbox, person_inbox}, + post::get_apub_post, + shared_inbox, }; use actix_web::*; use http_signature_normalization_actix::digest::middleware::VerifyDigest; +use lemmy_apub_lib::APUB_JSON_CONTENT_TYPE; use lemmy_utils::settings::structs::Settings; use sha2::{Digest, Sha256}; diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index 027da7f7a..2162b644c 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -1,17 +1,14 @@ -#[macro_use] -extern crate lazy_static; - pub mod activities; -pub mod activity_queue; -pub mod extensions; +mod context; pub mod fetcher; pub mod http; pub mod migrations; pub mod objects; -use crate::{extensions::signatures::PublicKey, fetcher::post_or_comment::PostOrComment}; +use crate::fetcher::post_or_comment::PostOrComment; use anyhow::{anyhow, Context}; use lemmy_api_common::blocking; +use lemmy_apub_lib::{activity_queue::send_activity, traits::ActorType}; use lemmy_db_queries::{source::activity::Activity_, DbPool}; use lemmy_db_schema::{ source::{activity::Activity, person::Person}, @@ -20,12 +17,12 @@ use lemmy_db_schema::{ }; use lemmy_db_views_actor::community_person_ban_view::CommunityPersonBanView; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; +use lemmy_websocket::LemmyContext; +use log::info; use serde::Serialize; use std::net::IpAddr; use url::{ParseError, Url}; -static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json"; - /// Checks if the ID is allowed for sending or receiving. /// /// In particular, it checks for: @@ -92,39 +89,6 @@ pub(crate) fn check_is_apub_id_valid( Ok(()) } -/// Common methods provided by ActivityPub actors (community and person). Not all methods are -/// implemented by all actors. -trait ActorType { - fn is_local(&self) -> bool; - fn actor_id(&self) -> Url; - fn name(&self) -> String; - - // TODO: every actor should have a public key, so this shouldnt be an option (needs to be fixed in db) - fn public_key(&self) -> Option; - fn private_key(&self) -> Option; - - 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 - /// local actors). - fn get_outbox_url(&self) -> Result { - /* TODO - if !self.is_local() { - return Err(anyhow!("get_outbox_url() called for remote actor").into()); - } - */ - Ok(Url::parse(&format!("{}/outbox", &self.actor_id()))?) - } - - fn get_public_key(&self) -> Result { - Ok(PublicKey { - id: format!("{}#main-key", self.actor_id()), - owner: self.actor_id(), - public_key_pem: self.public_key().context(location_info!())?, - }) - } -} - #[async_trait::async_trait(?Send)] pub trait CommunityType { fn followers_url(&self) -> Url; @@ -192,6 +156,10 @@ pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result Ok(Url::parse(&url)?.into()) } +pub fn generate_outbox_url(actor_id: &DbUrl) -> Result { + Ok(Url::parse(&format!("{}/outbox", actor_id))?.into()) +} + fn generate_moderators_url(community_id: &DbUrl) -> Result { Ok(Url::parse(&format!("{}/moderators", community_id))?.into()) } @@ -254,3 +222,46 @@ async fn check_community_or_site_ban( Ok(()) } + +pub(crate) async fn send_lemmy_activity( + context: &LemmyContext, + activity: &T, + activity_id: &Url, + actor: &dyn ActorType, + inboxes: Vec, + sensitive: bool, +) -> Result<(), LemmyError> { + if !context.settings().federation.enabled || inboxes.is_empty() { + return Ok(()); + } + + info!("Sending activity {}", activity_id.to_string()); + + // Don't send anything to ourselves + // TODO: this should be a debug assert + let hostname = context.settings().get_hostname_without_port()?; + let inboxes: Vec<&Url> = inboxes + .iter() + .filter(|i| i.domain().expect("valid inbox url") != hostname) + .collect(); + + let serialised_activity = serde_json::to_string(&activity)?; + + insert_activity( + activity_id, + serialised_activity.clone(), + true, + sensitive, + context.pool(), + ) + .await?; + + send_activity( + serialised_activity, + actor, + inboxes, + context.client(), + context.activity_queue(), + ) + .await +} diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index fe3e73284..1e01b3f56 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -1,10 +1,9 @@ use crate::{ activities::verify_person_in_community, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, migrations::CommentInReplyToMigration, objects::{create_tombstone, FromApub, Source, ToApub}, - ActorType, PostOrComment, }; use activitystreams::{ @@ -17,6 +16,7 @@ use anyhow::{anyhow, Context}; use chrono::{DateTime, FixedOffset}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ + traits::ActorType, values::{MediaTypeHtml, MediaTypeMarkdown, PublicUrl}, verify::verify_domains_match, }; diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index cb61df5a0..4087383ab 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -1,9 +1,11 @@ use crate::{ - extensions::{context::lemmy_context, signatures::PublicKey}, + check_is_apub_id_valid, + context::lemmy_context, fetcher::community::{fetch_community_outbox, update_community_mods}, generate_moderators_url, + generate_outbox_url, objects::{create_tombstone, FromApub, ImageObject, Source, ToApub}, - ActorType, + CommunityType, }; use activitystreams::{ actor::{kind::GroupType, Endpoints}, @@ -13,8 +15,11 @@ use activitystreams::{ unparsed::Unparsed, }; use chrono::{DateTime, FixedOffset}; +use itertools::Itertools; use lemmy_api_common::blocking; use lemmy_apub_lib::{ + signatures::PublicKey, + traits::ActorType, values::{MediaTypeHtml, MediaTypeMarkdown}, verify::verify_domains_match, }; @@ -23,6 +28,7 @@ use lemmy_db_schema::{ naive_now, source::community::{Community, CommunityForm}, }; +use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; use lemmy_utils::{ settings::structs::Settings, utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html}, @@ -143,7 +149,7 @@ impl ToApub for Community { sensitive: Some(self.nsfw), moderators: Some(generate_moderators_url(&self.actor_id)?.into()), inbox: self.inbox_url.clone().into(), - outbox: self.get_outbox_url()?, + outbox: generate_outbox_url(&self.actor_id)?.into(), followers: self.followers_url.clone().into(), endpoints: Endpoints { shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()), @@ -189,3 +195,35 @@ impl FromApub for Community { Ok(community) } } + +#[async_trait::async_trait(?Send)] +impl CommunityType for Community { + fn followers_url(&self) -> Url { + self.followers_url.clone().into() + } + + /// For a given community, returns the inboxes of all followers. + async fn get_follower_inboxes( + &self, + pool: &DbPool, + settings: &Settings, + ) -> Result, LemmyError> { + let id = self.id; + + let follows = blocking(pool, move |conn| { + CommunityFollowerView::for_community(conn, id) + }) + .await??; + let inboxes = follows + .into_iter() + .filter(|f| !f.follower.local) + .map(|f| f.follower.shared_inbox_url.unwrap_or(f.follower.inbox_url)) + .map(|i| i.into_inner()) + .unique() + // Don't send to blocked instances + .filter(|inbox| check_is_apub_id_valid(inbox, false, settings).is_ok()) + .collect(); + + Ok(inboxes) + } +} diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index a34a5c429..73e34794f 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -1,8 +1,8 @@ use crate::{ check_is_apub_id_valid, - extensions::{context::lemmy_context, signatures::PublicKey}, + context::lemmy_context, + generate_outbox_url, objects::{FromApub, ImageObject, Source, ToApub}, - ActorType, }; use activitystreams::{ actor::Endpoints, @@ -14,6 +14,8 @@ use activitystreams::{ }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ + signatures::PublicKey, + traits::ActorType, values::{MediaTypeHtml, MediaTypeMarkdown}, verify::verify_domains_match, }; @@ -113,7 +115,7 @@ impl ToApub for DbPerson { image, matrix_user_id: self.matrix_user_id.clone(), published: convert_datetime(self.published), - outbox: self.get_outbox_url()?, + outbox: generate_outbox_url(&self.actor_id)?.into(), endpoints: Endpoints { shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()), ..Default::default() diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 457cb2163..c18647dd1 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -1,9 +1,8 @@ use crate::{ activities::{extract_community, verify_person_in_community}, - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, objects::{create_tombstone, FromApub, ImageObject, Source, ToApub}, - ActorType, }; use activitystreams::{ base::AnyBase, @@ -18,6 +17,7 @@ use activitystreams::{ use chrono::{DateTime, FixedOffset}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ + traits::ActorType, values::{MediaTypeHtml, MediaTypeMarkdown}, verify::verify_domains_match, }; diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 7fd0708aa..0a3822772 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -1,5 +1,5 @@ use crate::{ - extensions::context::lemmy_context, + context::lemmy_context, fetcher::object_id::ObjectId, objects::{create_tombstone, FromApub, Source, ToApub}, }; diff --git a/crates/apub_lib/Cargo.toml b/crates/apub_lib/Cargo.toml index 2a94eb375..ba21ce0a6 100644 --- a/crates/apub_lib/Cargo.toml +++ b/crates/apub_lib/Cargo.toml @@ -16,3 +16,12 @@ serde_json = { version = "1.0.68", features = ["preserve_order"] } anyhow = "1.0.44" reqwest = { version = "0.11.4", features = ["json"] } log = "0.4.14" +base64 = "0.13.0" +openssl = "0.10.36" +lazy_static = "1.4.0" +http = "0.2.5" +sha2 = "0.9.8" +actix-web = { version = "4.0.0-beta.9", default-features = false } +http-signature-normalization-actix = { version = "0.5.0-beta.10", default-features = false, features = ["server", "sha-2"] } +http-signature-normalization-reqwest = { version = "0.2.0", default-features = false, features = ["sha-2"] } +background-jobs = "0.10.0" diff --git a/crates/apub/src/activity_queue.rs b/crates/apub_lib/src/activity_queue.rs similarity index 53% rename from crates/apub/src/activity_queue.rs rename to crates/apub_lib/src/activity_queue.rs index 3359df5d2..ec1ede46b 100644 --- a/crates/apub/src/activity_queue.rs +++ b/crates/apub_lib/src/activity_queue.rs @@ -1,10 +1,4 @@ -use crate::{ - activities::community::announce::{AnnouncableActivities, AnnounceActivity}, - extensions::signatures::sign_and_send, - insert_activity, - ActorType, - APUB_JSON_CONTENT_TYPE, -}; +use crate::{signatures::sign_and_send, traits::ActorType, APUB_JSON_CONTENT_TYPE}; use anyhow::{anyhow, Context, Error}; use background_jobs::{ create_server, @@ -15,83 +9,31 @@ use background_jobs::{ QueueHandle, WorkerConfig, }; -use lemmy_db_schema::source::community::Community; use lemmy_utils::{location_info, LemmyError}; -use lemmy_websocket::LemmyContext; -use log::{info, warn}; +use log::warn; use reqwest::Client; use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, env, fmt::Debug, future::Future, pin::Pin}; use url::Url; -pub(crate) async fn send_to_community_new( - activity: AnnouncableActivities, - activity_id: &Url, +pub async fn send_activity( + activity: String, actor: &dyn ActorType, - community: &Community, - additional_inboxes: Vec, - context: &LemmyContext, + inboxes: Vec<&Url>, + client: &Client, + activity_queue: &QueueHandle, ) -> Result<(), LemmyError> { - // if this is a local community, we need to do an announce from the community instead - if community.local { - insert_activity(activity_id, activity.clone(), true, false, context.pool()).await?; - AnnounceActivity::send(activity, community, additional_inboxes, context).await?; - } else { - let mut inboxes = additional_inboxes; - inboxes.push(community.get_shared_inbox_or_inbox_url()); - send_activity_new(context, &activity, activity_id, actor, inboxes, false).await?; - } - - Ok(()) -} - -pub(crate) async fn send_activity_new( - context: &LemmyContext, - activity: &T, - activity_id: &Url, - actor: &dyn ActorType, - inboxes: Vec, - sensitive: bool, -) -> Result<(), LemmyError> -where - T: Serialize, -{ - if !context.settings().federation.enabled || inboxes.is_empty() { - return Ok(()); - } - - info!("Sending activity {}", activity_id.to_string()); - - // Don't send anything to ourselves - // TODO: this should be a debug assert - let hostname = context.settings().get_hostname_without_port()?; - let inboxes: Vec<&Url> = inboxes - .iter() - .filter(|i| i.domain().expect("valid inbox url") != hostname) - .collect(); - - let serialised_activity = serde_json::to_string(&activity)?; - - insert_activity( - activity_id, - serialised_activity.clone(), - true, - sensitive, - context.pool(), - ) - .await?; - for i in inboxes { let message = SendActivityTask { - activity: serialised_activity.to_owned(), + activity: activity.clone(), inbox: i.to_owned(), actor_id: actor.actor_id(), private_key: actor.private_key().context(location_info!())?, }; - if env::var("LEMMY_TEST_SEND_SYNC").is_ok() { - do_send(message, context.client()).await?; + if env::var("APUB_TESTING_SEND_SYNC").is_ok() { + do_send(message, client).await?; } else { - context.activity_queue.queue::(message)?; + activity_queue.queue::(message)?; } } diff --git a/crates/apub_lib/src/lib.rs b/crates/apub_lib/src/lib.rs index e20a056ae..a35f54415 100644 --- a/crates/apub_lib/src/lib.rs +++ b/crates/apub_lib/src/lib.rs @@ -1,5 +1,12 @@ +#[macro_use] +extern crate lazy_static; + +pub mod activity_queue; pub mod data; +pub mod signatures; pub mod traits; pub mod values; pub mod verify; pub mod webfinger; + +pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json"; diff --git a/crates/apub/src/extensions/signatures.rs b/crates/apub_lib/src/signatures.rs similarity index 95% rename from crates/apub/src/extensions/signatures.rs rename to crates/apub_lib/src/signatures.rs index 675740365..df9590685 100644 --- a/crates/apub/src/extensions/signatures.rs +++ b/crates/apub_lib/src/signatures.rs @@ -23,7 +23,7 @@ lazy_static! { /// Creates an HTTP post request to `inbox_url`, with the given `client` and `headers`, and /// `activity` as request body. The request is signed with `private_key` and then sent. -pub(crate) async fn sign_and_send( +pub async fn sign_and_send( client: &Client, headers: BTreeMap, inbox_url: &Url, @@ -62,7 +62,7 @@ pub(crate) async fn sign_and_send( } /// Verifies the HTTP signature on an incoming inbox request. -pub(crate) fn verify_signature(request: &HttpRequest, public_key: &str) -> Result<(), LemmyError> { +pub fn verify_signature(request: &HttpRequest, public_key: &str) -> Result<(), LemmyError> { let verified = CONFIG2 .begin_verify( request.method(), diff --git a/crates/apub_lib/src/traits.rs b/crates/apub_lib/src/traits.rs index 0577e16be..f8cdba0a0 100644 --- a/crates/apub_lib/src/traits.rs +++ b/crates/apub_lib/src/traits.rs @@ -1,7 +1,8 @@ -use crate::data::Data; +use crate::{data::Data, signatures::PublicKey}; use activitystreams::chrono::NaiveDateTime; +use anyhow::Context; pub use lemmy_apub_lib_derive::*; -use lemmy_utils::LemmyError; +use lemmy_utils::{location_info, LemmyError}; use url::Url; pub trait ActivityFields { @@ -36,3 +37,31 @@ pub trait ApubObject { where Self: Sized; } + +/// Common methods provided by ActivityPub actors (community and person). Not all methods are +/// implemented by all actors. +pub trait ActorType { + fn is_local(&self) -> bool; + fn actor_id(&self) -> Url; + fn name(&self) -> String; + + // TODO: this should not be an option (needs db migration in lemmy) + fn public_key(&self) -> Option; + fn private_key(&self) -> Option; + + fn inbox_url(&self) -> Url; + + fn shared_inbox_url(&self) -> Option; + + fn shared_inbox_or_inbox_url(&self) -> Url { + self.shared_inbox_url().unwrap_or_else(|| self.inbox_url()) + } + + fn get_public_key(&self) -> Result { + Ok(PublicKey { + id: format!("{}#main-key", self.actor_id()), + owner: self.actor_id(), + public_key_pem: self.public_key().context(location_info!())?, + }) + } +} diff --git a/crates/db_schema/src/source/community.rs b/crates/db_schema/src/source/community.rs index 6e9583550..0018e3f02 100644 --- a/crates/db_schema/src/source/community.rs +++ b/crates/db_schema/src/source/community.rs @@ -6,7 +6,7 @@ use crate::{ }; use chrono::NaiveDateTime; use diesel::{ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl}; -use lemmy_apub_lib::traits::ApubObject; +use lemmy_apub_lib::traits::{ActorType, ApubObject}; use lemmy_utils::LemmyError; use serde::Serialize; use url::Url; @@ -148,3 +148,29 @@ impl ApubObject for Community { ) } } + +impl ActorType for Community { + fn is_local(&self) -> bool { + self.local + } + fn actor_id(&self) -> Url { + self.actor_id.to_owned().into() + } + fn name(&self) -> String { + self.name.clone() + } + fn public_key(&self) -> Option { + self.public_key.to_owned() + } + fn private_key(&self) -> Option { + self.private_key.to_owned() + } + + fn inbox_url(&self) -> Url { + self.inbox_url.clone().into() + } + + fn shared_inbox_url(&self) -> Option { + self.shared_inbox_url.clone().map(|s| s.into_inner()) + } +} diff --git a/crates/db_schema/src/source/person.rs b/crates/db_schema/src/source/person.rs index ddc4b0379..3b065b936 100644 --- a/crates/db_schema/src/source/person.rs +++ b/crates/db_schema/src/source/person.rs @@ -5,7 +5,7 @@ use crate::{ }; use chrono::NaiveDateTime; use diesel::{ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl}; -use lemmy_apub_lib::traits::ApubObject; +use lemmy_apub_lib::traits::{ActorType, ApubObject}; use lemmy_utils::LemmyError; use serde::Serialize; use url::Url; @@ -195,3 +195,31 @@ impl ApubObject for Person { ) } } + +impl ActorType for Person { + fn is_local(&self) -> bool { + self.local + } + fn actor_id(&self) -> Url { + self.actor_id.to_owned().into_inner() + } + fn name(&self) -> String { + self.name.clone() + } + + fn public_key(&self) -> Option { + self.public_key.to_owned() + } + + fn private_key(&self) -> Option { + self.private_key.to_owned() + } + + fn inbox_url(&self) -> Url { + self.inbox_url.clone().into() + } + + fn shared_inbox_url(&self) -> Option { + self.shared_inbox_url.clone().map(|s| s.into_inner()) + } +} diff --git a/docker/federation/docker-compose.yml b/docker/federation/docker-compose.yml index 8b76a65a2..55b3877cc 100644 --- a/docker/federation/docker-compose.yml +++ b/docker/federation/docker-compose.yml @@ -40,7 +40,7 @@ services: volumes: - ./lemmy_alpha.hjson:/config/config.hjson environment: - - LEMMY_TEST_SEND_SYNC=1 + - APUB_TESTING_SEND_SYNC - RUST_BACKTRACE=1 - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_queries=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" depends_on: @@ -69,7 +69,7 @@ services: volumes: - ./lemmy_beta.hjson:/config/config.hjson environment: - - LEMMY_TEST_SEND_SYNC=1 + - APUB_TESTING_SEND_SYNC - RUST_BACKTRACE=1 - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_queries=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" depends_on: @@ -98,7 +98,7 @@ services: volumes: - ./lemmy_gamma.hjson:/config/config.hjson environment: - - LEMMY_TEST_SEND_SYNC=1 + - APUB_TESTING_SEND_SYNC - RUST_BACKTRACE=1 - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_queries=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" depends_on: @@ -128,7 +128,7 @@ services: volumes: - ./lemmy_delta.hjson:/config/config.hjson environment: - - LEMMY_TEST_SEND_SYNC=1 + - APUB_TESTING_SEND_SYNC - RUST_BACKTRACE=1 - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_queries=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" depends_on: @@ -158,7 +158,7 @@ services: volumes: - ./lemmy_epsilon.hjson:/config/config.hjson environment: - - LEMMY_TEST_SEND_SYNC=1 + - APUB_TESTING_SEND_SYNC - RUST_BACKTRACE=1 - RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_queries=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug" depends_on: diff --git a/src/main.rs b/src/main.rs index 4f436d2cb..7d49e32ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use diesel::{ use lemmy_api::match_websocket_operation; use lemmy_api_common::blocking; use lemmy_api_crud::match_websocket_operation_crud; -use lemmy_apub::activity_queue::create_activity_queue; +use lemmy_apub_lib::activity_queue::create_activity_queue; use lemmy_db_queries::{get_database_url_from_env, source::secret::Secret_}; use lemmy_db_schema::source::secret::Secret; use lemmy_routes::{feeds, images, nodeinfo, webfinger};