From: Nutomic Date: Tue, 21 Mar 2023 15:03:05 +0000 (+0100) Subject: Activitypub crate rewrite (#2782) X-Git-Url: http://these/git/%7B%60%24%7BwebArchiveUrl%7D/%22%7B%7D/%7BpictshareAvatarThumbnail%28admin.avatar%29%7D?a=commitdiff_plain;h=6f513793cbd8e7427812638335b55dbb0547ffec;p=lemmy.git Activitypub crate rewrite (#2782) * update activitypub-federation crate to 0.4.0 * fixes * apub compiles! * everything compiling! * almost done, federated follow failing * some test fixes * use release * add code back in --- diff --git a/Cargo.lock b/Cargo.lock index 90989393..3dc23a46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,26 +4,33 @@ version = 3 [[package]] name = "activitypub_federation" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fbd2b7fb0aea9bdd738fc1441d34d3e7b585d60b42ed63deeac289c872f119" +checksum = "b52228e706f380074b0722dae97f9c8274264026dbd3aa16d20466758995b6f4" dependencies = [ + "activitystreams-kinds", + "actix-rt", "actix-web", "anyhow", "async-trait", "background-jobs", "base64", + "bytes", "chrono", - "derive_builder 0.11.2", + "derive_builder 0.12.0", + "displaydoc", "dyn-clone", "enum_delegate", + "futures-core", "http", - "http-signature-normalization-actix", + "http-signature-normalization", "http-signature-normalization-reqwest", "httpdate", "itertools", "once_cell", "openssl", + "pin-project-lite", + "regex", "reqwest", "reqwest-middleware", "serde", @@ -695,9 +702,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bytestring" @@ -1232,11 +1239,11 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" dependencies = [ - "derive_builder_macro 0.11.2", + "derive_builder_macro 0.12.0", ] [[package]] @@ -1253,9 +1260,9 @@ dependencies = [ [[package]] name = "derive_builder_core" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling 0.14.1", "proc-macro2 1.0.47", @@ -1275,11 +1282,11 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ - "derive_builder_core 0.11.2", + "derive_builder_core 0.12.0", "syn 1.0.103", ] @@ -1418,6 +1425,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +dependencies = [ + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.103", +] + [[package]] name = "dlv-list" version = "0.3.0" @@ -2451,7 +2469,6 @@ name = "lemmy_apub" version = "0.17.1" dependencies = [ "activitypub_federation", - "activitystreams-kinds", "actix-rt", "actix-web", "anyhow", @@ -2557,6 +2574,7 @@ dependencies = [ name = "lemmy_routes" version = "0.17.1" dependencies = [ + "activitypub_federation", "actix-web", "anyhow", "chrono", @@ -3998,9 +4016,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 6fcccc4d..970ebb75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ lemmy_routes = { version = "=0.17.1", path = "./crates/routes" } lemmy_db_views = { version = "=0.17.1", path = "./crates/db_views" } lemmy_db_views_actor = { version = "=0.17.1", path = "./crates/db_views_actor" } lemmy_db_views_moderator = { version = "=0.17.1", path = "./crates/db_views_moderator" } -activitypub_federation = "0.3.5" +activitypub_federation = { version = "0.4.0", default-features = false, features = ["actix-web"] } diesel = "2.0.2" diesel_migrations = "2.0.0" diesel-async = "0.1.1" diff --git a/crates/api_common/src/context.rs b/crates/api_common/src/context.rs index eb53b2af..4c91c2c5 100644 --- a/crates/api_common/src/context.rs +++ b/crates/api_common/src/context.rs @@ -7,12 +7,12 @@ use lemmy_utils::{ use reqwest_middleware::ClientWithMiddleware; use std::sync::Arc; +#[derive(Clone)] pub struct LemmyContext { pool: DbPool, chat_server: Arc, - client: ClientWithMiddleware, - settings: Settings, - secret: Secret, + client: Arc, + secret: Arc, rate_limit_cell: RateLimitCell, } @@ -21,16 +21,14 @@ impl LemmyContext { pool: DbPool, chat_server: Arc, client: ClientWithMiddleware, - settings: Settings, secret: Secret, rate_limit_cell: RateLimitCell, ) -> LemmyContext { LemmyContext { pool, chat_server, - client, - settings, - secret, + client: Arc::new(client), + secret: Arc::new(secret), rate_limit_cell, } } @@ -53,16 +51,3 @@ impl LemmyContext { &self.rate_limit_cell } } - -impl Clone for LemmyContext { - fn clone(&self) -> Self { - LemmyContext { - pool: self.pool.clone(), - chat_server: self.chat_server.clone(), - client: self.client.clone(), - settings: self.settings.clone(), - secret: self.secret.clone(), - rate_limit_cell: self.rate_limit_cell.clone(), - } - } -} diff --git a/crates/api_common/src/request.rs b/crates/api_common/src/request.rs index 260abce1..c6f71b86 100644 --- a/crates/api_common/src/request.rs +++ b/crates/api_common/src/request.rs @@ -39,7 +39,6 @@ fn html_to_site_metadata(html_bytes: &[u8]) -> Result let first_line = html .trim_start() .lines() - .into_iter() .next() .ok_or_else(|| LemmyError::from_message("No lines in html"))? .to_lowercase(); diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 920cc6ec..524d1020 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -73,7 +73,7 @@ pub struct SearchResponse { #[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct ResolveObject { pub q: String, - pub auth: Option>, + pub auth: Sensitive, } #[derive(Debug, Serialize, Deserialize, Default)] diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index c4828d35..27f34c02 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -142,6 +142,7 @@ pub async fn mark_post_as_unread( .map_err(|e| LemmyError::from_error_message(e, "couldnt_mark_post_as_read")) } +// TODO: this should simply take LemmyContext as param #[tracing::instrument(skip_all)] pub async fn get_local_user_view_from_jwt( jwt: &str, diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index 7eb81067..8aaeb8f6 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -1,5 +1,5 @@ use crate::PerformCrud; -use activitypub_federation::core::signatures::generate_actor_keypair; +use activitypub_federation::http_signatures::generate_actor_keypair; use actix_web::web::Data; use lemmy_api_common::{ community::{CommunityResponse, CreateCommunity}, diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs index 90b5dd8c..bda7eadc 100644 --- a/crates/api_crud/src/site/create.rs +++ b/crates/api_crud/src/site/create.rs @@ -1,5 +1,5 @@ use crate::{site::check_application_question, PerformCrud}; -use activitypub_federation::core::signatures::generate_actor_keypair; +use activitypub_federation::http_signatures::generate_actor_keypair; use actix_web::web::Data; use lemmy_api_common::{ context::LemmyContext, diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index c9539200..bf48b5f5 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -1,5 +1,5 @@ use crate::PerformCrud; -use activitypub_federation::core::signatures::generate_actor_keypair; +use activitypub_federation::http_signatures::generate_actor_keypair; use actix_web::web::Data; use lemmy_api_common::{ context::LemmyContext, diff --git a/crates/apub/Cargo.toml b/crates/apub/Cargo.toml index 3a4db66d..9ac447c9 100644 --- a/crates/apub/Cargo.toml +++ b/crates/apub/Cargo.toml @@ -41,7 +41,6 @@ once_cell = { workspace = true } tokio = { workspace = true } html2md = "0.2.13" serde_with = "1.14.0" -activitystreams-kinds = "0.2.1" http-signature-normalization-actix = { version = "0.6.1", default-features = false, features = ["server", "sha-2"] } enum_delegate = "0.2.0" diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs index a2f96fd5..46f9b794 100644 --- a/crates/apub/src/activities/block/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -9,18 +9,16 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, - local_instance, + insert_activity, objects::{instance::remote_instance_inboxes, person::ApubPerson}, protocol::activities::block::block_user::BlockUser, - ActorType, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, + config::Data, + kinds::{activity::BlockType, public}, + protocol::verification::verify_domains_match, traits::{ActivityHandler, Actor}, - utils::verify_domains_match, }; -use activitystreams_kinds::{activity::BlockType, public}; use anyhow::anyhow; use chrono::NaiveDateTime; use lemmy_api_common::{ @@ -51,17 +49,17 @@ impl BlockUser { remove_data: Option, reason: Option, expires: Option, - context: &LemmyContext, + context: &Data, ) -> Result { let audience = if let SiteOrCommunity::Community(c) = target { - Some(ObjectId::new(c.actor_id())) + Some(c.id().into()) } else { None }; Ok(BlockUser { - actor: ObjectId::new(mod_.actor_id()), + actor: mod_.id().into(), to: vec![public()], - object: ObjectId::new(user.actor_id()), + object: user.id().into(), cc: generate_cc(target, context.pool()).await?, target: target.id(), kind: BlockType::Block, @@ -84,7 +82,7 @@ impl BlockUser { remove_data: bool, reason: Option, expires: Option, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let block = BlockUser::new( target, @@ -111,7 +109,7 @@ impl BlockUser { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for BlockUser { type DataType = LemmyContext; type Error = LemmyError; @@ -125,17 +123,9 @@ impl ActivityHandler for BlockUser { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_is_public(&self.to, &self.cc)?; - match self - .target - .dereference(context, local_instance(context).await, request_counter) - .await? - { + match self.target.dereference(context).await? { SiteOrCommunity::Site(site) => { let domain = self.object.inner().domain().expect("url needs domain"); if context.settings().hostname == domain { @@ -144,43 +134,24 @@ impl ActivityHandler for BlockUser { ); } // site ban can only target a user who is on the same instance as the actor (admin) - verify_domains_match(&site.actor_id(), self.actor.inner())?; - verify_domains_match(&site.actor_id(), self.object.inner())?; + verify_domains_match(&site.id(), self.actor.inner())?; + verify_domains_match(&site.id(), self.object.inner())?; } SiteOrCommunity::Community(community) => { - verify_person_in_community(&self.actor, &community, context, request_counter).await?; - verify_mod_action( - &self.actor, - self.object.inner(), - community.id, - context, - request_counter, - ) - .await?; + verify_person_in_community(&self.actor, &community, context).await?; + verify_mod_action(&self.actor, self.object.inner(), community.id, context).await?; } } Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; let expires = self.expires.map(|u| u.naive_local()); - let mod_person = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; - let blocked_person = self - .object - .dereference(context, local_instance(context).await, request_counter) - .await?; - let target = self - .target - .dereference(context, local_instance(context).await, request_counter) - .await?; + let mod_person = self.actor.dereference(context).await?; + let blocked_person = self.object.dereference(context).await?; + let target = self.target.dereference(context).await?; match target { SiteOrCommunity::Site(_site) => { let blocked_person = Person::update( diff --git a/crates/apub/src/activities/block/mod.rs b/crates/apub/src/activities/block/mod.rs index 9a5b9fcb..a1315bb7 100644 --- a/crates/apub/src/activities/block/mod.rs +++ b/crates/apub/src/activities/block/mod.rs @@ -4,10 +4,13 @@ use crate::{ activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, objects::{group::Group, instance::Instance}, }, - ActorType, SendActivity, }; -use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject}; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + traits::{Actor, Object}, +}; use chrono::NaiveDateTime; use lemmy_api_common::{ community::{BanFromCommunity, BanFromCommunityResponse}, @@ -41,11 +44,10 @@ pub enum InstanceOrGroup { Group(Group), } -#[async_trait::async_trait(?Send)] -impl ApubObject for SiteOrCommunity { +#[async_trait::async_trait] +impl Object for SiteOrCommunity { type DataType = LemmyContext; - type ApubType = InstanceOrGroup; - type DbType = (); + type Kind = InstanceOrGroup; type Error = LemmyError; #[tracing::instrument(skip_all)] @@ -57,62 +59,51 @@ impl ApubObject for SiteOrCommunity { } #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - data: &Self::DataType, + data: &Data, ) -> Result, LemmyError> where Self: Sized, { - let site = ApubSite::read_from_apub_id(object_id.clone(), data).await?; + let site = ApubSite::read_from_id(object_id.clone(), data).await?; Ok(match site { Some(o) => Some(SiteOrCommunity::Site(o)), - None => ApubCommunity::read_from_apub_id(object_id, data) + None => ApubCommunity::read_from_id(object_id, data) .await? .map(SiteOrCommunity::Community), }) } - async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> { + async fn delete(self, _data: &Data) -> Result<(), LemmyError> { unimplemented!() } - async fn into_apub(self, _data: &Self::DataType) -> Result { + async fn into_json(self, _data: &Data) -> Result { unimplemented!() } #[tracing::instrument(skip_all)] async fn verify( - apub: &Self::ApubType, + apub: &Self::Kind, expected_domain: &Url, - data: &Self::DataType, - request_counter: &mut i32, + data: &Data, ) -> Result<(), LemmyError> { match apub { - InstanceOrGroup::Instance(i) => { - ApubSite::verify(i, expected_domain, data, request_counter).await - } - InstanceOrGroup::Group(g) => { - ApubCommunity::verify(g, expected_domain, data, request_counter).await - } + InstanceOrGroup::Instance(i) => ApubSite::verify(i, expected_domain, data).await, + InstanceOrGroup::Group(g) => ApubCommunity::verify(g, expected_domain, data).await, } } #[tracing::instrument(skip_all)] - async fn from_apub( - apub: Self::ApubType, - data: &Self::DataType, - request_counter: &mut i32, - ) -> Result + async fn from_json(apub: Self::Kind, data: &Data) -> Result where Self: Sized, { Ok(match apub { - InstanceOrGroup::Instance(p) => { - SiteOrCommunity::Site(ApubSite::from_apub(p, data, request_counter).await?) - } + InstanceOrGroup::Instance(p) => SiteOrCommunity::Site(ApubSite::from_json(p, data).await?), InstanceOrGroup::Group(n) => { - SiteOrCommunity::Community(ApubCommunity::from_apub(n, data, request_counter).await?) + SiteOrCommunity::Community(ApubCommunity::from_json(n, data).await?) } }) } @@ -121,8 +112,8 @@ impl ApubObject for SiteOrCommunity { impl SiteOrCommunity { fn id(&self) -> ObjectId { match self { - SiteOrCommunity::Site(s) => ObjectId::new(s.actor_id.clone()), - SiteOrCommunity::Community(c) => ObjectId::new(c.actor_id.clone()), + SiteOrCommunity::Site(s) => ObjectId::from(s.actor_id.clone()), + SiteOrCommunity::Community(c) => ObjectId::from(c.actor_id.clone()), } } } @@ -134,18 +125,18 @@ async fn generate_cc(target: &SiteOrCommunity, pool: &DbPool) -> Result .into_iter() .map(|s| s.actor_id.into()) .collect(), - SiteOrCommunity::Community(c) => vec![c.actor_id()], + SiteOrCommunity::Community(c) => vec![c.id()], }) } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for BanPerson { type Response = BanPersonResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -182,14 +173,14 @@ impl SendActivity for BanPerson { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for BanFromCommunity { type Response = BanFromCommunityResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs index 315667ac..43bc7352 100644 --- a/crates/apub/src/activities/block/undo_block_user.rs +++ b/crates/apub/src/activities/block/undo_block_user.rs @@ -7,18 +7,16 @@ use crate::{ verify_is_public, }, activity_lists::AnnouncableActivities, - local_instance, + insert_activity, objects::{instance::remote_instance_inboxes, person::ApubPerson}, protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, - ActorType, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, + config::Data, + kinds::{activity::UndoType, public}, + protocol::verification::verify_domains_match, traits::{ActivityHandler, Actor}, - utils::verify_domains_match, }; -use activitystreams_kinds::{activity::UndoType, public}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{ @@ -38,11 +36,11 @@ impl UndoBlockUser { user: &ApubPerson, mod_: &ApubPerson, reason: Option, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let block = BlockUser::new(target, user, mod_, None, reason, None, context).await?; let audience = if let SiteOrCommunity::Community(c) = target { - Some(ObjectId::new(c.actor_id())) + Some(c.id().into()) } else { None }; @@ -52,7 +50,7 @@ impl UndoBlockUser { &context.settings().get_protocol_and_hostname(), )?; let undo = UndoBlockUser { - actor: ObjectId::new(mod_.actor_id()), + actor: mod_.id().into(), to: vec![public()], object: block, cc: generate_cc(target, context.pool()).await?, @@ -75,7 +73,7 @@ impl UndoBlockUser { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for UndoBlockUser { type DataType = LemmyContext; type Error = LemmyError; @@ -89,40 +87,20 @@ impl ActivityHandler for UndoBlockUser { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_is_public(&self.to, &self.cc)?; verify_domains_match(self.actor.inner(), self.object.actor.inner())?; - self.object.verify(context, request_counter).await?; + self.object.verify(context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let instance = local_instance(context).await; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; let expires = self.object.expires.map(|u| u.naive_local()); - let mod_person = self - .actor - .dereference(context, instance, request_counter) - .await?; - let blocked_person = self - .object - .object - .dereference(context, instance, request_counter) - .await?; - match self - .object - .target - .dereference(context, instance, request_counter) - .await? - { + let mod_person = self.actor.dereference(context).await?; + let blocked_person = self.object.object.dereference(context).await?; + match self.object.target.dereference(context).await? { SiteOrCommunity::Site(_site) => { let blocked_person = Person::update( context.pool(), diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 306913aa..116b0272 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -14,17 +14,18 @@ use crate::{ IdOrNestedObject, InCommunity, }, - ActorType, }; -use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler}; -use activitystreams_kinds::{activity::AnnounceType, public}; +use activitypub_federation::{ + config::Data, + kinds::{activity::AnnounceType, public}, + traits::{ActivityHandler, Actor}, +}; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use serde_json::Value; -use tracing::debug; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for RawAnnouncableActivities { type DataType = LemmyContext; type Error = LemmyError; @@ -38,35 +39,27 @@ impl ActivityHandler for RawAnnouncableActivities { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - _data: &Data, - _request_counter: &mut i32, - ) -> Result<(), Self::Error> { + async fn verify(&self, _data: &Data) -> Result<(), Self::Error> { Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - data: &Data, - request_counter: &mut i32, - ) -> Result<(), Self::Error> { + async fn receive(self, data: &Data) -> Result<(), Self::Error> { let activity: AnnouncableActivities = self.clone().try_into()?; // This is only for sending, not receiving so we reject it. if let AnnouncableActivities::Page(_) = activity { return Err(LemmyError::from_message("Cant receive page")); } - let community = activity.community(data, &mut 0).await?; - let actor_id = ObjectId::new(activity.actor().clone()); + let community = activity.community(data).await?; + let actor_id = activity.actor().clone().into(); // verify and receive activity - activity.verify(data, request_counter).await?; - activity.receive(data, request_counter).await?; + activity.verify(data).await?; + activity.receive(data).await?; // send to community followers if community.local { - verify_person_in_community(&actor_id, &community, data, &mut 0).await?; + verify_person_in_community(&actor_id, &community, data).await?; AnnounceActivity::send(self, &community, data).await?; } Ok(()) @@ -77,10 +70,10 @@ impl AnnounceActivity { pub(crate) fn new( object: RawAnnouncableActivities, community: &ApubCommunity, - context: &LemmyContext, + context: &Data, ) -> Result { Ok(AnnounceActivity { - actor: ObjectId::new(community.actor_id()), + actor: community.id().into(), to: vec![public()], object: IdOrNestedObject::NestedObject(object), cc: vec![community.followers_url.clone().into()], @@ -96,7 +89,7 @@ impl AnnounceActivity { pub async fn send( object: RawAnnouncableActivities, community: &ApubCommunity, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let announce = AnnounceActivity::new(object.clone(), community, context)?; let inboxes = community.get_follower_inboxes(context).await?; @@ -109,7 +102,10 @@ impl AnnounceActivity { // Hack: need to convert Page into a format which can be sent as activity, which requires // adding actor field. let announcable_page = RawAnnouncableActivities { - id: c.object.id.clone().into_inner(), + id: generate_activity_id( + AnnounceType::Announce, + &context.settings().get_protocol_and_hostname(), + )?, actor: c.actor.clone().into_inner(), other: serde_json::to_value(c.object)? .as_object() @@ -123,7 +119,7 @@ impl AnnounceActivity { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for AnnounceActivity { type DataType = LemmyContext; type Error = LemmyError; @@ -137,44 +133,23 @@ impl ActivityHandler for AnnounceActivity { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - _context: &Data, - _request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, _context: &Data) -> Result<(), LemmyError> { verify_is_public(&self.to, &self.cc)?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let object: AnnouncableActivities = self - .object - .object(context, request_counter) - .await? - .try_into()?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; + let object: AnnouncableActivities = self.object.object(context).await?.try_into()?; // This is only for sending, not receiving so we reject it. if let AnnouncableActivities::Page(_) = object { return Err(LemmyError::from_message("Cant receive page")); } - // we have to verify this here in order to avoid fetching the object twice over http - object.verify(context, request_counter).await?; - - let object_value = serde_json::to_value(&object)?; - let insert = insert_activity(object.id(), object_value, false, true, context.pool()).await?; - if !insert { - debug!( - "Received duplicate activity in announce {}", - object.id().to_string() - ); - return Ok(()); - } - object.receive(context, request_counter).await + // verify here in order to avoid fetching the object twice over http + object.verify(context).await?; + object.receive(context).await } } diff --git a/crates/apub/src/activities/community/collection_add.rs b/crates/apub/src/activities/community/collection_add.rs index b45fa278..dfab0baf 100644 --- a/crates/apub/src/activities/community/collection_add.rs +++ b/crates/apub/src/activities/community/collection_add.rs @@ -7,7 +7,7 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, - local_instance, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::{ activities::{ @@ -17,15 +17,14 @@ use crate::{ }, InCommunity, }, - ActorType, SendActivity, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, + config::Data, + fetch::object_id::ObjectId, + kinds::{activity::AddType, public}, traits::{ActivityHandler, Actor}, }; -use activitystreams_kinds::{activity::AddType, public}; use lemmy_api_common::{ community::{AddModToCommunity, AddModToCommunityResponse}, context::LemmyContext, @@ -51,21 +50,21 @@ impl CollectionAdd { community: &ApubCommunity, added_mod: &ApubPerson, actor: &ApubPerson, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let id = generate_activity_id( AddType::Add, &context.settings().get_protocol_and_hostname(), )?; let add = CollectionAdd { - actor: ObjectId::new(actor.actor_id()), + actor: actor.id().into(), to: vec![public()], - object: added_mod.actor_id(), + object: added_mod.id(), target: generate_moderators_url(&community.actor_id)?.into(), - cc: vec![community.actor_id()], + cc: vec![community.id()], kind: AddType::Add, id: id.clone(), - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }; let activity = AnnouncableActivities::CollectionAdd(add); @@ -77,28 +76,28 @@ impl CollectionAdd { community: &ApubCommunity, featured_post: &ApubPost, actor: &ApubPerson, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let id = generate_activity_id( AddType::Add, &context.settings().get_protocol_and_hostname(), )?; let add = CollectionAdd { - actor: ObjectId::new(actor.actor_id()), + actor: actor.id().into(), to: vec![public()], object: featured_post.ap_id.clone().into(), target: generate_featured_url(&community.actor_id)?.into(), - cc: vec![community.actor_id()], + cc: vec![community.id()], kind: AddType::Add, id: id.clone(), - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }; let activity = AnnouncableActivities::CollectionAdd(add); send_activity_in_community(activity, actor, community, vec![], true, context).await } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for CollectionAdd { type DataType = LemmyContext; type Error = LemmyError; @@ -112,37 +111,23 @@ impl ActivityHandler for CollectionAdd { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_is_public(&self.to, &self.cc)?; - let community = self.community(context, request_counter).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; - verify_mod_action( - &self.actor, - &self.object, - community.id, - context, - request_counter, - ) - .await?; + let community = self.community(context).await?; + verify_person_in_community(&self.actor, &community, context).await?; + verify_mod_action(&self.actor, &self.object, community.id, context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; let (community, collection_type) = Community::get_by_collection_url(context.pool(), &self.target.into()).await?; match collection_type { CollectionType::Moderators => { - let new_mod = ObjectId::::new(self.object) - .dereference(context, local_instance(context).await, request_counter) + let new_mod = ObjectId::::from(self.object) + .dereference(context) .await?; // If we had to refetch the community while parsing the activity, then the new mod has already @@ -158,10 +143,7 @@ impl ActivityHandler for CollectionAdd { CommunityModerator::join(context.pool(), &form).await?; // write mod log - let actor = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; + let actor = self.actor.dereference(context).await?; let form = ModAddCommunityForm { mod_person_id: actor.id, other_person_id: new_mod.id, @@ -173,8 +155,8 @@ impl ActivityHandler for CollectionAdd { // TODO: send websocket notification about added mod } CollectionType::Featured => { - let post = ObjectId::::new(self.object) - .dereference(context, local_instance(context).await, request_counter) + let post = ObjectId::::from(self.object) + .dereference(context) .await?; let form = PostUpdateForm::builder() .featured_community(Some(true)) @@ -186,14 +168,14 @@ impl ActivityHandler for CollectionAdd { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for AddModToCommunity { type Response = AddModToCommunityResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -223,14 +205,14 @@ impl SendActivity for AddModToCommunity { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for FeaturePost { type Response = PostResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; diff --git a/crates/apub/src/activities/community/collection_remove.rs b/crates/apub/src/activities/community/collection_remove.rs index d804e65d..8f920cf2 100644 --- a/crates/apub/src/activities/community/collection_remove.rs +++ b/crates/apub/src/activities/community/collection_remove.rs @@ -7,17 +7,16 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, - local_instance, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::{activities::community::collection_remove::CollectionRemove, InCommunity}, - ActorType, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, + config::Data, + fetch::object_id::ObjectId, + kinds::{activity::RemoveType, public}, traits::{ActivityHandler, Actor}, }; -use activitystreams_kinds::{activity::RemoveType, public}; use lemmy_api_common::{ context::LemmyContext, utils::{generate_featured_url, generate_moderators_url}, @@ -40,21 +39,21 @@ impl CollectionRemove { community: &ApubCommunity, removed_mod: &ApubPerson, actor: &ApubPerson, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let id = generate_activity_id( RemoveType::Remove, &context.settings().get_protocol_and_hostname(), )?; let remove = CollectionRemove { - actor: ObjectId::new(actor.actor_id()), + actor: actor.id().into(), to: vec![public()], - object: ObjectId::new(removed_mod.actor_id()), + object: removed_mod.id(), target: generate_moderators_url(&community.actor_id)?.into(), id: id.clone(), - cc: vec![community.actor_id()], + cc: vec![community.id()], kind: RemoveType::Remove, - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }; let activity = AnnouncableActivities::CollectionRemove(remove); @@ -66,28 +65,28 @@ impl CollectionRemove { community: &ApubCommunity, featured_post: &ApubPost, actor: &ApubPerson, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let id = generate_activity_id( RemoveType::Remove, &context.settings().get_protocol_and_hostname(), )?; let remove = CollectionRemove { - actor: ObjectId::new(actor.actor_id()), + actor: actor.id().into(), to: vec![public()], object: featured_post.ap_id.clone().into(), target: generate_featured_url(&community.actor_id)?.into(), - cc: vec![community.actor_id()], + cc: vec![community.id()], kind: RemoveType::Remove, id: id.clone(), - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }; let activity = AnnouncableActivities::CollectionRemove(remove); send_activity_in_community(activity, actor, community, vec![], true, context).await } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for CollectionRemove { type DataType = LemmyContext; type Error = LemmyError; @@ -101,38 +100,23 @@ impl ActivityHandler for CollectionRemove { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_is_public(&self.to, &self.cc)?; - let community = self.community(context, request_counter).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; - verify_mod_action( - &self.actor, - self.object.inner(), - community.id, - context, - request_counter, - ) - .await?; + let community = self.community(context).await?; + verify_person_in_community(&self.actor, &community, context).await?; + verify_mod_action(&self.actor, &self.object, community.id, context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; let (community, collection_type) = Community::get_by_collection_url(context.pool(), &self.target.into()).await?; match collection_type { CollectionType::Moderators => { - let remove_mod = self - .object - .dereference(context, local_instance(context).await, request_counter) + let remove_mod = ObjectId::::from(self.object) + .dereference(context) .await?; let form = CommunityModeratorForm { @@ -142,10 +126,7 @@ impl ActivityHandler for CollectionRemove { CommunityModerator::leave(context.pool(), &form).await?; // write mod log - let actor = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; + let actor = self.actor.dereference(context).await?; let form = ModAddCommunityForm { mod_person_id: actor.id, other_person_id: remove_mod.id, @@ -157,8 +138,8 @@ impl ActivityHandler for CollectionRemove { // TODO: send websocket notification about removed mod } CollectionType::Featured => { - let post = ObjectId::::new(self.object) - .dereference(context, local_instance(context).await, request_counter) + let post = ObjectId::::from(self.object) + .dereference(context) .await?; let form = PostUpdateForm::builder() .featured_community(Some(false)) diff --git a/crates/apub/src/activities/community/lock_page.rs b/crates/apub/src/activities/community/lock_page.rs index 8caf3bfb..498c3a32 100644 --- a/crates/apub/src/activities/community/lock_page.rs +++ b/crates/apub/src/activities/community/lock_page.rs @@ -8,7 +8,7 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, - local_instance, + insert_activity, protocol::{ activities::{ community::lock_page::{LockPage, LockType, UndoLockPage}, @@ -19,8 +19,11 @@ use crate::{ }, SendActivity, }; -use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler}; -use activitystreams_kinds::{activity::UndoType, public}; +use activitypub_federation::{ + config::Data, + kinds::{activity::UndoType, public}, + traits::ActivityHandler, +}; use lemmy_api_common::{ context::LemmyContext, post::{LockPost, PostResponse}, @@ -36,7 +39,7 @@ use lemmy_db_schema::{ use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for LockPage { type DataType = LemmyContext; type Error = LemmyError; @@ -49,42 +52,24 @@ impl ActivityHandler for LockPage { self.actor.inner() } - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), Self::Error> { + async fn verify(&self, context: &Data) -> Result<(), Self::Error> { verify_is_public(&self.to, &self.cc)?; - let community = self.community(context, request_counter).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; + let community = self.community(context).await?; + verify_person_in_community(&self.actor, &community, context).await?; check_community_deleted_or_removed(&community)?; - verify_mod_action( - &self.actor, - self.object.inner(), - community.id, - context, - request_counter, - ) - .await?; + verify_mod_action(&self.actor, self.object.inner(), community.id, context).await?; Ok(()) } - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), Self::Error> { + async fn receive(self, context: &Data) -> Result<(), Self::Error> { let form = PostUpdateForm::builder().locked(Some(true)).build(); - let post = self - .object - .dereference(context, local_instance(context).await, request_counter) - .await?; + let post = self.object.dereference(context).await?; Post::update(context.pool(), post.id, &form).await?; Ok(()) } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for UndoLockPage { type DataType = LemmyContext; type Error = LemmyError; @@ -97,50 +82,38 @@ impl ActivityHandler for UndoLockPage { self.actor.inner() } - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), Self::Error> { + async fn verify(&self, context: &Data) -> Result<(), Self::Error> { verify_is_public(&self.to, &self.cc)?; - let community = self.community(context, request_counter).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; + let community = self.community(context).await?; + verify_person_in_community(&self.actor, &community, context).await?; check_community_deleted_or_removed(&community)?; verify_mod_action( &self.actor, self.object.object.inner(), community.id, context, - request_counter, ) .await?; Ok(()) } - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), Self::Error> { + async fn receive(self, context: &Data) -> Result<(), Self::Error> { + insert_activity(&self.id, &self, false, false, context).await?; let form = PostUpdateForm::builder().locked(Some(false)).build(); - let post = self - .object - .object - .dereference(context, local_instance(context).await, request_counter) - .await?; + let post = self.object.object.dereference(context).await?; Post::update(context.pool(), post.id, &form).await?; Ok(()) } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for LockPost { type Response = PostResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -156,16 +129,16 @@ impl SendActivity for LockPost { LockType::Lock, &context.settings().get_protocol_and_hostname(), )?; - let community_id: Url = response.post_view.community.actor_id.clone().into(); - let actor = ObjectId::new(local_user_view.person.actor_id.clone()); + let community_id = response.post_view.community.actor_id.clone(); + let actor = local_user_view.person.actor_id.clone().into(); let lock = LockPage { actor, to: vec![public()], - object: ObjectId::new(response.post_view.post.ap_id.clone()), - cc: vec![community_id.clone()], + object: response.post_view.post.ap_id.clone().into(), + cc: vec![community_id.clone().into()], kind: LockType::Lock, id, - audience: Some(ObjectId::new(community_id)), + audience: Some(community_id.into()), }; let activity = if request.locked { AnnouncableActivities::LockPost(lock) diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 226d5d64..b770c47a 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -4,7 +4,7 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::community::announce::AnnounceActivity, }; -use activitypub_federation::traits::Actor; +use activitypub_federation::{config::Data, traits::Actor}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::person::PersonFollower; use lemmy_utils::error::LemmyError; @@ -36,29 +36,30 @@ pub(crate) async fn send_activity_in_community( community: &ApubCommunity, extra_inboxes: Vec, is_mod_action: bool, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { - // send to extra_inboxes - send_lemmy_activity(context, activity.clone(), actor, extra_inboxes, false).await?; + // send to any users which are mentioned or affected directly + let mut inboxes = extra_inboxes; + + // send to user followers + if !is_mod_action { + inboxes.append( + &mut PersonFollower::list_followers(context.pool(), actor.id) + .await? + .into_iter() + .map(|p| ApubPerson(p).shared_inbox_or_inbox()) + .collect(), + ); + } if community.local { // send directly to community followers AnnounceActivity::send(activity.clone().try_into()?, community, context).await?; } else { // send to the community, which will then forward to followers - let inbox = vec![community.shared_inbox_or_inbox()]; - send_lemmy_activity(context, activity.clone(), actor, inbox, false).await?; - } - - // send to those who follow `actor` - if !is_mod_action { - let inboxes = PersonFollower::list_followers(context.pool(), actor.id) - .await? - .into_iter() - .map(|p| ApubPerson(p).shared_inbox_or_inbox()) - .collect(); - send_lemmy_activity(context, activity, actor, inboxes, false).await?; + inboxes.push(community.shared_inbox_or_inbox()); } + send_lemmy_activity(context, activity.clone(), actor, inboxes, false).await?; Ok(()) } diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs index dfd0a83c..a5fab8a0 100644 --- a/crates/apub/src/activities/community/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -1,18 +1,17 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community}, - local_instance, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::community::report::Report, InCommunity}, - ActorType, PostOrComment, SendActivity, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, + config::Data, + fetch::object_id::ObjectId, + kinds::activity::FlagType, traits::{ActivityHandler, Actor}, }; -use activitystreams_kinds::activity::FlagType; use lemmy_api_common::{ comment::{CommentReportResponse, CreateCommentReport}, context::LemmyContext, @@ -31,21 +30,21 @@ use lemmy_db_views::structs::{CommentReportView, PostReportView}; use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for CreatePostReport { type Response = PostReportResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; Report::send( - ObjectId::new(response.post_report_view.post.ap_id.clone()), + ObjectId::from(response.post_report_view.post.ap_id.clone()), &local_user_view.person.into(), - ObjectId::new(response.post_report_view.community.actor_id.clone()), + ObjectId::from(response.post_report_view.community.actor_id.clone()), request.reason.to_string(), context, ) @@ -53,21 +52,21 @@ impl SendActivity for CreatePostReport { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for CreateCommentReport { type Response = CommentReportResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; Report::send( - ObjectId::new(response.comment_report_view.comment.ap_id.clone()), + ObjectId::from(response.comment_report_view.comment.ap_id.clone()), &local_user_view.person.into(), - ObjectId::new(response.comment_report_view.community.actor_id.clone()), + ObjectId::from(response.comment_report_view.community.actor_id.clone()), request.reason.to_string(), context, ) @@ -82,7 +81,7 @@ impl Report { actor: &ApubPerson, community_id: ObjectId, reason: String, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let community = community_id.dereference_local(context).await?; let kind = FlagType::Flag; @@ -91,13 +90,13 @@ impl Report { &context.settings().get_protocol_and_hostname(), )?; let report = Report { - actor: ObjectId::new(actor.actor_id()), - to: [ObjectId::new(community.actor_id())], + actor: actor.id().into(), + to: [community.id().into()], object: object_id, summary: reason, kind, id: id.clone(), - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }; let inbox = vec![community.shared_inbox_or_inbox()]; @@ -105,7 +104,7 @@ impl Report { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for Report { type DataType = LemmyContext; type Error = LemmyError; @@ -119,31 +118,17 @@ impl ActivityHandler for Report { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let community = self.community(context, request_counter).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + let community = self.community(context).await?; + verify_person_in_community(&self.actor, &community, context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let actor = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; - match self - .object - .dereference(context, local_instance(context).await, request_counter) - .await? - { + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, true, context).await?; + let actor = self.actor.dereference(context).await?; + match self.object.dereference(context).await? { PostOrComment::Post(post) => { let report_form = PostReportForm { creator_id: actor.id, diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index 969741ef..f0eaa09c 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -7,17 +7,16 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::community::update::UpdateCommunity, InCommunity}, - ActorType, SendActivity, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, - traits::{ActivityHandler, ApubObject}, + config::Data, + kinds::{activity::UpdateType, public}, + traits::{ActivityHandler, Actor, Object}, }; -use activitystreams_kinds::{activity::UpdateType, public}; use lemmy_api_common::{ community::{CommunityResponse, EditCommunity, HideCommunity}, context::LemmyContext, @@ -28,14 +27,14 @@ use lemmy_db_schema::{source::community::Community, traits::Crud}; use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for EditCommunity { type Response = CommunityResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -49,20 +48,20 @@ impl UpdateCommunity { pub async fn send( community: ApubCommunity, actor: &ApubPerson, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let id = generate_activity_id( UpdateType::Update, &context.settings().get_protocol_and_hostname(), )?; let update = UpdateCommunity { - actor: ObjectId::new(actor.actor_id()), + actor: actor.id().into(), to: vec![public()], - object: Box::new(community.clone().into_apub(context).await?), - cc: vec![community.actor_id()], + object: Box::new(community.clone().into_json(context).await?), + cc: vec![community.id()], kind: UpdateType::Update, id: id.clone(), - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }; let activity = AnnouncableActivities::UpdateCommunity(update); @@ -70,7 +69,7 @@ impl UpdateCommunity { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for UpdateCommunity { type DataType = LemmyContext; type Error = LemmyError; @@ -84,39 +83,19 @@ impl ActivityHandler for UpdateCommunity { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_is_public(&self.to, &self.cc)?; - let community = self.community(context, request_counter).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; - verify_mod_action( - &self.actor, - self.object.id.inner(), - community.id, - context, - request_counter, - ) - .await?; - ApubCommunity::verify( - &self.object, - &community.actor_id.clone().into(), - context, - request_counter, - ) - .await?; + let community = self.community(context).await?; + verify_person_in_community(&self.actor, &community, context).await?; + verify_mod_action(&self.actor, self.object.id.inner(), community.id, context).await?; + ApubCommunity::verify(&self.object, &community.actor_id.clone().into(), context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let community = self.community(context, request_counter).await?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; + let community = self.community(context).await?; let community_update_form = self.object.into_update_form(); @@ -135,14 +114,14 @@ impl ActivityHandler for UpdateCommunity { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for HideCommunity { type Response = CommunityResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs index 4177f71f..55d5e3f0 100644 --- a/crates/apub/src/activities/create_or_update/comment.rs +++ b/crates/apub/src/activities/create_or_update/comment.rs @@ -8,23 +8,22 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, - local_instance, + insert_activity, mentions::MentionOrValue, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson}, protocol::{ activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType}, InCommunity, }, - ActorType, SendActivity, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, - traits::{ActivityHandler, Actor, ApubObject}, - utils::verify_domains_match, + config::Data, + fetch::object_id::ObjectId, + kinds::public, + protocol::verification::verify_domains_match, + traits::{ActivityHandler, Actor, Object}, }; -use activitystreams_kinds::public; use lemmy_api_common::{ comment::{CommentResponse, CreateComment, EditComment}, context::LemmyContext, @@ -44,14 +43,14 @@ use lemmy_db_schema::{ use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for CreateComment { type Response = CommentResponse; async fn send_activity( _request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { CreateOrUpdateNote::send( &response.comment_view.comment, @@ -63,14 +62,14 @@ impl SendActivity for CreateComment { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for EditComment { type Response = CommentResponse; async fn send_activity( _request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { CreateOrUpdateNote::send( &response.comment_view.comment, @@ -88,7 +87,7 @@ impl CreateOrUpdateNote { comment: &Comment, person_id: PersonId, kind: CreateOrUpdateType, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { // TODO: might be helpful to add a comment method to retrieve community directly let post_id = comment.post_id; @@ -101,17 +100,17 @@ impl CreateOrUpdateNote { kind.clone(), &context.settings().get_protocol_and_hostname(), )?; - let note = ApubComment(comment.clone()).into_apub(context).await?; + let note = ApubComment(comment.clone()).into_json(context).await?; let create_or_update = CreateOrUpdateNote { - actor: ObjectId::new(person.actor_id()), + actor: person.id().into(), to: vec![public()], cc: note.cc.clone(), tag: note.tag.clone(), object: note, kind, id: id.clone(), - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }; let tagged_users: Vec> = create_or_update @@ -125,13 +124,11 @@ impl CreateOrUpdateNote { } }) .map(|t| t.href.clone()) - .map(ObjectId::new) + .map(ObjectId::from) .collect(); let mut inboxes = vec![]; for t in tagged_users { - let person = t - .dereference(context, local_instance(context).await, &mut 0) - .await?; + let person = t.dereference(context).await?; inboxes.push(person.shared_inbox_or_inbox()); } @@ -140,7 +137,7 @@ impl CreateOrUpdateNote { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for CreateOrUpdateNote { type DataType = LemmyContext; type Error = LemmyError; @@ -154,47 +151,37 @@ impl ActivityHandler for CreateOrUpdateNote { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_is_public(&self.to, &self.cc)?; - let post = self.object.get_parents(context, request_counter).await?.0; - let community = self.community(context, request_counter).await?; + let post = self.object.get_parents(context).await?.0; + let community = self.community(context).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context).await?; verify_domains_match(self.actor.inner(), self.object.id.inner())?; check_community_deleted_or_removed(&community)?; check_post_deleted_or_removed(&post)?; - ApubComment::verify(&self.object, self.actor.inner(), context, request_counter).await?; + ApubComment::verify(&self.object, self.actor.inner(), context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - // Need to do this check here instead of Note::from_apub because we need the person who + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; + // Need to do this check here instead of Note::from_json because we need the person who // send the activity, not the comment author. let existing_comment = self.object.id.dereference_local(context).await.ok(); if let (Some(distinguished), Some(existing_comment)) = (self.object.distinguished, existing_comment) { if distinguished != existing_comment.distinguished { - let creator = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; - let (post, _) = self.object.get_parents(context, request_counter).await?; + let creator = self.actor.dereference(context).await?; + let (post, _) = self.object.get_parents(context).await?; is_mod_or_admin(context.pool(), creator.id, post.community_id).await?; } } - let comment = ApubComment::from_apub(self.object, context, request_counter).await?; + let comment = ApubComment::from_json(self.object, context).await?; // author likes their own comment by default let like_form = CommentLikeForm { @@ -206,14 +193,8 @@ impl ActivityHandler for CreateOrUpdateNote { CommentLike::like(context.pool(), &like_form).await?; let do_send_email = self.kind == CreateOrUpdateType::Create; - let recipients = get_comment_notif_recipients( - &self.actor, - &comment, - do_send_email, - context, - request_counter, - ) - .await?; + let recipients = + get_comment_notif_recipients(&self.actor, &comment, do_send_email, context).await?; let notif_type = match self.kind { CreateOrUpdateType::Create => UserOperationCrud::CreateComment, CreateOrUpdateType::Update => UserOperationCrud::EditComment, diff --git a/crates/apub/src/activities/create_or_update/mod.rs b/crates/apub/src/activities/create_or_update/mod.rs index 1f8ae79c..b1fcfb6e 100644 --- a/crates/apub/src/activities/create_or_update/mod.rs +++ b/crates/apub/src/activities/create_or_update/mod.rs @@ -1,5 +1,5 @@ -use crate::{local_instance, objects::person::ApubPerson}; -use activitypub_federation::core::object_id::ObjectId; +use crate::objects::person::ApubPerson; +use activitypub_federation::{config::Data, fetch::object_id::ObjectId}; use lemmy_api_common::{context::LemmyContext, websocket::send::send_local_notifs}; use lemmy_db_schema::{ newtypes::LocalUserId, @@ -17,14 +17,11 @@ async fn get_comment_notif_recipients( actor: &ObjectId, comment: &Comment, do_send_email: bool, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result, LemmyError> { let post_id = comment.post_id; let post = Post::read(context.pool(), post_id).await?; - let actor = actor - .dereference(context, local_instance(context).await, request_counter) - .await?; + let actor = actor.dereference(context).await?; // Note: // Although mentions could be gotten from the post tags (they are included there), or the ccs, diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs index d2e8e76c..169db5ea 100644 --- a/crates/apub/src/activities/create_or_update/post.rs +++ b/crates/apub/src/activities/create_or_update/post.rs @@ -8,21 +8,20 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::{ activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType}, InCommunity, }, - ActorType, SendActivity, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, - traits::{ActivityHandler, ApubObject}, - utils::{verify_domains_match, verify_urls_match}, + config::Data, + kinds::public, + protocol::verification::{verify_domains_match, verify_urls_match}, + traits::{ActivityHandler, Actor, Object}, }; -use activitystreams_kinds::public; use lemmy_api_common::{ context::LemmyContext, post::{CreatePost, EditPost, PostResponse}, @@ -40,14 +39,14 @@ use lemmy_db_schema::{ use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for CreatePost { type Response = PostResponse; async fn send_activity( _request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { CreateOrUpdatePage::send( &response.post_view.post, @@ -59,14 +58,14 @@ impl SendActivity for CreatePost { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for EditPost { type Response = PostResponse; async fn send_activity( _request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { CreateOrUpdatePage::send( &response.post_view.post, @@ -84,20 +83,20 @@ impl CreateOrUpdatePage { actor: &ApubPerson, community: &ApubCommunity, kind: CreateOrUpdateType, - context: &LemmyContext, + context: &Data, ) -> Result { let id = generate_activity_id( kind.clone(), &context.settings().get_protocol_and_hostname(), )?; Ok(CreateOrUpdatePage { - actor: ObjectId::new(actor.actor_id()), + actor: actor.id().into(), to: vec![public()], - object: post.into_apub(context).await?, - cc: vec![community.actor_id()], + object: post.into_json(context).await?, + cc: vec![community.id()], kind, id: id.clone(), - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }) } @@ -106,7 +105,7 @@ impl CreateOrUpdatePage { post: &Post, person_id: PersonId, kind: CreateOrUpdateType, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let post = ApubPost(post.clone()); let community_id = post.community_id; @@ -130,7 +129,7 @@ impl CreateOrUpdatePage { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for CreateOrUpdatePage { type DataType = LemmyContext; type Error = LemmyError; @@ -144,14 +143,10 @@ impl ActivityHandler for CreateOrUpdatePage { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_is_public(&self.to, &self.cc)?; - let community = self.community(context, request_counter).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; + let community = self.community(context).await?; + verify_person_in_community(&self.actor, &community, context).await?; check_community_deleted_or_removed(&community)?; match self.kind { @@ -173,31 +168,21 @@ impl ActivityHandler for CreateOrUpdatePage { CreateOrUpdateType::Update => { let is_mod_action = self.object.is_mod_action(context).await?; if is_mod_action { - verify_mod_action( - &self.actor, - self.object.id.inner(), - community.id, - context, - request_counter, - ) - .await?; + verify_mod_action(&self.actor, self.object.id.inner(), community.id, context).await?; } else { verify_domains_match(self.actor.inner(), self.object.id.inner())?; verify_urls_match(self.actor.inner(), self.object.creator()?.inner())?; } } } - ApubPost::verify(&self.object, self.actor.inner(), context, request_counter).await?; + ApubPost::verify(&self.object, self.actor.inner(), context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let post = ApubPost::from_apub(self.object, context, request_counter).await?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; + let post = ApubPost::from_json(self.object, context).await?; // author likes their own post by default let like_form = PostLikeForm { diff --git a/crates/apub/src/activities/create_or_update/private_message.rs b/crates/apub/src/activities/create_or_update/private_message.rs index 071209c3..cacfc1a8 100644 --- a/crates/apub/src/activities/create_or_update/private_message.rs +++ b/crates/apub/src/activities/create_or_update/private_message.rs @@ -1,18 +1,17 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_person}, + insert_activity, objects::{person::ApubPerson, private_message::ApubPrivateMessage}, protocol::activities::{ create_or_update::chat_message::CreateOrUpdateChatMessage, CreateOrUpdateType, }, - ActorType, SendActivity, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, - traits::{ActivityHandler, Actor, ApubObject}, - utils::verify_domains_match, + config::Data, + protocol::verification::verify_domains_match, + traits::{ActivityHandler, Actor, Object}, }; use lemmy_api_common::{ context::LemmyContext, @@ -27,14 +26,14 @@ use lemmy_db_schema::{ use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for CreatePrivateMessage { type Response = PrivateMessageResponse; async fn send_activity( _request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { CreateOrUpdateChatMessage::send( &response.private_message_view.private_message, @@ -45,14 +44,14 @@ impl SendActivity for CreatePrivateMessage { .await } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for EditPrivateMessage { type Response = PrivateMessageResponse; async fn send_activity( _request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { CreateOrUpdateChatMessage::send( &response.private_message_view.private_message, @@ -70,7 +69,7 @@ impl CreateOrUpdateChatMessage { private_message: &PrivateMessage, sender_id: PersonId, kind: CreateOrUpdateType, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let recipient_id = private_message.recipient_id; let sender: ApubPerson = Person::read(context.pool(), sender_id).await?.into(); @@ -82,10 +81,10 @@ impl CreateOrUpdateChatMessage { )?; let create_or_update = CreateOrUpdateChatMessage { id: id.clone(), - actor: ObjectId::new(sender.actor_id()), - to: [ObjectId::new(recipient.actor_id())], + actor: sender.id().into(), + to: [recipient.id().into()], object: ApubPrivateMessage(private_message.clone()) - .into_apub(context) + .into_json(context) .await?, kind, }; @@ -94,7 +93,7 @@ impl CreateOrUpdateChatMessage { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for CreateOrUpdateChatMessage { type DataType = LemmyContext; type Error = LemmyError; @@ -108,26 +107,18 @@ impl ActivityHandler for CreateOrUpdateChatMessage { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_person(&self.actor, context, request_counter).await?; + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + verify_person(&self.actor, context).await?; verify_domains_match(self.actor.inner(), self.object.id.inner())?; verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?; - ApubPrivateMessage::verify(&self.object, self.actor.inner(), context, request_counter).await?; + ApubPrivateMessage::verify(&self.object, self.actor.inner(), context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let private_message = - ApubPrivateMessage::from_apub(self.object, context, request_counter).await?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, true, context).await?; + let private_message = ApubPrivateMessage::from_json(self.object, context).await?; let notif_type = match self.kind { CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage, diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 138a1fae..337ba9e8 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -3,12 +3,11 @@ use crate::{ deletion::{receive_delete_action, verify_delete_activity, DeletableObjects}, generate_activity_id, }, - local_instance, - objects::{community::ApubCommunity, person::ApubPerson}, + insert_activity, + objects::person::ApubPerson, protocol::{activities::deletion::delete::Delete, IdOrNestedObject}, }; -use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler}; -use activitystreams_kinds::activity::DeleteType; +use activitypub_federation::{config::Data, kinds::activity::DeleteType, traits::ActivityHandler}; use lemmy_api_common::{ context::LemmyContext, websocket::{ @@ -35,7 +34,7 @@ use lemmy_db_schema::{ use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for Delete { type DataType = LemmyContext; type Error = LemmyError; @@ -49,21 +48,14 @@ impl ActivityHandler for Delete { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?; + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + verify_delete_activity(self, self.summary.is_some(), context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; if let Some(reason) = self.summary { // We set reason to empty string if it doesn't exist, to distinguish between delete and // remove. Here we change it back to option, so we don't write it to db. @@ -73,24 +65,14 @@ impl ActivityHandler for Delete { Some(reason) }; receive_remove_action( - &self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?, + &self.actor.dereference(context).await?, self.object.id(), reason, context, ) .await } else { - receive_delete_action( - self.object.id(), - &self.actor, - true, - context, - request_counter, - ) - .await + receive_delete_action(self.object.id(), &self.actor, true, context).await } } } @@ -102,7 +84,7 @@ impl Delete { to: Url, community: Option<&Community>, summary: Option, - context: &LemmyContext, + context: &Data, ) -> Result { let id = generate_activity_id( DeleteType::Delete, @@ -110,14 +92,14 @@ impl Delete { )?; let cc: Option = community.map(|c| c.actor_id.clone().into()); Ok(Delete { - actor: ObjectId::new(actor.actor_id.clone()), + actor: actor.actor_id.clone().into(), to: vec![to], object: IdOrNestedObject::Id(object.id()), cc: cc.into_iter().collect(), kind: DeleteType::Delete, summary, id, - audience: community.map(|c| ObjectId::::new(c.actor_id.clone())), + audience: community.map(|c| c.actor_id.clone().into()), }) } } @@ -127,7 +109,7 @@ pub(in crate::activities) async fn receive_remove_action( actor: &ApubPerson, object: &Url, reason: Option, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { use UserOperationCrud::*; match DeletableObjects::read_from_db(object, context).await? { diff --git a/crates/apub/src/activities/deletion/delete_user.rs b/crates/apub/src/activities/deletion/delete_user.rs index 4ace431e..e5c1021c 100644 --- a/crates/apub/src/activities/deletion/delete_user.rs +++ b/crates/apub/src/activities/deletion/delete_user.rs @@ -1,17 +1,16 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person}, - local_instance, + insert_activity, objects::{instance::remote_instance_inboxes, person::ApubPerson}, protocol::activities::deletion::delete_user::DeleteUser, SendActivity, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, - traits::ActivityHandler, - utils::verify_urls_match, + config::Data, + kinds::{activity::DeleteType, public}, + protocol::verification::verify_urls_match, + traits::{ActivityHandler, Actor}, }; -use activitystreams_kinds::{activity::DeleteType, public}; use lemmy_api_common::{ context::LemmyContext, person::{DeleteAccount, DeleteAccountResponse}, @@ -20,14 +19,14 @@ use lemmy_api_common::{ use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for DeleteAccount { type Response = DeleteAccountResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -40,15 +39,14 @@ impl SendActivity for DeleteAccount { ) .await?; - let actor_id = ObjectId::new(actor.actor_id.clone()); let id = generate_activity_id( DeleteType::Delete, &context.settings().get_protocol_and_hostname(), )?; let delete = DeleteUser { - actor: actor_id.clone(), + actor: actor.id().into(), to: vec![public()], - object: actor_id, + object: actor.id().into(), kind: DeleteType::Delete, id: id.clone(), cc: vec![], @@ -62,7 +60,7 @@ impl SendActivity for DeleteAccount { /// This can be separate from Delete activity because it doesn't need to be handled in shared inbox /// (cause instance actor doesn't have shared inbox). -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for DeleteUser { type DataType = LemmyContext; type Error = LemmyError; @@ -75,26 +73,16 @@ impl ActivityHandler for DeleteUser { self.actor.inner() } - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_is_public(&self.to, &[])?; - verify_person(&self.actor, context, request_counter).await?; + verify_person(&self.actor, context).await?; verify_urls_match(self.actor.inner(), self.object.inner())?; Ok(()) } - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let actor = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; + let actor = self.actor.dereference(context).await?; delete_user_account( actor.id, context.pool(), diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index c17dcbfc..7f901d59 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -8,7 +8,6 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, - local_instance, objects::{ comment::ApubComment, community::ApubCommunity, @@ -20,15 +19,15 @@ use crate::{ activities::deletion::{delete::Delete, undo_delete::UndoDelete}, InCommunity, }, - ActorType, SendActivity, }; use activitypub_federation::{ - core::object_id::ObjectId, - traits::{Actor, ApubObject}, - utils::verify_domains_match, + config::Data, + fetch::object_id::ObjectId, + kinds::public, + protocol::verification::verify_domains_match, + traits::{Actor, Object}, }; -use activitystreams_kinds::public; use lemmy_api_common::{ comment::{CommentResponse, DeleteComment, RemoveComment}, community::{CommunityResponse, DeleteCommunity, RemoveCommunity}, @@ -64,14 +63,14 @@ pub mod delete; pub mod delete_user; pub mod undo_delete; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for DeletePost { type Response = PostResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -89,14 +88,14 @@ impl SendActivity for DeletePost { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for RemovePost { type Response = PostResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -114,14 +113,14 @@ impl SendActivity for RemovePost { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for DeleteComment { type Response = CommentResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let community_id = response.comment_view.community.id; let community = Community::read(context.pool(), community_id).await?; @@ -132,14 +131,14 @@ impl SendActivity for DeleteComment { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for RemoveComment { type Response = CommentResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -158,14 +157,14 @@ impl SendActivity for RemoveComment { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for DeletePrivateMessage { type Response = PrivateMessageResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -179,14 +178,14 @@ impl SendActivity for DeletePrivateMessage { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for DeleteCommunity { type Response = CommunityResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -204,14 +203,14 @@ impl SendActivity for DeleteCommunity { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for RemoveCommunity { type Response = CommunityResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; @@ -238,7 +237,7 @@ async fn send_apub_delete_in_community( object: DeletableObjects, reason: Option, deleted: bool, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let actor = ApubPerson::from(actor); let is_mod_action = reason.is_some(); @@ -265,7 +264,7 @@ async fn send_apub_delete_private_message( actor: &ApubPerson, pm: PrivateMessage, deleted: bool, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let recipient_id = pm.recipient_id; let recipient: ApubPerson = Person::read(context.pool(), recipient_id).await?.into(); @@ -273,10 +272,10 @@ async fn send_apub_delete_private_message( let deletable = DeletableObjects::PrivateMessage(pm.into()); let inbox = vec![recipient.shared_inbox_or_inbox()]; if deleted { - let delete = Delete::new(actor, deletable, recipient.actor_id(), None, None, context)?; + let delete = Delete::new(actor, deletable, recipient.id(), None, None, context)?; send_lemmy_activity(context, delete, actor, inbox, true).await?; } else { - let undo = UndoDelete::new(actor, deletable, recipient.actor_id(), None, None, context)?; + let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, context)?; send_lemmy_activity(context, undo, actor, inbox, true).await?; }; Ok(()) @@ -293,18 +292,18 @@ impl DeletableObjects { #[tracing::instrument(skip_all)] pub(crate) async fn read_from_db( ap_id: &Url, - context: &LemmyContext, + context: &Data, ) -> Result { - if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? { + if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? { return Ok(DeletableObjects::Community(c)); } - if let Some(p) = ApubPost::read_from_apub_id(ap_id.clone(), context).await? { + if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? { return Ok(DeletableObjects::Post(p)); } - if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? { + if let Some(c) = ApubComment::read_from_id(ap_id.clone(), context).await? { return Ok(DeletableObjects::Comment(c)); } - if let Some(p) = ApubPrivateMessage::read_from_apub_id(ap_id.clone(), context).await? { + if let Some(p) = ApubPrivateMessage::read_from_id(ap_id.clone(), context).await? { return Ok(DeletableObjects::PrivateMessage(p)); } Err(diesel::NotFound.into()) @@ -312,7 +311,7 @@ impl DeletableObjects { pub(crate) fn id(&self) -> Url { match self { - DeletableObjects::Community(c) => c.actor_id(), + DeletableObjects::Community(c) => c.id(), DeletableObjects::Comment(c) => c.ap_id.clone().into(), DeletableObjects::Post(p) => p.ap_id.clone().into(), DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(), @@ -324,8 +323,7 @@ impl DeletableObjects { pub(in crate::activities) async fn verify_delete_activity( activity: &Delete, is_mod_action: bool, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { let object = DeletableObjects::read_from_db(activity.object.id(), context).await?; match object { @@ -334,27 +332,19 @@ pub(in crate::activities) async fn verify_delete_activity( if community.local { // can only do this check for local community, in remote case it would try to fetch the // deleted community (which fails) - verify_person_in_community(&activity.actor, &community, context, request_counter).await?; + verify_person_in_community(&activity.actor, &community, context).await?; } // community deletion is always a mod (or admin) action - verify_mod_action( - &activity.actor, - activity.object.id(), - community.id, - context, - request_counter, - ) - .await?; + verify_mod_action(&activity.actor, activity.object.id(), community.id, context).await?; } DeletableObjects::Post(p) => { verify_is_public(&activity.to, &[])?; verify_delete_post_or_comment( &activity.actor, &p.ap_id.clone().into(), - &activity.community(context, request_counter).await?, + &activity.community(context).await?, is_mod_action, context, - request_counter, ) .await?; } @@ -363,15 +353,14 @@ pub(in crate::activities) async fn verify_delete_activity( verify_delete_post_or_comment( &activity.actor, &c.ap_id.clone().into(), - &activity.community(context, request_counter).await?, + &activity.community(context).await?, is_mod_action, context, - request_counter, ) .await?; } DeletableObjects::PrivateMessage(_) => { - verify_person(&activity.actor, context, request_counter).await?; + verify_person(&activity.actor, context).await?; verify_domains_match(activity.actor.inner(), activity.object.id())?; } } @@ -384,12 +373,11 @@ async fn verify_delete_post_or_comment( object_id: &Url, community: &ApubCommunity, is_mod_action: bool, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { - verify_person_in_community(actor, community, context, request_counter).await?; + verify_person_in_community(actor, community, context).await?; if is_mod_action { - verify_mod_action(actor, object_id, community.id, context, request_counter).await?; + verify_mod_action(actor, object_id, community.id, context).await?; } else { // domain of post ap_id and post.creator ap_id are identical, so we just check the former verify_domains_match(actor.inner(), object_id)?; @@ -403,17 +391,12 @@ async fn receive_delete_action( object: &Url, actor: &ObjectId, deleted: bool, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { match DeletableObjects::read_from_db(object, context).await? { DeletableObjects::Community(community) => { if community.local { - let mod_: Person = actor - .dereference(context, local_instance(context).await, request_counter) - .await? - .deref() - .clone(); + let mod_: Person = actor.dereference(context).await?.deref().clone(); let object = DeletableObjects::Community(community.clone()); let c: Community = community.deref().deref().clone(); send_apub_delete_in_community(mod_, c, object, None, true, context).await?; diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index b3cef385..d1401838 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -3,12 +3,11 @@ use crate::{ deletion::{receive_delete_action, verify_delete_activity, DeletableObjects}, generate_activity_id, }, - local_instance, - objects::{community::ApubCommunity, person::ApubPerson}, + insert_activity, + objects::person::ApubPerson, protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete}, }; -use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler}; -use activitystreams_kinds::activity::UndoType; +use activitypub_federation::{config::Data, kinds::activity::UndoType, traits::ActivityHandler}; use lemmy_api_common::{ context::LemmyContext, websocket::{ @@ -35,7 +34,7 @@ use lemmy_db_schema::{ use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for UndoDelete { type DataType = LemmyContext; type Error = LemmyError; @@ -48,48 +47,24 @@ impl ActivityHandler for UndoDelete { self.actor.inner() } - #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - self.object.verify(context, request_counter).await?; - verify_delete_activity( - &self.object, - self.object.summary.is_some(), - context, - request_counter, - ) - .await?; + async fn verify(&self, data: &Data) -> Result<(), Self::Error> { + self.object.verify(data).await?; + verify_delete_activity(&self.object, self.object.summary.is_some(), data).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, false, context).await?; if self.object.summary.is_some() { UndoDelete::receive_undo_remove_action( - &self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?, + &self.actor.dereference(context).await?, self.object.object.id(), context, ) .await } else { - receive_delete_action( - self.object.object.id(), - &self.actor, - false, - context, - request_counter, - ) - .await + receive_delete_action(self.object.object.id(), &self.actor, false, context).await } } } @@ -102,7 +77,7 @@ impl UndoDelete { to: Url, community: Option<&Community>, summary: Option, - context: &LemmyContext, + context: &Data, ) -> Result { let object = Delete::new(actor, object, to.clone(), community, summary, context)?; @@ -112,13 +87,13 @@ impl UndoDelete { )?; let cc: Option = community.map(|c| c.actor_id.clone().into()); Ok(UndoDelete { - actor: ObjectId::new(actor.actor_id.clone()), + actor: actor.actor_id.clone().into(), to: vec![to], object, cc: cc.into_iter().collect(), kind: UndoType::Undo, id, - audience: community.map(|c| ObjectId::::new(c.actor_id.clone())), + audience: community.map(|c| c.actor_id.clone().into()), }) } @@ -126,7 +101,7 @@ impl UndoDelete { pub(in crate::activities) async fn receive_undo_remove_action( actor: &ApubPerson, object: &Url, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { use UserOperationCrud::*; match DeletableObjects::read_from_db(object, context).await? { diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index bd3e5cad..250fbf54 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -1,16 +1,14 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity}, - local_instance, + insert_activity, protocol::activities::following::{accept::AcceptFollow, follow::Follow}, - ActorType, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, + config::Data, + kinds::activity::AcceptType, + protocol::verification::verify_urls_match, traits::{ActivityHandler, Actor}, - utils::verify_urls_match, }; -use activitystreams_kinds::activity::AcceptType; use lemmy_api_common::{ community::CommunityResponse, context::LemmyContext, @@ -27,19 +25,11 @@ use url::Url; impl AcceptFollow { #[tracing::instrument(skip_all)] - pub async fn send( - follow: Follow, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + pub async fn send(follow: Follow, context: &Data) -> Result<(), LemmyError> { let user_or_community = follow.object.dereference_local(context).await?; - let person = follow - .actor - .clone() - .dereference(context, local_instance(context).await, request_counter) - .await?; + let person = follow.actor.clone().dereference(context).await?; let accept = AcceptFollow { - actor: ObjectId::new(user_or_community.actor_id()), + actor: user_or_community.id().into(), object: follow, kind: AcceptType::Accept, id: generate_activity_id( @@ -53,7 +43,7 @@ impl AcceptFollow { } /// Handle accepted follows -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for AcceptFollow { type DataType = LemmyContext; type Error = LemmyError; @@ -67,31 +57,17 @@ impl ActivityHandler for AcceptFollow { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_urls_match(self.actor.inner(), self.object.object.inner())?; - self.object.verify(context, request_counter).await?; + self.object.verify(context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let community = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; - let person = self - .object - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, true, context).await?; + let community = self.actor.dereference(context).await?; + let person = self.object.actor.dereference(context).await?; // This will throw an error if no follow was requested let community_id = community.id; let person_id = person.id; diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 62bd2415..41d2e71e 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -6,22 +6,20 @@ use crate::{ verify_person_in_community, }, fetcher::user_or_community::UserOrCommunity, - local_instance, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::following::{ accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow, }, - ActorType, SendActivity, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, + config::Data, + kinds::activity::FollowType, traits::{ActivityHandler, Actor}, }; -use activitystreams_kinds::activity::FollowType; use lemmy_api_common::{ community::{BlockCommunity, BlockCommunityResponse}, context::LemmyContext, @@ -41,11 +39,11 @@ impl Follow { pub(in crate::activities::following) fn new( actor: &ApubPerson, community: &ApubCommunity, - context: &LemmyContext, + context: &Data, ) -> Result { Ok(Follow { - actor: ObjectId::new(actor.actor_id()), - object: ObjectId::new(community.actor_id()), + actor: actor.id().into(), + object: community.id().into(), kind: FollowType::Follow, id: generate_activity_id( FollowType::Follow, @@ -58,7 +56,7 @@ impl Follow { pub async fn send( actor: &ApubPerson, community: &ApubCommunity, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let community_follower_form = CommunityFollowerForm { community_id: community.id, @@ -75,7 +73,7 @@ impl Follow { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for Follow { type DataType = LemmyContext; type Error = LemmyError; @@ -89,36 +87,20 @@ impl ActivityHandler for Follow { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_person(&self.actor, context, request_counter).await?; - let object = self - .object - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + verify_person(&self.actor, context).await?; + let object = self.object.dereference(context).await?; if let UserOrCommunity::Community(c) = object { - verify_person_in_community(&self.actor, &c, context, request_counter).await?; + verify_person_in_community(&self.actor, &c, context).await?; } Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let actor = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; - let object = self - .object - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, true, context).await?; + let actor = self.actor.dereference(context).await?; + let object = self.object.dereference(context).await?; match object { UserOrCommunity::User(u) => { let form = PersonFollowerForm { @@ -138,18 +120,18 @@ impl ActivityHandler for Follow { } } - AcceptFollow::send(self, context, request_counter).await + AcceptFollow::send(self, context).await } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for BlockCommunity { type Response = BlockCommunityResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; diff --git a/crates/apub/src/activities/following/mod.rs b/crates/apub/src/activities/following/mod.rs index e472dc73..5b9d98b4 100644 --- a/crates/apub/src/activities/following/mod.rs +++ b/crates/apub/src/activities/following/mod.rs @@ -3,6 +3,7 @@ use crate::{ protocol::activities::following::{follow::Follow, undo_follow::UndoFollow}, SendActivity, }; +use activitypub_federation::config::Data; use lemmy_api_common::{ community::{CommunityResponse, FollowCommunity}, context::LemmyContext, @@ -15,14 +16,14 @@ pub mod accept; pub mod follow; pub mod undo_follow; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for FollowCommunity { type Response = CommunityResponse; async fn send_activity( request: &Self, _response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let local_user_view = get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?; diff --git a/crates/apub/src/activities/following/undo_follow.rs b/crates/apub/src/activities/following/undo_follow.rs index 436d8a02..9f1b89dc 100644 --- a/crates/apub/src/activities/following/undo_follow.rs +++ b/crates/apub/src/activities/following/undo_follow.rs @@ -1,18 +1,16 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_person}, fetcher::user_or_community::UserOrCommunity, - local_instance, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::following::{follow::Follow, undo_follow::UndoFollow}, - ActorType, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, + config::Data, + kinds::activity::UndoType, + protocol::verification::verify_urls_match, traits::{ActivityHandler, Actor}, - utils::verify_urls_match, }; -use activitystreams_kinds::activity::UndoType; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{ @@ -29,11 +27,11 @@ impl UndoFollow { pub async fn send( actor: &ApubPerson, community: &ApubCommunity, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let object = Follow::new(actor, community, context)?; let undo = UndoFollow { - actor: ObjectId::new(actor.actor_id()), + actor: actor.id().into(), object, kind: UndoType::Undo, id: generate_activity_id( @@ -46,7 +44,7 @@ impl UndoFollow { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for UndoFollow { type DataType = LemmyContext; type Error = LemmyError; @@ -60,32 +58,18 @@ impl ActivityHandler for UndoFollow { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { verify_urls_match(self.actor.inner(), self.object.actor.inner())?; - verify_person(&self.actor, context, request_counter).await?; - self.object.verify(context, request_counter).await?; + verify_person(&self.actor, context).await?; + self.object.verify(context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let person = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; - let object = self - .object - .object - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, true, context).await?; + let person = self.actor.dereference(context).await?; + let object = self.object.object.dereference(context).await?; match object { UserOrCommunity::User(u) => { diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 2896959f..4b50c5c8 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -1,22 +1,19 @@ use crate::{ insert_activity, - local_instance, objects::{community::ApubCommunity, person::ApubPerson}, - ActorType, CONTEXT, }; use activitypub_federation::{ - core::{activity_queue::send_activity, object_id::ObjectId}, - deser::context::WithContext, + activity_queue::send_activity, + config::Data, + fetch::object_id::ObjectId, + kinds::public, + protocol::context::WithContext, traits::{ActivityHandler, Actor}, }; -use activitystreams_kinds::public; use anyhow::anyhow; use lemmy_api_common::context::LemmyContext; -use lemmy_db_schema::{ - newtypes::CommunityId, - source::{community::Community, local_site::LocalSite}, -}; +use lemmy_db_schema::{newtypes::CommunityId, source::community::Community}; use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView}; use lemmy_utils::error::LemmyError; use serde::Serialize; @@ -38,12 +35,9 @@ pub mod voting; #[tracing::instrument(skip_all)] async fn verify_person( person_id: &ObjectId, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { - let person = person_id - .dereference(context, local_instance(context).await, request_counter) - .await?; + let person = person_id.dereference(context).await?; if person.banned { let err = anyhow!("Person {} is banned", person_id); return Err(LemmyError::from_error_message(err, "banned")); @@ -57,12 +51,9 @@ async fn verify_person( pub(crate) async fn verify_person_in_community( person_id: &ObjectId, community: &ApubCommunity, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { - let person = person_id - .dereference(context, local_instance(context).await, request_counter) - .await?; + let person = person_id.dereference(context).await?; if person.banned { return Err(LemmyError::from_message("Person is banned from site")); } @@ -88,12 +79,9 @@ pub(crate) async fn verify_mod_action( mod_id: &ObjectId, object_id: &Url, community_id: CommunityId, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { - let mod_ = mod_id - .dereference(context, local_instance(context).await, request_counter) - .await?; + let mod_ = mod_id.dereference(context).await?; let is_mod_or_admin = CommunityView::is_mod_or_admin(context.pool(), mod_.id, community_id).await?; @@ -159,39 +147,22 @@ where #[tracing::instrument(skip_all)] async fn send_lemmy_activity( - context: &LemmyContext, + data: &Data, activity: Activity, actor: &ActorT, inbox: Vec, sensitive: bool, ) -> Result<(), LemmyError> where - Activity: ActivityHandler + Serialize, - ActorT: Actor + ActorType, + Activity: ActivityHandler + Serialize + Send + Sync + Clone, + ActorT: Actor, Activity: ActivityHandler, { - let federation_enabled = LocalSite::read(context.pool()) - .await - .map(|l| l.federation_enabled) - .unwrap_or(false); - if !federation_enabled { - return Ok(()); - } - info!("Sending activity {}", activity.id().to_string()); let activity = WithContext::new(activity, CONTEXT.deref().clone()); - let object_value = serde_json::to_value(&activity)?; - insert_activity(activity.id(), object_value, true, sensitive, context.pool()).await?; - - send_activity( - activity, - actor.get_public_key(), - actor.private_key().expect("actor has private key"), - inbox, - local_instance(context).await, - ) - .await?; + insert_activity(activity.id(), &activity, true, sensitive, data).await?; + send_activity(activity, actor, inbox, data).await?; Ok(()) } diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs index 4471fbd9..5ab31da5 100644 --- a/crates/apub/src/activities/voting/mod.rs +++ b/crates/apub/src/activities/voting/mod.rs @@ -9,7 +9,7 @@ use crate::{ }, SendActivity, }; -use activitypub_federation::core::object_id::ObjectId; +use activitypub_federation::{config::Data, fetch::object_id::ObjectId}; use lemmy_api_common::{ comment::{CommentResponse, CreateCommentLike}, context::LemmyContext, @@ -36,16 +36,16 @@ use lemmy_utils::error::LemmyError; pub mod undo_vote; pub mod vote; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for CreatePostLike { type Response = PostResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { - let object_id = ObjectId::new(response.post_view.post.ap_id.clone()); + let object_id = ObjectId::from(response.post_view.post.ap_id.clone()); let community_id = response.post_view.community.id; send_activity( object_id, @@ -58,16 +58,16 @@ impl SendActivity for CreatePostLike { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl SendActivity for CreateCommentLike { type Response = CommentResponse; async fn send_activity( request: &Self, response: &Self::Response, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { - let object_id = ObjectId::new(response.comment_view.comment.ap_id.clone()); + let object_id = ObjectId::from(response.comment_view.comment.ap_id.clone()); let community_id = response.comment_view.community.id; send_activity( object_id, @@ -85,7 +85,7 @@ async fn send_activity( community_id: CommunityId, score: i16, jwt: &Sensitive, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let community = Community::read(context.pool(), community_id).await?.into(); let local_user_view = get_local_user_view_from_jwt(jwt, context.pool(), context.secret()).await?; @@ -112,7 +112,7 @@ async fn vote_comment( vote_type: &VoteType, actor: ApubPerson, comment: &ApubComment, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let comment_id = comment.id; let like_form = CommentLikeForm { @@ -134,7 +134,7 @@ async fn vote_post( vote_type: &VoteType, actor: ApubPerson, post: &ApubPost, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let post_id = post.id; let like_form = PostLikeForm { @@ -154,7 +154,7 @@ async fn vote_post( async fn undo_vote_comment( actor: ApubPerson, comment: &ApubComment, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let comment_id = comment.id; let person_id = actor.id; @@ -168,7 +168,7 @@ async fn undo_vote_comment( async fn undo_vote_post( actor: ApubPerson, post: &ApubPost, - context: &LemmyContext, + context: &Data, ) -> Result<(), LemmyError> { let post_id = post.id; let person_id = actor.id; diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index 7a419e87..bcb8ee40 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -4,22 +4,20 @@ use crate::{ verify_person_in_community, voting::{undo_vote_comment, undo_vote_post}, }, - local_instance, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{ activities::voting::{undo_vote::UndoVote, vote::Vote}, InCommunity, }, - ActorType, PostOrComment, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, - traits::ActivityHandler, - utils::verify_urls_match, + config::Data, + kinds::activity::UndoType, + protocol::verification::verify_urls_match, + traits::{ActivityHandler, Actor}, }; -use activitystreams_kinds::activity::UndoType; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use url::Url; @@ -29,22 +27,22 @@ impl UndoVote { vote: Vote, actor: &ApubPerson, community: &ApubCommunity, - context: &LemmyContext, + context: &Data, ) -> Result { Ok(UndoVote { - actor: ObjectId::new(actor.actor_id()), + actor: actor.id().into(), object: vote, kind: UndoType::Undo, id: generate_activity_id( UndoType::Undo, &context.settings().get_protocol_and_hostname(), )?, - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }) } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for UndoVote { type DataType = LemmyContext; type Error = LemmyError; @@ -58,33 +56,19 @@ impl ActivityHandler for UndoVote { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let community = self.community(context, request_counter).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + let community = self.community(context).await?; + verify_person_in_community(&self.actor, &community, context).await?; verify_urls_match(self.actor.inner(), self.object.actor.inner())?; - self.object.verify(context, request_counter).await?; + self.object.verify(context).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let actor = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; - let object = self - .object - .object - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, true, context).await?; + let actor = self.actor.dereference(context).await?; + let object = self.object.object.dereference(context).await?; match object { PostOrComment::Post(p) => undo_vote_post(actor, &p, context).await, PostOrComment::Comment(c) => undo_vote_comment(actor, &c, context).await, diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index e435b682..7f36ed47 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -4,16 +4,19 @@ use crate::{ verify_person_in_community, voting::{vote_comment, vote_post}, }, - local_instance, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{ activities::voting::vote::{Vote, VoteType}, InCommunity, }, - ActorType, PostOrComment, }; -use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler}; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + traits::{ActivityHandler, Actor}, +}; use anyhow::anyhow; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::local_site::LocalSite; @@ -26,19 +29,19 @@ impl Vote { actor: &ApubPerson, community: &ApubCommunity, kind: VoteType, - context: &LemmyContext, + context: &Data, ) -> Result { Ok(Vote { - actor: ObjectId::new(actor.actor_id()), + actor: actor.id().into(), object: object_id, kind: kind.clone(), id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?, - audience: Some(ObjectId::new(community.actor_id())), + audience: Some(community.id().into()), }) } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for Vote { type DataType = LemmyContext; type Error = LemmyError; @@ -52,13 +55,9 @@ impl ActivityHandler for Vote { } #[tracing::instrument(skip_all)] - async fn verify( - &self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let community = self.community(context, request_counter).await?; - verify_person_in_community(&self.actor, &community, context, request_counter).await?; + async fn verify(&self, context: &Data) -> Result<(), LemmyError> { + let community = self.community(context).await?; + verify_person_in_community(&self.actor, &community, context).await?; let enable_downvotes = LocalSite::read(context.pool()) .await .map(|l| l.enable_downvotes) @@ -70,19 +69,10 @@ impl ActivityHandler for Vote { } #[tracing::instrument(skip_all)] - async fn receive( - self, - context: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let actor = self - .actor - .dereference(context, local_instance(context).await, request_counter) - .await?; - let object = self - .object - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn receive(self, context: &Data) -> Result<(), LemmyError> { + insert_activity(&self.id, &self, false, true, context).await?; + let actor = self.actor.dereference(context).await?; + let object = self.object.dereference(context).await?; match object { PostOrComment::Post(p) => vote_post(&self.kind, actor, &p, context).await, PostOrComment::Comment(c) => vote_comment(&self.kind, actor, &c, context).await, diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs index 70ae1bb8..70584955 100644 --- a/crates/apub/src/activity_lists.rs +++ b/crates/apub/src/activity_lists.rs @@ -24,7 +24,11 @@ use crate::{ InCommunity, }, }; -use activitypub_federation::{data::Data, deser::context::WithContext, traits::ActivityHandler}; +use activitypub_federation::{ + config::Data, + protocol::context::WithContext, + traits::ActivityHandler, +}; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; @@ -104,29 +108,25 @@ pub enum SiteInboxActivities { DeleteUser(DeleteUser), } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for AnnouncableActivities { #[tracing::instrument(skip(self, context))] - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { + async fn community(&self, context: &Data) -> Result { use AnnouncableActivities::*; match self { - CreateOrUpdateComment(a) => a.community(context, request_counter).await, - CreateOrUpdatePost(a) => a.community(context, request_counter).await, - Vote(a) => a.community(context, request_counter).await, - UndoVote(a) => a.community(context, request_counter).await, - Delete(a) => a.community(context, request_counter).await, - UndoDelete(a) => a.community(context, request_counter).await, - UpdateCommunity(a) => a.community(context, request_counter).await, - BlockUser(a) => a.community(context, request_counter).await, - UndoBlockUser(a) => a.community(context, request_counter).await, - CollectionAdd(a) => a.community(context, request_counter).await, - CollectionRemove(a) => a.community(context, request_counter).await, - LockPost(a) => a.community(context, request_counter).await, - UndoLockPost(a) => a.community(context, request_counter).await, + CreateOrUpdateComment(a) => a.community(context).await, + CreateOrUpdatePost(a) => a.community(context).await, + Vote(a) => a.community(context).await, + UndoVote(a) => a.community(context).await, + Delete(a) => a.community(context).await, + UndoDelete(a) => a.community(context).await, + UpdateCommunity(a) => a.community(context).await, + BlockUser(a) => a.community(context).await, + UndoBlockUser(a) => a.community(context).await, + CollectionAdd(a) => a.community(context).await, + CollectionRemove(a) => a.community(context).await, + LockPost(a) => a.community(context).await, + UndoLockPost(a) => a.community(context).await, Page(_) => unimplemented!(), } } diff --git a/crates/apub/src/api/list_comments.rs b/crates/apub/src/api/list_comments.rs index 3d44baf0..48ba0ffe 100644 --- a/crates/apub/src/api/list_comments.rs +++ b/crates/apub/src/api/list_comments.rs @@ -3,7 +3,7 @@ use crate::{ fetcher::resolve_actor_identifier, objects::community::ApubCommunity, }; -use actix_web::web::Data; +use activitypub_federation::config::Data; use lemmy_api_common::{ comment::{GetComments, GetCommentsResponse}, context::LemmyContext, @@ -20,7 +20,7 @@ use lemmy_db_schema::{ use lemmy_db_views::comment_view::CommentQuery; use lemmy_utils::{error::LemmyError, ConnectionId}; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl PerformApub for GetComments { type Response = GetCommentsResponse; @@ -41,10 +41,10 @@ impl PerformApub for GetComments { let listing_type = listing_type_with_site_default(data.type_, &local_site)?; let community_actor_id = if let Some(name) = &data.community_name { - resolve_actor_identifier::(name, context, true) + resolve_actor_identifier::(name, context, &None, true) .await .ok() - .map(|c| c.actor_id) + .map(|c| c.actor_id.clone()) } else { None }; diff --git a/crates/apub/src/api/list_posts.rs b/crates/apub/src/api/list_posts.rs index 2908c4ba..b1cfa14d 100644 --- a/crates/apub/src/api/list_posts.rs +++ b/crates/apub/src/api/list_posts.rs @@ -3,7 +3,7 @@ use crate::{ fetcher::resolve_actor_identifier, objects::community::ApubCommunity, }; -use actix_web::web::Data; +use activitypub_federation::config::Data; use lemmy_api_common::{ context::LemmyContext, post::{GetPosts, GetPostsResponse}, @@ -18,7 +18,7 @@ use lemmy_db_schema::source::{community::Community, local_site::LocalSite}; use lemmy_db_views::post_view::PostQuery; use lemmy_utils::{error::LemmyError, ConnectionId}; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl PerformApub for GetPosts { type Response = GetPostsResponse; @@ -43,10 +43,10 @@ impl PerformApub for GetPosts { let limit = data.limit; let community_id = data.community_id; let community_actor_id = if let Some(name) = &data.community_name { - resolve_actor_identifier::(name, context, true) + resolve_actor_identifier::(name, context, &None, true) .await .ok() - .map(|c| c.actor_id) + .map(|c| c.actor_id.clone()) } else { None }; diff --git a/crates/apub/src/api/mod.rs b/crates/apub/src/api/mod.rs index 233f72f0..8906f324 100644 --- a/crates/apub/src/api/mod.rs +++ b/crates/apub/src/api/mod.rs @@ -1,4 +1,4 @@ -use actix_web::web::Data; +use activitypub_federation::config::Data; use lemmy_api_common::context::LemmyContext; use lemmy_utils::{error::LemmyError, ConnectionId}; @@ -9,7 +9,7 @@ mod read_person; mod resolve_object; mod search; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] pub trait PerformApub { type Response: serde::ser::Serialize + Send; diff --git a/crates/apub/src/api/read_community.rs b/crates/apub/src/api/read_community.rs index 738c8a3f..ee62275d 100644 --- a/crates/apub/src/api/read_community.rs +++ b/crates/apub/src/api/read_community.rs @@ -3,7 +3,7 @@ use crate::{ fetcher::resolve_actor_identifier, objects::community::ApubCommunity, }; -use actix_web::web::Data; +use activitypub_federation::config::Data; use lemmy_api_common::{ community::{GetCommunity, GetCommunityResponse}, context::LemmyContext, @@ -21,7 +21,7 @@ use lemmy_db_schema::{ use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_utils::{error::LemmyError, ConnectionId}; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl PerformApub for GetCommunity { type Response = GetCommunityResponse; @@ -49,7 +49,7 @@ impl PerformApub for GetCommunity { Some(id) => id, None => { let name = data.name.clone().unwrap_or_else(|| "main".to_string()); - resolve_actor_identifier::(&name, context, true) + resolve_actor_identifier::(&name, context, &local_user_view, true) .await .map_err(|e| e.with_message("couldnt_find_community"))? .id diff --git a/crates/apub/src/api/read_person.rs b/crates/apub/src/api/read_person.rs index a3a5f943..95d9ecc0 100644 --- a/crates/apub/src/api/read_person.rs +++ b/crates/apub/src/api/read_person.rs @@ -1,5 +1,5 @@ use crate::{api::PerformApub, fetcher::resolve_actor_identifier, objects::person::ApubPerson}; -use actix_web::web::Data; +use activitypub_federation::config::Data; use lemmy_api_common::{ context::LemmyContext, person::{GetPersonDetails, GetPersonDetailsResponse}, @@ -13,7 +13,7 @@ use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery}; use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView}; use lemmy_utils::{error::LemmyError, ConnectionId}; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl PerformApub for GetPersonDetails { type Response = GetPersonDetailsResponse; @@ -42,7 +42,7 @@ impl PerformApub for GetPersonDetails { Some(id) => id, None => { if let Some(username) = &data.username { - resolve_actor_identifier::(username, context, true) + resolve_actor_identifier::(username, context, &local_user_view, true) .await .map_err(|e| e.with_message("couldnt_find_that_username_or_email"))? .id diff --git a/crates/apub/src/api/resolve_object.rs b/crates/apub/src/api/resolve_object.rs index cd5ee47c..f0cb15e3 100644 --- a/crates/apub/src/api/resolve_object.rs +++ b/crates/apub/src/api/resolve_object.rs @@ -2,19 +2,19 @@ use crate::{ api::PerformApub, fetcher::search::{search_query_to_object_id, SearchableObjects}, }; -use actix_web::web::Data; +use activitypub_federation::config::Data; use diesel::NotFound; use lemmy_api_common::{ context::LemmyContext, site::{ResolveObject, ResolveObjectResponse}, - utils::{check_private_instance, get_local_user_view_from_jwt_opt}, + utils::{check_private_instance, get_local_user_view_from_jwt}, }; use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool}; use lemmy_db_views::structs::{CommentView, PostView}; use lemmy_db_views_actor::structs::{CommunityView, PersonView}; use lemmy_utils::{error::LemmyError, ConnectionId}; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl PerformApub for ResolveObject { type Response = ResolveObjectResponse; @@ -25,17 +25,15 @@ impl PerformApub for ResolveObject { _websocket_id: Option, ) -> Result { let local_user_view = - get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret()) - .await?; + get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?; let local_site = LocalSite::read(context.pool()).await?; - check_private_instance(&local_user_view, &local_site)?; + let person_id = local_user_view.person.id; + check_private_instance(&Some(local_user_view), &local_site)?; - // In release builds only allow for authenticated users to fetch remote objects - let local_only = local_user_view.is_none() && cfg!(not(debug_assertions)); - let res = search_query_to_object_id(&self.q, local_only, context) + let res = search_query_to_object_id(&self.q, context) .await .map_err(|e| e.with_message("couldnt_find_object"))?; - convert_response(res, local_user_view.map(|l| l.person.id), context.pool()) + convert_response(res, person_id, context.pool()) .await .map_err(|e| e.with_message("couldnt_find_object")) } @@ -43,7 +41,7 @@ impl PerformApub for ResolveObject { async fn convert_response( object: SearchableObjects, - user_id: Option, + user_id: PersonId, pool: &DbPool, ) -> Result { use SearchableObjects::*; @@ -56,15 +54,15 @@ async fn convert_response( } Community(c) => { removed_or_deleted = c.deleted || c.removed; - res.community = Some(CommunityView::read(pool, c.id, user_id, None).await?) + res.community = Some(CommunityView::read(pool, c.id, Some(user_id), None).await?) } Post(p) => { removed_or_deleted = p.deleted || p.removed; - res.post = Some(PostView::read(pool, p.id, user_id, None).await?) + res.post = Some(PostView::read(pool, p.id, Some(user_id), None).await?) } Comment(c) => { removed_or_deleted = c.deleted || c.removed; - res.comment = Some(CommentView::read(pool, c.id, user_id).await?) + res.comment = Some(CommentView::read(pool, c.id, Some(user_id)).await?) } }; // if the object was deleted from database, dont return it diff --git a/crates/apub/src/api/search.rs b/crates/apub/src/api/search.rs index dee8c802..f02017ad 100644 --- a/crates/apub/src/api/search.rs +++ b/crates/apub/src/api/search.rs @@ -3,7 +3,7 @@ use crate::{ fetcher::resolve_actor_identifier, objects::community::ApubCommunity, }; -use actix_web::web::Data; +use activitypub_federation::config::Data; use lemmy_api_common::{ context::LemmyContext, site::{Search, SearchResponse}, @@ -18,7 +18,7 @@ use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery}; use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery}; use lemmy_utils::{error::LemmyError, ConnectionId}; -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl PerformApub for Search { type Response = SearchResponse; @@ -39,8 +39,6 @@ impl PerformApub for Search { let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok()); - let local_user = local_user_view.map(|l| l.local_user); - let mut posts = Vec::new(); let mut comments = Vec::new(); let mut communities = Vec::new(); @@ -56,14 +54,15 @@ impl PerformApub for Search { let search_type = data.type_.unwrap_or(SearchType::All); let community_id = data.community_id; let community_actor_id = if let Some(name) = &data.community_name { - resolve_actor_identifier::(name, context, false) + resolve_actor_identifier::(name, context, &local_user_view, false) .await .ok() - .map(|c| c.actor_id) + .map(|c| c.actor_id.clone()) } else { None }; let creator_id = data.creator_id; + let local_user = local_user_view.map(|l| l.local_user); match search_type { SearchType::Posts => { posts = PostQuery::builder() diff --git a/crates/apub/src/collections/community_featured.rs b/crates/apub/src/collections/community_featured.rs index 91abd43b..0743a9fb 100644 --- a/crates/apub/src/collections/community_featured.rs +++ b/crates/apub/src/collections/community_featured.rs @@ -1,16 +1,15 @@ use crate::{ - collections::CommunityContext, - objects::post::ApubPost, + objects::{community::ApubCommunity, post::ApubPost}, protocol::collections::group_featured::GroupFeatured, }; use activitypub_federation::{ - data::Data, - traits::{ActivityHandler, ApubObject}, - utils::verify_domains_match, + config::Data, + kinds::collection::OrderedCollectionType, + protocol::verification::verify_domains_match, + traits::{ActivityHandler, Collection, Object}, }; -use activitystreams_kinds::collection::OrderedCollectionType; use futures::future::{join_all, try_join_all}; -use lemmy_api_common::utils::generate_featured_url; +use lemmy_api_common::{context::LemmyContext, utils::generate_featured_url}; use lemmy_db_schema::{source::post::Post, utils::FETCH_LIMIT_MAX}; use lemmy_utils::error::LemmyError; use url::Url; @@ -18,58 +17,46 @@ use url::Url; #[derive(Clone, Debug)] pub(crate) struct ApubCommunityFeatured(Vec); -#[async_trait::async_trait(?Send)] -impl ApubObject for ApubCommunityFeatured { - type DataType = CommunityContext; - type ApubType = GroupFeatured; - type DbType = (); +#[async_trait::async_trait] +impl Collection for ApubCommunityFeatured { + type Owner = ApubCommunity; + type DataType = LemmyContext; + type Kind = GroupFeatured; type Error = LemmyError; - async fn read_from_apub_id( - _object_id: Url, - data: &Self::DataType, - ) -> Result, Self::Error> - where - Self: Sized, - { - // Only read from database if its a local community, otherwise fetch over http - if data.0.local { - let community_id = data.0.id; - let post_list: Vec = Post::list_featured_for_community(data.1.pool(), community_id) + async fn read_local( + owner: &Self::Owner, + data: &Data, + ) -> Result { + let ordered_items = try_join_all( + Post::list_featured_for_community(data.pool(), owner.id) .await? .into_iter() - .map(Into::into) - .collect(); - Ok(Some(ApubCommunityFeatured(post_list))) - } else { - Ok(None) - } - } - - async fn into_apub(self, data: &Self::DataType) -> Result { - let ordered_items = try_join_all(self.0.into_iter().map(|p| p.into_apub(&data.1))).await?; + .map(ApubPost::from) + .map(|p| p.into_json(data)), + ) + .await?; Ok(GroupFeatured { r#type: OrderedCollectionType::OrderedCollection, - id: generate_featured_url(&data.0.actor_id)?.into(), + id: generate_featured_url(&owner.actor_id)?.into(), total_items: ordered_items.len() as i32, ordered_items, }) } async fn verify( - apub: &Self::ApubType, + apub: &Self::Kind, expected_domain: &Url, - _data: &Self::DataType, - _request_counter: &mut i32, + _data: &Data, ) -> Result<(), Self::Error> { verify_domains_match(expected_domain, &apub.id)?; Ok(()) } - async fn from_apub( - apub: Self::ApubType, - data: &Self::DataType, - _request_counter: &mut i32, + async fn from_json( + apub: Self::Kind, + _owner: &Self::Owner, + data: &Data, ) -> Result where Self: Sized, @@ -85,16 +72,14 @@ impl ApubObject for ApubCommunityFeatured { // We intentionally ignore errors here. This is because the outbox might contain posts from old // Lemmy versions, or from other software which we cant parse. In that case, we simply skip the // item and only parse the ones that work. - let data = Data::new(data.1.clone()); // process items in parallel, to avoid long delay from fetch_site_metadata() and other processing join_all(posts.into_iter().map(|post| { async { // use separate request counter for each item, otherwise there will be problems with // parallel processing - let request_counter = &mut 0; - let verify = post.verify(&data, request_counter).await; + let verify = post.verify(data).await; if verify.is_ok() { - post.receive(&data, request_counter).await.ok(); + post.receive(data).await.ok(); } } })) diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index 5c05258d..c439da71 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -1,17 +1,15 @@ use crate::{ - collections::CommunityContext, - local_instance, - objects::person::ApubPerson, + objects::{community::ApubCommunity, person::ApubPerson}, protocol::collections::group_moderators::GroupModerators, }; use activitypub_federation::{ - core::object_id::ObjectId, - traits::ApubObject, - utils::verify_domains_match, + config::Data, + fetch::object_id::ObjectId, + kinds::collection::OrderedCollectionType, + protocol::verification::verify_domains_match, + traits::Collection, }; -use activitystreams_kinds::collection::OrderedCollectionType; -use chrono::NaiveDateTime; -use lemmy_api_common::utils::generate_moderators_url; +use lemmy_api_common::{context::LemmyContext, utils::generate_moderators_url}; use lemmy_db_schema::{ source::community::{CommunityModerator, CommunityModeratorForm}, traits::Joinable, @@ -23,46 +21,26 @@ use url::Url; #[derive(Clone, Debug)] pub(crate) struct ApubCommunityModerators(pub(crate) Vec); -#[async_trait::async_trait(?Send)] -impl ApubObject for ApubCommunityModerators { - type DataType = CommunityContext; - type ApubType = GroupModerators; +#[async_trait::async_trait] +impl Collection for ApubCommunityModerators { + type Owner = ApubCommunity; + type DataType = LemmyContext; + type Kind = GroupModerators; type Error = LemmyError; - fn last_refreshed_at(&self) -> Option { - None - } - - #[tracing::instrument(skip_all)] - async fn read_from_apub_id( - _object_id: Url, - data: &Self::DataType, - ) -> Result, LemmyError> { - // Only read from database if its a local community, otherwise fetch over http - if data.0.local { - let cid = data.0.id; - let moderators = CommunityModeratorView::for_community(data.1.pool(), cid).await?; - Ok(Some(ApubCommunityModerators(moderators))) - } else { - Ok(None) - } - } - #[tracing::instrument(skip_all)] - async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> { - unimplemented!() - } - - #[tracing::instrument(skip_all)] - async fn into_apub(self, data: &Self::DataType) -> Result { - let ordered_items = self - .0 + async fn read_local( + owner: &Self::Owner, + data: &Data, + ) -> Result { + let moderators = CommunityModeratorView::for_community(data.pool(), owner.id).await?; + let ordered_items = moderators .into_iter() - .map(|m| ObjectId::::new(m.moderator.actor_id)) + .map(|m| ObjectId::::from(m.moderator.actor_id)) .collect(); Ok(GroupModerators { r#type: OrderedCollectionType::OrderedCollection, - id: generate_moderators_url(&data.0.actor_id)?.into(), + id: generate_moderators_url(&owner.actor_id)?.into(), ordered_items, }) } @@ -71,40 +49,36 @@ impl ApubObject for ApubCommunityModerators { async fn verify( group_moderators: &GroupModerators, expected_domain: &Url, - _context: &CommunityContext, - _request_counter: &mut i32, + _data: &Data, ) -> Result<(), LemmyError> { verify_domains_match(&group_moderators.id, expected_domain)?; Ok(()) } #[tracing::instrument(skip_all)] - async fn from_apub( - apub: Self::ApubType, - data: &Self::DataType, - request_counter: &mut i32, + async fn from_json( + apub: Self::Kind, + owner: &Self::Owner, + data: &Data, ) -> Result { - let community_id = data.0.id; + let community_id = owner.id; let current_moderators = - CommunityModeratorView::for_community(data.1.pool(), community_id).await?; + CommunityModeratorView::for_community(data.pool(), community_id).await?; // Remove old mods from database which arent in the moderators collection anymore for mod_user in ¤t_moderators { - let mod_id = ObjectId::new(mod_user.moderator.actor_id.clone()); + let mod_id = ObjectId::from(mod_user.moderator.actor_id.clone()); if !apub.ordered_items.contains(&mod_id) { let community_moderator_form = CommunityModeratorForm { community_id: mod_user.community.id, person_id: mod_user.moderator.id, }; - CommunityModerator::leave(data.1.pool(), &community_moderator_form).await?; + CommunityModerator::leave(data.pool(), &community_moderator_form).await?; } } // Add new mods to database which have been added to moderators collection for mod_id in apub.ordered_items { - let mod_id = ObjectId::new(mod_id); - let mod_user: ApubPerson = mod_id - .dereference(&data.1, local_instance(&data.1).await, request_counter) - .await?; + let mod_user: ApubPerson = mod_id.dereference(data).await?; if !current_moderators .iter() @@ -112,18 +86,16 @@ impl ApubObject for ApubCommunityModerators { .any(|x| x == mod_user.actor_id) { let community_moderator_form = CommunityModeratorForm { - community_id: data.0.id, + community_id: owner.id, person_id: mod_user.id, }; - CommunityModerator::join(data.1.pool(), &community_moderator_form).await?; + CommunityModerator::join(data.pool(), &community_moderator_form).await?; } } // This return value is unused, so just set an empty vec Ok(ApubCommunityModerators(Vec::new())) } - - type DbType = (); } #[cfg(test)] @@ -181,15 +153,13 @@ mod tests { let json: GroupModerators = file_to_json_object("assets/lemmy/collections/group_moderators.json").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap(); - let mut request_counter = 0; - let community_context = CommunityContext(community, context.clone()); - ApubCommunityModerators::verify(&json, &url, &community_context, &mut request_counter) + ApubCommunityModerators::verify(&json, &url, &context) .await .unwrap(); - ApubCommunityModerators::from_apub(json, &community_context, &mut request_counter) + ApubCommunityModerators::from_json(json, &community, &context) .await .unwrap(); - assert_eq!(request_counter, 0); + assert_eq!(context.request_count(), 0); let current_moderators = CommunityModeratorView::for_community(context.pool(), community_id) .await @@ -200,7 +170,7 @@ mod tests { Person::delete(context.pool(), old_mod.id).await.unwrap(); Person::delete(context.pool(), new_mod.id).await.unwrap(); - Community::delete(context.pool(), community_context.0.id) + Community::delete(context.pool(), community.id) .await .unwrap(); Site::delete(context.pool(), site.id).await.unwrap(); diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index c6d8bb44..c95b64aa 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -1,7 +1,6 @@ use crate::{ activity_lists::AnnouncableActivities, - collections::CommunityContext, - objects::post::ApubPost, + objects::{community::ApubCommunity, post::ApubPost}, protocol::{ activities::{ community::announce::AnnounceActivity, @@ -12,14 +11,13 @@ use crate::{ }, }; use activitypub_federation::{ - data::Data, - traits::{ActivityHandler, ApubObject}, - utils::verify_domains_match, + config::Data, + kinds::collection::OrderedCollectionType, + protocol::verification::verify_domains_match, + traits::{ActivityHandler, Collection}, }; -use activitystreams_kinds::collection::OrderedCollectionType; -use chrono::NaiveDateTime; use futures::future::join_all; -use lemmy_api_common::utils::generate_outbox_url; +use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url}; use lemmy_db_schema::{ source::{person::Person, post::Post}, traits::Crud, @@ -31,52 +29,36 @@ use url::Url; #[derive(Clone, Debug)] pub(crate) struct ApubCommunityOutbox(Vec); -#[async_trait::async_trait(?Send)] -impl ApubObject for ApubCommunityOutbox { - type DataType = CommunityContext; - type ApubType = GroupOutbox; +#[async_trait::async_trait] +impl Collection for ApubCommunityOutbox { + type Owner = ApubCommunity; + type DataType = LemmyContext; + type Kind = GroupOutbox; type Error = LemmyError; - type DbType = (); - - fn last_refreshed_at(&self) -> Option { - None - } - - #[tracing::instrument(skip_all)] - async fn read_from_apub_id( - _object_id: Url, - data: &Self::DataType, - ) -> Result, LemmyError> { - // Only read from database if its a local community, otherwise fetch over http - if data.0.local { - let community_id = data.0.id; - let post_list: Vec = Post::list_for_community(data.1.pool(), community_id) - .await? - .into_iter() - .map(Into::into) - .collect(); - Ok(Some(ApubCommunityOutbox(post_list))) - } else { - Ok(None) - } - } #[tracing::instrument(skip_all)] - async fn into_apub(self, data: &Self::DataType) -> Result { + async fn read_local( + owner: &Self::Owner, + data: &Data, + ) -> Result { + let post_list: Vec = Post::list_for_community(data.pool(), owner.id) + .await? + .into_iter() + .map(Into::into) + .collect(); let mut ordered_items = vec![]; - for post in self.0 { - let person = Person::read(data.1.pool(), post.creator_id).await?.into(); + for post in post_list { + let person = Person::read(data.pool(), post.creator_id).await?.into(); let create = - CreateOrUpdatePage::new(post, &person, &data.0, CreateOrUpdateType::Create, &data.1) - .await?; + CreateOrUpdatePage::new(post, &person, owner, CreateOrUpdateType::Create, data).await?; let announcable = AnnouncableActivities::CreateOrUpdatePost(create); - let announce = AnnounceActivity::new(announcable.try_into()?, &data.0, &data.1)?; + let announce = AnnounceActivity::new(announcable.try_into()?, owner, data)?; ordered_items.push(announce); } Ok(GroupOutbox { r#type: OrderedCollectionType::OrderedCollection, - id: generate_outbox_url(&data.0.actor_id)?.into(), + id: generate_outbox_url(&owner.actor_id)?.into(), total_items: ordered_items.len() as i32, ordered_items, }) @@ -86,18 +68,17 @@ impl ApubObject for ApubCommunityOutbox { async fn verify( group_outbox: &GroupOutbox, expected_domain: &Url, - _context: &CommunityContext, - _request_counter: &mut i32, + _data: &Data, ) -> Result<(), LemmyError> { verify_domains_match(expected_domain, &group_outbox.id)?; Ok(()) } #[tracing::instrument(skip_all)] - async fn from_apub( - apub: Self::ApubType, - data: &Self::DataType, - _request_counter: &mut i32, + async fn from_json( + apub: Self::Kind, + _owner: &Self::Owner, + data: &Data, ) -> Result { let mut outbox_activities = apub.ordered_items; if outbox_activities.len() as i64 > FETCH_LIMIT_MAX { @@ -110,16 +91,14 @@ impl ApubObject for ApubCommunityOutbox { // We intentionally ignore errors here. This is because the outbox might contain posts from old // Lemmy versions, or from other software which we cant parse. In that case, we simply skip the // item and only parse the ones that work. - let data = Data::new(data.1.clone()); // process items in parallel, to avoid long delay from fetch_site_metadata() and other processing join_all(outbox_activities.into_iter().map(|activity| { async { // use separate request counter for each item, otherwise there will be problems with // parallel processing - let request_counter = &mut 0; - let verify = activity.verify(&data, request_counter).await; + let verify = activity.verify(data).await; if verify.is_ok() { - activity.receive(&data, request_counter).await.ok(); + activity.receive(data).await.ok(); } } })) diff --git a/crates/apub/src/collections/mod.rs b/crates/apub/src/collections/mod.rs index a8d5e136..32922bef 100644 --- a/crates/apub/src/collections/mod.rs +++ b/crates/apub/src/collections/mod.rs @@ -1,9 +1,3 @@ -use crate::objects::community::ApubCommunity; -use lemmy_api_common::context::LemmyContext; - pub(crate) mod community_featured; pub(crate) mod community_moderators; pub(crate) mod community_outbox; - -/// Put community in the data, so we dont have to read it again from the database. -pub(crate) struct CommunityContext(pub ApubCommunity, pub LemmyContext); diff --git a/crates/apub/src/fetcher/mod.rs b/crates/apub/src/fetcher/mod.rs index 0ce4dd8f..de244178 100644 --- a/crates/apub/src/fetcher/mod.rs +++ b/crates/apub/src/fetcher/mod.rs @@ -1,31 +1,38 @@ -use crate::{fetcher::webfinger::webfinger_resolve_actor, ActorType}; -use activitypub_federation::traits::ApubObject; +use activitypub_federation::{ + config::Data, + fetch::webfinger::webfinger_resolve_actor, + traits::{Actor, Object}, +}; +use diesel::NotFound; use itertools::Itertools; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::traits::ApubActor; +use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::LemmyError; pub mod post_or_comment; pub mod search; pub mod user_or_community; -pub mod webfinger; -/// Resolve actor identifier (eg `!news@example.com`) from local database to avoid network requests. -/// This only works for local actors, and remote actors which were previously fetched (so it doesnt -/// trigger any new fetch). +/// Resolve actor identifier like `!news@example.com` to user or community object. +/// +/// In case the requesting user is logged in and the object was not found locally, it is attempted +/// to fetch via webfinger from the original instance. #[tracing::instrument(skip_all)] -pub async fn resolve_actor_identifier( +pub async fn resolve_actor_identifier( identifier: &str, - context: &LemmyContext, + context: &Data, + local_user_view: &Option, include_deleted: bool, -) -> Result +) -> Result where - Actor: ApubObject - + ApubObject - + ActorType + ActorType: Object + + Object + + Actor + + From + Send + 'static, - for<'de2> ::ApubType: serde::Deserialize<'de2>, + for<'de2> ::Kind: serde::Deserialize<'de2>, DbActor: ApubActor + Send + 'static, { // remote actor @@ -38,19 +45,22 @@ where let domain = format!("{}://{}", context.settings().get_protocol_string(), domain); let actor = DbActor::read_from_name_and_domain(context.pool(), &name, &domain).await; if actor.is_ok() { - Ok(actor?) - } else { + Ok(actor?.into()) + } else if local_user_view.is_some() { // Fetch the actor from its home instance using webfinger - let id = webfinger_resolve_actor::(identifier, true, context, &mut 0).await?; - let actor: DbActor = DbActor::read_from_apub_id(context.pool(), &id) - .await? - .expect("actor exists as we fetched just before"); + let actor: ActorType = webfinger_resolve_actor(identifier, context).await?; Ok(actor) + } else { + Err(NotFound.into()) } } // local actor else { let identifier = identifier.to_string(); - Ok(DbActor::read_from_name(context.pool(), &identifier, include_deleted).await?) + Ok( + DbActor::read_from_name(context.pool(), &identifier, include_deleted) + .await? + .into(), + ) } } diff --git a/crates/apub/src/fetcher/post_or_comment.rs b/crates/apub/src/fetcher/post_or_comment.rs index c0a42301..651abf7d 100644 --- a/crates/apub/src/fetcher/post_or_comment.rs +++ b/crates/apub/src/fetcher/post_or_comment.rs @@ -5,7 +5,7 @@ use crate::{ InCommunity, }, }; -use activitypub_federation::traits::ApubObject; +use activitypub_federation::{config::Data, traits::Object}; use chrono::NaiveDateTime; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ @@ -29,11 +29,10 @@ pub enum PageOrNote { Note(Note), } -#[async_trait::async_trait(?Send)] -impl ApubObject for PostOrComment { +#[async_trait::async_trait] +impl Object for PostOrComment { type DataType = LemmyContext; - type ApubType = PageOrNote; - type DbType = (); + type Kind = PageOrNote; type Error = LemmyError; fn last_refreshed_at(&self) -> Option { @@ -41,68 +40,55 @@ impl ApubObject for PostOrComment { } #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - data: &Self::DataType, + data: &Data, ) -> Result, LemmyError> { - let post = ApubPost::read_from_apub_id(object_id.clone(), data).await?; + let post = ApubPost::read_from_id(object_id.clone(), data).await?; Ok(match post { Some(o) => Some(PostOrComment::Post(o)), - None => ApubComment::read_from_apub_id(object_id, data) + None => ApubComment::read_from_id(object_id, data) .await? .map(PostOrComment::Comment), }) } #[tracing::instrument(skip_all)] - async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> { + async fn delete(self, data: &Data) -> Result<(), LemmyError> { match self { PostOrComment::Post(p) => p.delete(data).await, PostOrComment::Comment(c) => c.delete(data).await, } } - async fn into_apub(self, _data: &Self::DataType) -> Result { + async fn into_json(self, _data: &Data) -> Result { unimplemented!() } #[tracing::instrument(skip_all)] async fn verify( - apub: &Self::ApubType, + apub: &Self::Kind, expected_domain: &Url, - data: &Self::DataType, - request_counter: &mut i32, + data: &Data, ) -> Result<(), LemmyError> { match apub { - PageOrNote::Page(a) => ApubPost::verify(a, expected_domain, data, request_counter).await, - PageOrNote::Note(a) => ApubComment::verify(a, expected_domain, data, request_counter).await, + PageOrNote::Page(a) => ApubPost::verify(a, expected_domain, data).await, + PageOrNote::Note(a) => ApubComment::verify(a, expected_domain, data).await, } } #[tracing::instrument(skip_all)] - async fn from_apub( - apub: PageOrNote, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { + async fn from_json(apub: PageOrNote, context: &Data) -> Result { Ok(match apub { - PageOrNote::Page(p) => { - PostOrComment::Post(ApubPost::from_apub(*p, context, request_counter).await?) - } - PageOrNote::Note(n) => { - PostOrComment::Comment(ApubComment::from_apub(n, context, request_counter).await?) - } + PageOrNote::Page(p) => PostOrComment::Post(ApubPost::from_json(*p, context).await?), + PageOrNote::Note(n) => PostOrComment::Comment(ApubComment::from_json(n, context).await?), }) } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for PostOrComment { - async fn community( - &self, - context: &LemmyContext, - _: &mut i32, - ) -> Result { + async fn community(&self, context: &Data) -> Result { let cid = match self { PostOrComment::Post(p) => p.community_id, PostOrComment::Comment(c) => Post::read(context.pool(), c.post_id).await?.community_id, diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index ffc2c961..41bcce37 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -1,10 +1,12 @@ use crate::{ - fetcher::webfinger::webfinger_resolve_actor, - local_instance, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::objects::{group::Group, note::Note, page::Page, person::Person}, }; -use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject}; +use activitypub_federation::{ + config::Data, + fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor}, + traits::Object, +}; use chrono::NaiveDateTime; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; @@ -17,39 +19,29 @@ use url::Url; #[tracing::instrument(skip_all)] pub(crate) async fn search_query_to_object_id( query: &str, - local_only: bool, - context: &LemmyContext, + context: &Data, ) -> Result { - let request_counter = &mut 0; - let object_id = match Url::parse(query) { - // its already an url, just go with it - Ok(url) => ObjectId::new(url), + Ok(match Url::parse(query) { + Ok(url) => { + // its already an url, just go with it + ObjectId::from(url).dereference(context).await? + } Err(_) => { // not an url, try to resolve via webfinger let mut chars = query.chars(); let kind = chars.next(); let identifier = chars.as_str(); - let id = match kind { - Some('@') => { - webfinger_resolve_actor::(identifier, local_only, context, request_counter) - .await? - } - Some('!') => { - webfinger_resolve_actor::(identifier, local_only, context, request_counter) - .await? - } + match kind { + Some('@') => SearchableObjects::Person( + webfinger_resolve_actor::(identifier, context).await?, + ), + Some('!') => SearchableObjects::Community( + webfinger_resolve_actor::(identifier, context).await?, + ), _ => return Err(LemmyError::from_message("invalid query")), - }; - ObjectId::new(id) + } } - }; - if local_only { - object_id.dereference_local(context).await - } else { - object_id - .dereference(context, local_instance(context).await, request_counter) - .await - } + }) } /// The types of ActivityPub objects that can be fetched directly by searching for their ID. @@ -63,18 +55,17 @@ pub(crate) enum SearchableObjects { #[derive(Deserialize)] #[serde(untagged)] -pub(crate) enum SearchableApubTypes { +pub(crate) enum SearchableKinds { Group(Group), Person(Person), Page(Page), Note(Note), } -#[async_trait::async_trait(?Send)] -impl ApubObject for SearchableObjects { +#[async_trait::async_trait] +impl Object for SearchableObjects { type DataType = LemmyContext; - type ApubType = SearchableApubTypes; - type DbType = (); + type Kind = SearchableKinds; type Error = LemmyError; fn last_refreshed_at(&self) -> Option { @@ -92,23 +83,23 @@ impl ApubObject for SearchableObjects { // we could skip this and always return an error, but then it would always fetch objects // over http, and not be able to mark objects as deleted that were deleted by remote server. #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - context: &LemmyContext, + context: &Data, ) -> Result, LemmyError> { - let c = ApubCommunity::read_from_apub_id(object_id.clone(), context).await?; + let c = ApubCommunity::read_from_id(object_id.clone(), context).await?; if let Some(c) = c { return Ok(Some(SearchableObjects::Community(c))); } - let p = ApubPerson::read_from_apub_id(object_id.clone(), context).await?; + let p = ApubPerson::read_from_id(object_id.clone(), context).await?; if let Some(p) = p { return Ok(Some(SearchableObjects::Person(p))); } - let p = ApubPost::read_from_apub_id(object_id.clone(), context).await?; + let p = ApubPost::read_from_id(object_id.clone(), context).await?; if let Some(p) = p { return Ok(Some(SearchableObjects::Post(p))); } - let c = ApubComment::read_from_apub_id(object_id, context).await?; + let c = ApubComment::read_from_id(object_id, context).await?; if let Some(c) = c { return Ok(Some(SearchableObjects::Comment(c))); } @@ -116,7 +107,7 @@ impl ApubObject for SearchableObjects { } #[tracing::instrument(skip_all)] - async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> { + async fn delete(self, data: &Data) -> Result<(), LemmyError> { match self { SearchableObjects::Person(p) => p.delete(data).await, SearchableObjects::Community(c) => c.delete(data).await, @@ -125,46 +116,33 @@ impl ApubObject for SearchableObjects { } } - async fn into_apub(self, _data: &Self::DataType) -> Result { + async fn into_json(self, _data: &Data) -> Result { unimplemented!() } #[tracing::instrument(skip_all)] async fn verify( - apub: &Self::ApubType, + apub: &Self::Kind, expected_domain: &Url, - data: &Self::DataType, - request_counter: &mut i32, + data: &Data, ) -> Result<(), LemmyError> { match apub { - SearchableApubTypes::Group(a) => { - ApubCommunity::verify(a, expected_domain, data, request_counter).await - } - SearchableApubTypes::Person(a) => { - ApubPerson::verify(a, expected_domain, data, request_counter).await - } - SearchableApubTypes::Page(a) => { - ApubPost::verify(a, expected_domain, data, request_counter).await - } - SearchableApubTypes::Note(a) => { - ApubComment::verify(a, expected_domain, data, request_counter).await - } + SearchableKinds::Group(a) => ApubCommunity::verify(a, expected_domain, data).await, + SearchableKinds::Person(a) => ApubPerson::verify(a, expected_domain, data).await, + SearchableKinds::Page(a) => ApubPost::verify(a, expected_domain, data).await, + SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await, } } #[tracing::instrument(skip_all)] - async fn from_apub( - apub: Self::ApubType, - context: &LemmyContext, - rc: &mut i32, - ) -> Result { - use SearchableApubTypes as SAT; + async fn from_json(apub: Self::Kind, context: &Data) -> Result { + use SearchableKinds as SAT; use SearchableObjects as SO; Ok(match apub { - SAT::Group(g) => SO::Community(ApubCommunity::from_apub(g, context, rc).await?), - SAT::Person(p) => SO::Person(ApubPerson::from_apub(p, context, rc).await?), - SAT::Page(p) => SO::Post(ApubPost::from_apub(p, context, rc).await?), - SAT::Note(n) => SO::Comment(ApubComment::from_apub(n, context, rc).await?), + SAT::Group(g) => SO::Community(ApubCommunity::from_json(g, context).await?), + SAT::Person(p) => SO::Person(ApubPerson::from_json(p, context).await?), + SAT::Page(p) => SO::Post(ApubPost::from_json(p, context).await?), + SAT::Note(n) => SO::Comment(ApubComment::from_json(n, context).await?), }) } } diff --git a/crates/apub/src/fetcher/user_or_community.rs b/crates/apub/src/fetcher/user_or_community.rs index 3af86be1..d872c4e2 100644 --- a/crates/apub/src/fetcher/user_or_community.rs +++ b/crates/apub/src/fetcher/user_or_community.rs @@ -1,9 +1,11 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::objects::{group::Group, person::Person}, - ActorType, }; -use activitypub_federation::traits::{Actor, ApubObject}; +use activitypub_federation::{ + config::Data, + traits::{Actor, Object}, +}; use chrono::NaiveDateTime; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; @@ -29,11 +31,10 @@ pub enum PersonOrGroupType { Group, } -#[async_trait::async_trait(?Send)] -impl ApubObject for UserOrCommunity { +#[async_trait::async_trait] +impl Object for UserOrCommunity { type DataType = LemmyContext; - type ApubType = PersonOrGroup; - type DbType = (); + type Kind = PersonOrGroup; type Error = LemmyError; fn last_refreshed_at(&self) -> Option { @@ -44,90 +45,77 @@ impl ApubObject for UserOrCommunity { } #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - data: &Self::DataType, + data: &Data, ) -> Result, LemmyError> { - let person = ApubPerson::read_from_apub_id(object_id.clone(), data).await?; + let person = ApubPerson::read_from_id(object_id.clone(), data).await?; Ok(match person { Some(o) => Some(UserOrCommunity::User(o)), - None => ApubCommunity::read_from_apub_id(object_id, data) + None => ApubCommunity::read_from_id(object_id, data) .await? .map(UserOrCommunity::Community), }) } #[tracing::instrument(skip_all)] - async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> { + async fn delete(self, data: &Data) -> Result<(), LemmyError> { match self { UserOrCommunity::User(p) => p.delete(data).await, UserOrCommunity::Community(p) => p.delete(data).await, } } - async fn into_apub(self, _data: &Self::DataType) -> Result { + async fn into_json(self, _data: &Data) -> Result { unimplemented!() } #[tracing::instrument(skip_all)] async fn verify( - apub: &Self::ApubType, + apub: &Self::Kind, expected_domain: &Url, - data: &Self::DataType, - request_counter: &mut i32, + data: &Data, ) -> Result<(), LemmyError> { match apub { - PersonOrGroup::Person(a) => { - ApubPerson::verify(a, expected_domain, data, request_counter).await - } - PersonOrGroup::Group(a) => { - ApubCommunity::verify(a, expected_domain, data, request_counter).await - } + PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await, + PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await, } } #[tracing::instrument(skip_all)] - async fn from_apub( - apub: Self::ApubType, - data: &Self::DataType, - request_counter: &mut i32, - ) -> Result { + async fn from_json(apub: Self::Kind, data: &Data) -> Result { Ok(match apub { - PersonOrGroup::Person(p) => { - UserOrCommunity::User(ApubPerson::from_apub(p, data, request_counter).await?) - } + PersonOrGroup::Person(p) => UserOrCommunity::User(ApubPerson::from_json(p, data).await?), PersonOrGroup::Group(p) => { - UserOrCommunity::Community(ApubCommunity::from_apub(p, data, request_counter).await?) + UserOrCommunity::Community(ApubCommunity::from_json(p, data).await?) } }) } } impl Actor for UserOrCommunity { - fn public_key(&self) -> &str { + fn id(&self) -> Url { match self { - UserOrCommunity::User(p) => p.public_key(), - UserOrCommunity::Community(p) => p.public_key(), + UserOrCommunity::User(u) => u.id(), + UserOrCommunity::Community(c) => c.id(), } } - fn inbox(&self) -> Url { - unimplemented!() - } -} - -impl ActorType for UserOrCommunity { - fn actor_id(&self) -> Url { + fn public_key_pem(&self) -> &str { match self { - UserOrCommunity::User(u) => u.actor_id(), - UserOrCommunity::Community(c) => c.actor_id(), + UserOrCommunity::User(p) => p.public_key_pem(), + UserOrCommunity::Community(p) => p.public_key_pem(), } } - fn private_key(&self) -> Option { + fn private_key_pem(&self) -> Option { match self { - UserOrCommunity::User(u) => u.private_key(), - UserOrCommunity::Community(c) => c.private_key(), + UserOrCommunity::User(p) => p.private_key_pem(), + UserOrCommunity::Community(p) => p.private_key_pem(), } } + + fn inbox(&self) -> Url { + unimplemented!() + } } diff --git a/crates/apub/src/fetcher/webfinger.rs b/crates/apub/src/fetcher/webfinger.rs deleted file mode 100644 index 968a0d70..00000000 --- a/crates/apub/src/fetcher/webfinger.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::{local_instance, ActorType, FEDERATION_HTTP_FETCH_LIMIT}; -use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject}; -use anyhow::anyhow; -use itertools::Itertools; -use lemmy_api_common::context::LemmyContext; -use lemmy_db_schema::newtypes::DbUrl; -use lemmy_utils::{error::LemmyError, WebfingerResponse}; -use tracing::debug; -use url::Url; - -/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`, -/// using webfinger. -pub(crate) async fn webfinger_resolve_actor( - identifier: &str, - local_only: bool, - context: &LemmyContext, - request_counter: &mut i32, -) -> Result -where - Kind: ApubObject + ActorType + Send + 'static, - for<'de2> ::ApubType: serde::Deserialize<'de2>, -{ - let protocol = context.settings().get_protocol_string(); - let (_, domain) = identifier - .splitn(2, '@') - .collect_tuple() - .ok_or_else(|| LemmyError::from_message("Invalid webfinger query, missing domain"))?; - let fetch_url = format!("{protocol}://{domain}/.well-known/webfinger?resource=acct:{identifier}"); - debug!("Fetching webfinger url: {}", &fetch_url); - - *request_counter += 1; - if *request_counter > FEDERATION_HTTP_FETCH_LIMIT { - return Err(LemmyError::from_message("Request retry limit reached")); - } - - let response = context.client().get(&fetch_url).send().await?; - - let res: WebfingerResponse = response.json().await.map_err(LemmyError::from)?; - - let links: Vec = res - .links - .iter() - .filter(|link| { - if let Some(type_) = &link.kind { - type_.starts_with("application/") - } else { - false - } - }) - .filter_map(|l| l.href.clone()) - .collect(); - for l in links { - let object_id = ObjectId::::new(l); - let object = if local_only { - object_id.dereference_local(context).await - } else { - object_id - .dereference(context, local_instance(context).await, request_counter) - .await - }; - if object.is_ok() { - return object.map(|o| o.actor_id().into()); - } - } - let err = anyhow!("Failed to resolve actor for {}", identifier); - Err(LemmyError::from_error_message(err, "failed_to_resolve")) -} diff --git a/crates/apub/src/http/comment.rs b/crates/apub/src/http/comment.rs index 6753271c..c02bd054 100644 --- a/crates/apub/src/http/comment.rs +++ b/crates/apub/src/http/comment.rs @@ -2,8 +2,8 @@ use crate::{ http::{create_apub_response, create_apub_tombstone_response, err_object_not_local}, objects::comment::ApubComment, }; -use activitypub_federation::traits::ApubObject; -use actix_web::{web, web::Path, HttpResponse}; +use activitypub_federation::{config::Data, traits::Object}; +use actix_web::{web::Path, HttpResponse}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{newtypes::CommentId, source::comment::Comment, traits::Crud}; use lemmy_utils::error::LemmyError; @@ -18,7 +18,7 @@ pub(crate) struct CommentQuery { #[tracing::instrument(skip_all)] pub(crate) async fn get_apub_comment( info: Path, - context: web::Data, + context: Data, ) -> Result { let id = CommentId(info.comment_id.parse::()?); let comment: ApubComment = Comment::read(context.pool(), id).await?.into(); @@ -27,7 +27,7 @@ pub(crate) async fn get_apub_comment( } if !comment.deleted && !comment.removed { - Ok(create_apub_response(&comment.into_apub(&context).await?)) + Ok(create_apub_response(&comment.into_json(&context).await?)) } else { Ok(create_apub_tombstone_response(comment.ap_id.clone())) } diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index 9a0d1f02..4ae50bb0 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -4,23 +4,19 @@ use crate::{ community_featured::ApubCommunityFeatured, community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, - CommunityContext, }, - http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity}, - local_instance, + http::{create_apub_response, create_apub_tombstone_response}, objects::{community::ApubCommunity, person::ApubPerson}, protocol::collections::group_followers::GroupFollowers, }; use activitypub_federation::{ - core::object_id::ObjectId, - deser::context::WithContext, - traits::ApubObject, -}; -use actix_web::{web, HttpRequest, HttpResponse}; -use lemmy_api_common::{ - context::LemmyContext, - utils::{generate_featured_url, generate_outbox_url}, + actix_web::inbox::receive_activity, + config::Data, + protocol::context::WithContext, + traits::{Collection, Object}, }; +use actix_web::{web, web::Bytes, HttpRequest, HttpResponse}; +use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{source::community::Community, traits::ApubActor}; use lemmy_utils::error::LemmyError; use serde::Deserialize; @@ -34,7 +30,7 @@ pub(crate) struct CommunityQuery { #[tracing::instrument(skip_all)] pub(crate) async fn get_apub_community_http( info: web::Path, - context: web::Data, + context: Data, ) -> Result { let community: ApubCommunity = Community::read_from_name(context.pool(), &info.community_name, true) @@ -42,7 +38,7 @@ pub(crate) async fn get_apub_community_http( .into(); if !community.deleted && !community.removed { - let apub = community.into_apub(&context).await?; + let apub = community.into_json(&context).await?; Ok(create_apub_response(&apub)) } else { @@ -54,17 +50,19 @@ pub(crate) async fn get_apub_community_http( #[tracing::instrument(skip_all)] pub async fn community_inbox( request: HttpRequest, - payload: String, - context: web::Data, + body: Bytes, + data: Data, ) -> Result { - receive_lemmy_activity::, ApubPerson>(request, payload, context) - .await + receive_activity::, ApubPerson, LemmyContext>( + request, body, &data, + ) + .await } /// Returns an empty followers collection, only populating the size (for privacy). pub(crate) async fn get_apub_community_followers( info: web::Path, - context: web::Data, + context: Data, ) -> Result { let community = Community::read_from_name(context.pool(), &info.community_name, false).await?; let followers = GroupFollowers::new(community, &context).await?; @@ -75,24 +73,23 @@ pub(crate) async fn get_apub_community_followers( /// activites like votes or comments). pub(crate) async fn get_apub_community_outbox( info: web::Path, - context: web::Data, + context: Data, ) -> Result { - let community = Community::read_from_name(context.pool(), &info.community_name, false).await?; + let community: ApubCommunity = + Community::read_from_name(context.pool(), &info.community_name, false) + .await? + .into(); if community.deleted || community.removed { return Err(LemmyError::from_message("deleted")); } - let id = ObjectId::new(generate_outbox_url(&community.actor_id)?); - let outbox_data = CommunityContext(community.into(), context.get_ref().clone()); - let outbox: ApubCommunityOutbox = id - .dereference(&outbox_data, local_instance(&context).await, &mut 0) - .await?; - Ok(create_apub_response(&outbox.into_apub(&outbox_data).await?)) + let outbox = ApubCommunityOutbox::read_local(&community, &context).await?; + Ok(create_apub_response(&outbox)) } #[tracing::instrument(skip_all)] pub(crate) async fn get_apub_community_moderators( info: web::Path, - context: web::Data, + context: Data, ) -> Result { let community: ApubCommunity = Community::read_from_name(context.pool(), &info.community_name, false) @@ -101,29 +98,22 @@ pub(crate) async fn get_apub_community_moderators( if community.deleted || community.removed { return Err(LemmyError::from_message("deleted")); } - let id = ObjectId::new(generate_outbox_url(&community.actor_id)?); - let outbox_data = CommunityContext(community, context.get_ref().clone()); - let moderators: ApubCommunityModerators = id - .dereference(&outbox_data, local_instance(&context).await, &mut 0) - .await?; - Ok(create_apub_response( - &moderators.into_apub(&outbox_data).await?, - )) + let moderators = ApubCommunityModerators::read_local(&community, &context).await?; + Ok(create_apub_response(&moderators)) } /// Returns collection of featured (stickied) posts. pub(crate) async fn get_apub_community_featured( info: web::Path, - context: web::Data, + context: Data, ) -> Result { - let community = Community::read_from_name(context.pool(), &info.community_name, false).await?; + let community: ApubCommunity = + Community::read_from_name(context.pool(), &info.community_name, false) + .await? + .into(); if community.deleted || community.removed { return Err(LemmyError::from_message("deleted")); } - let id = ObjectId::new(generate_featured_url(&community.actor_id)?); - let data = CommunityContext(community.into(), context.get_ref().clone()); - let featured: ApubCommunityFeatured = id - .dereference(&data, local_instance(&context).await, &mut 0) - .await?; - Ok(create_apub_response(&featured.into_apub(&data).await?)) + let featured = ApubCommunityFeatured::read_local(&community, &context).await?; + Ok(create_apub_response(&featured)) } diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index 8b539919..72819ccb 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -1,28 +1,22 @@ use crate::{ activity_lists::SharedInboxActivities, fetcher::user_or_community::UserOrCommunity, - insert_activity, - local_instance, protocol::objects::tombstone::Tombstone, CONTEXT, }; use activitypub_federation::{ - core::inbox::receive_activity, - data::Data, - deser::context::WithContext, - traits::{ActivityHandler, Actor, ApubObject}, - APUB_JSON_CONTENT_TYPE, + actix_web::inbox::receive_activity, + config::Data, + protocol::context::WithContext, + FEDERATION_CONTENT_TYPE, }; -use actix_web::{web, HttpRequest, HttpResponse}; +use actix_web::{web, web::Bytes, HttpRequest, HttpResponse}; use http::StatusCode; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::activity::Activity; use lemmy_utils::error::LemmyError; -use once_cell::sync::OnceCell; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; +use serde::{Deserialize, Serialize}; use std::ops::Deref; -use tracing::{debug, log::info}; use url::Url; mod comment; @@ -34,45 +28,11 @@ pub mod site; pub async fn shared_inbox( request: HttpRequest, - payload: String, - context: web::Data, + body: Bytes, + data: Data, ) -> Result { - receive_lemmy_activity::(request, payload, context).await -} - -pub async fn receive_lemmy_activity( - request: HttpRequest, - payload: String, - context: web::Data, -) -> Result -where - Activity: ActivityHandler - + DeserializeOwned - + Send - + 'static, - ActorT: ApubObject + Actor + Send + 'static, - for<'de2> ::ApubType: serde::Deserialize<'de2>, -{ - static DATA: OnceCell> = OnceCell::new(); - let activity_value: Value = serde_json::from_str(&payload)?; - debug!("Parsing activity {}", payload); - let activity: Activity = serde_json::from_value(activity_value.clone())?; - // Log the activity, so we avoid receiving and parsing it twice. - let insert = insert_activity(activity.id(), activity_value, false, true, context.pool()).await?; - if !insert { - debug!("Received duplicate activity {}", activity.id().to_string()); - return Ok(HttpResponse::BadRequest().finish()); - } - info!("Received activity {}", payload); - - let data = DATA.get_or_init(|| Data::new(context.get_ref().clone())); - receive_activity::( - request, - activity, - local_instance(&context).await, - data, - ) - .await + receive_activity::(request, body, &data) + .await } /// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub @@ -82,20 +42,20 @@ where T: Serialize, { HttpResponse::Ok() - .content_type(APUB_JSON_CONTENT_TYPE) + .content_type(FEDERATION_CONTENT_TYPE) .json(WithContext::new(data, CONTEXT.deref().clone())) } fn create_json_apub_response(data: serde_json::Value) -> HttpResponse { HttpResponse::Ok() - .content_type(APUB_JSON_CONTENT_TYPE) + .content_type(FEDERATION_CONTENT_TYPE) .json(data) } fn create_apub_tombstone_response>(id: T) -> HttpResponse { let tombstone = Tombstone::new(id.into()); HttpResponse::Gone() - .content_type(APUB_JSON_CONTENT_TYPE) + .content_type(FEDERATION_CONTENT_TYPE) .status(StatusCode::GONE) .json(WithContext::new(tombstone, CONTEXT.deref().clone())) } diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index 6a1bc5b3..79696660 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -1,12 +1,17 @@ use crate::{ activity_lists::PersonInboxActivitiesWithAnnouncable, fetcher::user_or_community::UserOrCommunity, - http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity}, + http::{create_apub_response, create_apub_tombstone_response}, objects::person::ApubPerson, protocol::collections::empty_outbox::EmptyOutbox, }; -use activitypub_federation::{deser::context::WithContext, traits::ApubObject}; -use actix_web::{web, HttpRequest, HttpResponse}; +use activitypub_federation::{ + actix_web::inbox::receive_activity, + config::Data, + protocol::context::WithContext, + traits::Object, +}; +use actix_web::{web, web::Bytes, HttpRequest, HttpResponse}; use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url}; use lemmy_db_schema::{source::person::Person, traits::ApubActor}; use lemmy_utils::error::LemmyError; @@ -21,7 +26,7 @@ pub struct PersonQuery { #[tracing::instrument(skip_all)] pub(crate) async fn get_apub_person_http( info: web::Path, - context: web::Data, + context: Data, ) -> Result { let user_name = info.into_inner().user_name; // TODO: this needs to be able to read deleted persons, so that it can send tombstones @@ -30,7 +35,7 @@ pub(crate) async fn get_apub_person_http( .into(); if !person.deleted { - let apub = person.into_apub(&context).await?; + let apub = person.into_json(&context).await?; Ok(create_apub_response(&apub)) } else { @@ -41,11 +46,11 @@ pub(crate) async fn get_apub_person_http( #[tracing::instrument(skip_all)] pub async fn person_inbox( request: HttpRequest, - payload: String, - context: web::Data, + body: Bytes, + data: Data, ) -> Result { - receive_lemmy_activity::, UserOrCommunity>( - request, payload, context, + receive_activity::, UserOrCommunity, LemmyContext>( + request, body, &data, ) .await } @@ -53,7 +58,7 @@ pub async fn person_inbox( #[tracing::instrument(skip_all)] pub(crate) async fn get_apub_person_outbox( info: web::Path, - context: web::Data, + context: Data, ) -> Result { let person = Person::read_from_name(context.pool(), &info.user_name, false).await?; let outbox_id = generate_outbox_url(&person.actor_id)?.into(); diff --git a/crates/apub/src/http/post.rs b/crates/apub/src/http/post.rs index fc164a54..ed1815c0 100644 --- a/crates/apub/src/http/post.rs +++ b/crates/apub/src/http/post.rs @@ -2,7 +2,7 @@ use crate::{ http::{create_apub_response, create_apub_tombstone_response, err_object_not_local}, objects::post::ApubPost, }; -use activitypub_federation::traits::ApubObject; +use activitypub_federation::{config::Data, traits::Object}; use actix_web::{web, HttpResponse}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{newtypes::PostId, source::post::Post, traits::Crud}; @@ -18,7 +18,7 @@ pub(crate) struct PostQuery { #[tracing::instrument(skip_all)] pub(crate) async fn get_apub_post( info: web::Path, - context: web::Data, + context: Data, ) -> Result { let id = PostId(info.post_id.parse::()?); let post: ApubPost = Post::read(context.pool(), id).await?.into(); @@ -27,7 +27,7 @@ pub(crate) async fn get_apub_post( } if !post.deleted && !post.removed { - Ok(create_apub_response(&post.into_apub(&context).await?)) + Ok(create_apub_response(&post.into_json(&context).await?)) } else { Ok(create_apub_tombstone_response(post.ap_id.clone())) } diff --git a/crates/apub/src/http/site.rs b/crates/apub/src/http/site.rs index fe6c34f6..61802eae 100644 --- a/crates/apub/src/http/site.rs +++ b/crates/apub/src/http/site.rs @@ -1,28 +1,33 @@ use crate::{ activity_lists::SiteInboxActivities, - http::{create_apub_response, receive_lemmy_activity}, + http::create_apub_response, objects::{instance::ApubSite, person::ApubPerson}, protocol::collections::empty_outbox::EmptyOutbox, }; -use activitypub_federation::{deser::context::WithContext, traits::ApubObject}; -use actix_web::{web, HttpRequest, HttpResponse}; +use activitypub_federation::{ + actix_web::inbox::receive_activity, + config::Data, + protocol::context::WithContext, + traits::Object, +}; +use actix_web::{web::Bytes, HttpRequest, HttpResponse}; use lemmy_api_common::context::LemmyContext; use lemmy_db_views::structs::SiteView; use lemmy_utils::error::LemmyError; use url::Url; pub(crate) async fn get_apub_site_http( - context: web::Data, + context: Data, ) -> Result { let site: ApubSite = SiteView::read_local(context.pool()).await?.site.into(); - let apub = site.into_apub(&context).await?; + let apub = site.into_json(&context).await?; Ok(create_apub_response(&apub)) } #[tracing::instrument(skip_all)] pub(crate) async fn get_apub_site_outbox( - context: web::Data, + context: Data, ) -> Result { let outbox_id = format!( "{}/site_outbox", @@ -35,9 +40,11 @@ pub(crate) async fn get_apub_site_outbox( #[tracing::instrument(skip_all)] pub async fn get_apub_site_inbox( request: HttpRequest, - payload: String, - context: web::Data, + body: Bytes, + data: Data, ) -> Result { - receive_lemmy_activity::, ApubPerson>(request, payload, context) - .await + receive_activity::, ApubPerson, LemmyContext>( + request, body, &data, + ) + .await } diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index d277a3bc..43e26961 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -1,20 +1,19 @@ use crate::fetcher::post_or_comment::PostOrComment; -use activitypub_federation::{ - core::signatures::PublicKey, - traits::{Actor, ApubObject}, - InstanceSettings, - LocalInstance, - UrlVerifier, -}; +use activitypub_federation::config::{Data, UrlVerifier}; use async_trait::async_trait; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ - source::{activity::Activity, instance::Instance, local_site::LocalSite}, + source::{ + activity::{Activity, ActivityInsertForm}, + instance::Instance, + local_site::LocalSite, + }, + traits::Crud, utils::DbPool, }; use lemmy_utils::{error::LemmyError, settings::structs::Settings}; use once_cell::sync::Lazy; -use tokio::sync::OnceCell; +use serde::Serialize; use url::Url; pub mod activities; @@ -27,52 +26,23 @@ pub(crate) mod mentions; pub mod objects; pub mod protocol; -const FEDERATION_HTTP_FETCH_LIMIT: i32 = 25; +pub const FEDERATION_HTTP_FETCH_LIMIT: u32 = 50; static CONTEXT: Lazy> = Lazy::new(|| { serde_json::from_str(include_str!("../assets/lemmy/context.json")).expect("parse context") }); -// TODO: store this in context? but its only used in this crate, no need to expose it elsewhere -// TODO this singleton needs to be redone to account for live data. -async fn local_instance(context: &LemmyContext) -> &'static LocalInstance { - static LOCAL_INSTANCE: OnceCell = OnceCell::const_new(); - LOCAL_INSTANCE - .get_or_init(|| async { - // Local site may be missing - let local_site = &LocalSite::read(context.pool()).await; - let worker_count = local_site - .as_ref() - .map(|l| l.federation_worker_count) - .unwrap_or(64) as u64; - - let settings = InstanceSettings::builder() - .http_fetch_retry_limit(FEDERATION_HTTP_FETCH_LIMIT) - .worker_count(worker_count) - .debug(cfg!(debug_assertions)) - .http_signature_compat(true) - .url_verifier(Box::new(VerifyUrlData(context.clone()))) - .build() - .expect("configure federation"); - LocalInstance::new( - context.settings().hostname.clone(), - context.client().clone(), - settings, - ) - }) - .await -} - #[derive(Clone)] -struct VerifyUrlData(LemmyContext); +pub struct VerifyUrlData(pub DbPool); #[async_trait] impl UrlVerifier for VerifyUrlData { async fn verify(&self, url: &Url) -> Result<(), &'static str> { - let local_site_data = fetch_local_site_data(self.0.pool()) + let local_site_data = fetch_local_site_data(&self.0) .await .expect("read local site data"); - check_apub_id_valid(url, &local_site_data, self.0.settings()) + check_apub_id_valid(url, &local_site_data)?; + Ok(()) } } @@ -86,19 +56,9 @@ impl UrlVerifier for VerifyUrlData { /// /// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a /// post/comment in a local community. -#[tracing::instrument(skip(settings, local_site_data))] -fn check_apub_id_valid( - apub_id: &Url, - local_site_data: &LocalSiteData, - settings: &Settings, -) -> Result<(), &'static str> { +#[tracing::instrument(skip(local_site_data))] +fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> Result<(), &'static str> { let domain = apub_id.domain().expect("apud id has domain").to_string(); - let local_instance = settings - .get_hostname_without_port() - .expect("local hostname is valid"); - if domain == local_instance { - return Ok(()); - } if !local_site_data .local_site @@ -109,10 +69,6 @@ fn check_apub_id_valid( return Err("Federation disabled"); } - if apub_id.scheme() != settings.get_protocol_string() { - return Err("Invalid protocol scheme"); - } - if let Some(blocked) = local_site_data.blocked_instances.as_ref() { if blocked.iter().any(|i| domain.eq(&i.domain)) { return Err("Domain is blocked"); @@ -161,7 +117,6 @@ pub(crate) fn check_apub_id_valid_with_strictness( local_site_data: &LocalSiteData, settings: &Settings, ) -> Result<(), LemmyError> { - check_apub_id_valid(apub_id, local_site_data, settings).map_err(LemmyError::from_message)?; let domain = apub_id.domain().expect("apud id has domain").to_string(); let local_instance = settings .get_hostname_without_port() @@ -169,6 +124,7 @@ pub(crate) fn check_apub_id_valid_with_strictness( if domain == local_instance { return Ok(()); } + check_apub_id_valid(apub_id, local_site_data).map_err(LemmyError::from_message)?; if let Some(allowed) = local_site_data.allowed_instances.as_ref() { // Only check allowlist if this is a community @@ -179,8 +135,12 @@ pub(crate) fn check_apub_id_valid_with_strictness( .iter() .map(|i| i.domain.clone()) .collect::>(); + let local_instance = settings + .get_hostname_without_port() + .expect("local hostname is valid"); allowed_and_local.push(local_instance); + let domain = apub_id.domain().expect("apud id has domain").to_string(); if !allowed_and_local.contains(&domain) { return Err(LemmyError::from_message( "Federation forbidden by strict allowlist", @@ -191,40 +151,41 @@ pub(crate) fn check_apub_id_valid_with_strictness( Ok(()) } -/// Store a sent or received activity in the database, for logging purposes. These records are not -/// persistent. -#[tracing::instrument(skip(pool))] -async fn insert_activity( +/// Store a sent or received activity in the database. +/// +/// Stored activities are served over the HTTP endpoint `GET /activities/{type_}/{id}`. This also +/// ensures that the same activity cannot be received more than once. +#[tracing::instrument(skip(data, activity))] +async fn insert_activity( ap_id: &Url, - activity: serde_json::Value, + activity: &T, local: bool, sensitive: bool, - pool: &DbPool, -) -> Result { + data: &Data, +) -> Result<(), LemmyError> +where + T: Serialize, +{ let ap_id = ap_id.clone().into(); - Ok(Activity::insert(pool, ap_id, activity, local, Some(sensitive)).await?) -} - -/// Common methods provided by ActivityPub actors (community and person). Not all methods are -/// implemented by all actors. -pub trait ActorType: Actor + ApubObject { - fn actor_id(&self) -> Url; - - fn private_key(&self) -> Option; - - fn get_public_key(&self) -> PublicKey { - PublicKey::new_main_key(self.actor_id(), self.public_key().to_string()) - } + let form = ActivityInsertForm { + ap_id, + data: serde_json::to_value(activity)?, + local: Some(local), + sensitive: Some(sensitive), + updated: None, + }; + Activity::create(data.pool(), &form).await?; + Ok(()) } -#[async_trait::async_trait(?Send)] -pub trait SendActivity { - type Response; +#[async_trait::async_trait] +pub trait SendActivity: Sync { + type Response: Sync + Send; async fn send_activity( _request: &Self, _response: &Self::Response, - _context: &LemmyContext, + _context: &Data, ) -> Result<(), LemmyError> { Ok(()) } diff --git a/crates/apub/src/mentions.rs b/crates/apub/src/mentions.rs index e23b8634..4de822cc 100644 --- a/crates/apub/src/mentions.rs +++ b/crates/apub/src/mentions.rs @@ -1,10 +1,10 @@ -use crate::{ - fetcher::webfinger::webfinger_resolve_actor, - objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson}, - ActorType, +use crate::objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson}; +use activitypub_federation::{ + config::Data, + fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor}, + kinds::link::MentionType, + traits::Actor, }; -use activitypub_federation::core::object_id::ObjectId; -use activitystreams_kinds::link::MentionType; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{comment::Comment, person::Person, post::Post}, @@ -46,11 +46,10 @@ pub struct MentionsAndAddresses { pub async fn collect_non_local_mentions( comment: &ApubComment, community_id: ObjectId, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result { let parent_creator = get_comment_parent_creator(context.pool(), comment).await?; - let mut addressed_ccs: Vec = vec![community_id.into(), parent_creator.actor_id()]; + let mut addressed_ccs: Vec = vec![community_id.into(), parent_creator.id()]; // Add the mention tag let parent_creator_tag = Mention { @@ -58,7 +57,7 @@ pub async fn collect_non_local_mentions( name: Some(format!( "@{}@{}", &parent_creator.name, - &parent_creator.actor_id().domain().expect("has domain") + &parent_creator.id().domain().expect("has domain") )), kind: MentionType::Mention, }; @@ -73,14 +72,12 @@ pub async fn collect_non_local_mentions( for mention in &mentions { let identifier = format!("{}@{}", mention.name, mention.domain); - let actor_id = - webfinger_resolve_actor::(&identifier, true, context, request_counter).await; - if let Ok(actor_id) = actor_id { - let actor_id: ObjectId = ObjectId::new(actor_id); - addressed_ccs.push(actor_id.to_string().parse()?); + let person = webfinger_resolve_actor::(&identifier, context).await; + if let Ok(person) = person { + addressed_ccs.push(person.actor_id.to_string().parse()?); let mention_tag = Mention { - href: actor_id.into(), + href: person.id(), name: Some(mention.full_name()), kind: MentionType::Mention, }; diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 6021ff36..e2a03b8b 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -2,7 +2,6 @@ use crate::{ activities::{verify_is_public, verify_person_in_community}, check_apub_id_valid_with_strictness, fetch_local_site_data, - local_instance, mentions::collect_non_local_mentions, objects::{read_from_string_or_source, verify_is_remote_object}, protocol::{ @@ -10,15 +9,13 @@ use crate::{ InCommunity, Source, }, - PostOrComment, }; use activitypub_federation::{ - core::object_id::ObjectId, - deser::values::MediaTypeMarkdownOrHtml, - traits::ApubObject, - utils::verify_domains_match, + config::Data, + kinds::{object::NoteType, public}, + protocol::{values::MediaTypeMarkdownOrHtml, verification::verify_domains_match}, + traits::Object, }; -use activitystreams_kinds::{object::NoteType, public}; use chrono::NaiveDateTime; use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex}; use lemmy_db_schema::{ @@ -54,11 +51,10 @@ impl From for ApubComment { } } -#[async_trait::async_trait(?Send)] -impl ApubObject for ApubComment { +#[async_trait::async_trait] +impl Object for ApubComment { type DataType = LemmyContext; - type ApubType = Note; - type DbType = Comment; + type Kind = Note; type Error = LemmyError; fn last_refreshed_at(&self) -> Option { @@ -66,9 +62,9 @@ impl ApubObject for ApubComment { } #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - context: &LemmyContext, + context: &Data, ) -> Result, LemmyError> { Ok( Comment::read_from_apub_id(context.pool(), object_id) @@ -78,7 +74,7 @@ impl ApubObject for ApubComment { } #[tracing::instrument(skip_all)] - async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { + async fn delete(self, context: &Data) -> Result<(), LemmyError> { if !self.deleted { let form = CommentUpdateForm::builder().deleted(Some(true)).build(); Comment::update(context.pool(), self.id, &form).await?; @@ -87,7 +83,7 @@ impl ApubObject for ApubComment { } #[tracing::instrument(skip_all)] - async fn into_apub(self, context: &LemmyContext) -> Result { + async fn into_json(self, context: &Data) -> Result { let creator_id = self.creator_id; let creator = Person::read(context.pool(), creator_id).await?; @@ -98,23 +94,17 @@ impl ApubObject for ApubComment { let in_reply_to = if let Some(comment_id) = self.parent_comment_id() { let parent_comment = Comment::read(context.pool(), comment_id).await?; - ObjectId::::new(parent_comment.ap_id) + parent_comment.ap_id.into() } else { - ObjectId::::new(post.ap_id) + post.ap_id.into() }; let language = LanguageTag::new_single(self.language_id, context.pool()).await?; - let maa = collect_non_local_mentions( - &self, - ObjectId::new(community.actor_id.clone()), - context, - &mut 0, - ) - .await?; + let maa = collect_non_local_mentions(&self, community.actor_id.clone().into(), context).await?; let note = Note { r#type: NoteType::Note, - id: ObjectId::new(self.ap_id.clone()), - attributed_to: ObjectId::new(creator.actor_id), + id: self.ap_id.clone().into(), + attributed_to: creator.actor_id.into(), to: vec![public()], cc: maa.ccs, content: markdown_to_html(&self.content), @@ -126,7 +116,7 @@ impl ApubObject for ApubComment { tag: maa.tags, distinguished: Some(self.distinguished), language, - audience: Some(ObjectId::new(community.actor_id)), + audience: Some(community.actor_id.into()), }; Ok(note) @@ -136,13 +126,12 @@ impl ApubObject for ApubComment { async fn verify( note: &Note, expected_domain: &Url, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { verify_domains_match(note.id.inner(), expected_domain)?; verify_domains_match(note.attributed_to.inner(), note.id.inner())?; verify_is_public(¬e.to, ¬e.cc)?; - let community = note.community(context, request_counter).await?; + let community = note.community(context).await?; let local_site_data = fetch_local_site_data(context.pool()).await?; check_apub_id_valid_with_strictness( @@ -152,8 +141,8 @@ impl ApubObject for ApubComment { context.settings(), )?; verify_is_remote_object(note.id.inner(), context.settings())?; - verify_person_in_community(¬e.attributed_to, &community, context, request_counter).await?; - let (post, _) = note.get_parents(context, request_counter).await?; + verify_person_in_community(¬e.attributed_to, &community, context).await?; + let (post, _) = note.get_parents(context).await?; if post.locked { return Err(LemmyError::from_message("Post is locked")); } @@ -164,16 +153,9 @@ impl ApubObject for ApubComment { /// /// If the parent community, post and comment(s) are not known locally, these are also fetched. #[tracing::instrument(skip_all)] - async fn from_apub( - note: Note, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let creator = note - .attributed_to - .dereference(context, local_instance(context).await, request_counter) - .await?; - let (post, parent_comment) = note.get_parents(context, request_counter).await?; + async fn from_json(note: Note, context: &Data) -> Result { + let creator = note.attributed_to.dereference(context).await?; + let (post, parent_comment) = note.get_parents(context).await?; let content = read_from_string_or_source(¬e.content, ¬e.media_type, ¬e.source); @@ -221,17 +203,15 @@ pub(crate) mod tests { async fn prepare_comment_test( url: &Url, - context: &LemmyContext, + context: &Data, ) -> (ApubPerson, ApubCommunity, ApubPost, ApubSite) { - let (person, site) = parse_lemmy_person(context).await; - let community = parse_lemmy_community(context).await; + // use separate counter so this doesnt affect tests + let context2 = context.reset_request_count(); + let (person, site) = parse_lemmy_person(&context2).await; + let community = parse_lemmy_community(&context2).await; let post_json = file_to_json_object("assets/lemmy/objects/page.json").unwrap(); - ApubPost::verify(&post_json, url, context, &mut 0) - .await - .unwrap(); - let post = ApubPost::from_apub(post_json, context, &mut 0) - .await - .unwrap(); + ApubPost::verify(&post_json, url, &context2).await.unwrap(); + let post = ApubPost::from_json(post_json, &context2).await.unwrap(); (person, community, post, site) } @@ -251,21 +231,18 @@ pub(crate) mod tests { let data = prepare_comment_test(&url, &context).await; let json: Note = file_to_json_object("assets/lemmy/objects/note.json").unwrap(); - let mut request_counter = 0; - ApubComment::verify(&json, &url, &context, &mut request_counter) - .await - .unwrap(); - let comment = ApubComment::from_apub(json.clone(), &context, &mut request_counter) + ApubComment::verify(&json, &url, &context).await.unwrap(); + let comment = ApubComment::from_json(json.clone(), &context) .await .unwrap(); assert_eq!(comment.ap_id, url.into()); assert_eq!(comment.content.len(), 14); assert!(!comment.local); - assert_eq!(request_counter, 0); + assert_eq!(context.request_count(), 0); let comment_id = comment.id; - let to_apub = comment.into_apub(&context).await.unwrap(); + let to_apub = comment.into_json(&context).await.unwrap(); assert_json_include!(actual: json, expected: to_apub); Comment::delete(context.pool(), comment_id).await.unwrap(); @@ -283,25 +260,20 @@ pub(crate) mod tests { Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2") .unwrap(); let person_json = file_to_json_object("assets/pleroma/objects/person.json").unwrap(); - ApubPerson::verify(&person_json, &pleroma_url, &context, &mut 0) - .await - .unwrap(); - ApubPerson::from_apub(person_json, &context, &mut 0) + ApubPerson::verify(&person_json, &pleroma_url, &context) .await .unwrap(); + ApubPerson::from_json(person_json, &context).await.unwrap(); let json = file_to_json_object("assets/pleroma/objects/note.json").unwrap(); - let mut request_counter = 0; - ApubComment::verify(&json, &pleroma_url, &context, &mut request_counter) - .await - .unwrap(); - let comment = ApubComment::from_apub(json, &context, &mut request_counter) + ApubComment::verify(&json, &pleroma_url, &context) .await .unwrap(); + let comment = ApubComment::from_json(json, &context).await.unwrap(); assert_eq!(comment.ap_id, pleroma_url.into()); assert_eq!(comment.content.len(), 64); assert!(!comment.local); - assert_eq!(request_counter, 0); + assert_eq!(context.request_count(), 1); Comment::delete(context.pool(), comment.id).await.unwrap(); cleanup(data, &context).await; diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index 7a1583ca..396c2af8 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -1,21 +1,18 @@ use crate::{ check_apub_id_valid_with_strictness, - collections::CommunityContext, fetch_local_site_data, - local_instance, objects::instance::fetch_instance_actor_for_object, protocol::{ objects::{group::Group, Endpoints, LanguageTag}, ImageObject, Source, }, - ActorType, }; use activitypub_federation::{ - core::object_id::ObjectId, - traits::{Actor, ApubObject}, + config::Data, + kinds::actor::GroupType, + traits::{Actor, Object}, }; -use activitystreams_kinds::actor::GroupType; use chrono::NaiveDateTime; use itertools::Itertools; use lemmy_api_common::{ @@ -54,11 +51,10 @@ impl From for ApubCommunity { } } -#[async_trait::async_trait(?Send)] -impl ApubObject for ApubCommunity { +#[async_trait::async_trait] +impl Object for ApubCommunity { type DataType = LemmyContext; - type ApubType = Group; - type DbType = Community; + type Kind = Group; type Error = LemmyError; fn last_refreshed_at(&self) -> Option { @@ -66,9 +62,9 @@ impl ApubObject for ApubCommunity { } #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - context: &LemmyContext, + context: &Data, ) -> Result, LemmyError> { Ok( Community::read_from_apub_id(context.pool(), &object_id.into()) @@ -78,21 +74,21 @@ impl ApubObject for ApubCommunity { } #[tracing::instrument(skip_all)] - async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { + async fn delete(self, context: &Data) -> Result<(), LemmyError> { let form = CommunityUpdateForm::builder().deleted(Some(true)).build(); Community::update(context.pool(), self.id, &form).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn into_apub(self, data: &LemmyContext) -> Result { + async fn into_json(self, data: &Data) -> Result { let community_id = self.id; let langs = CommunityLanguage::read(data.pool(), community_id).await?; let language = LanguageTag::new_multiple(langs, data.pool()).await?; let group = Group { kind: GroupType::Group, - id: ObjectId::new(self.actor_id()), + id: self.id().into(), preferred_username: self.name.clone(), name: Some(self.title.clone()), summary: self.description.as_ref().map(|b| markdown_to_html(b)), @@ -103,12 +99,12 @@ impl ApubObject for ApubCommunity { moderators: Some(generate_moderators_url(&self.actor_id)?.into()), featured: Some(generate_featured_url(&self.actor_id)?.into()), inbox: self.inbox_url.clone().into(), - outbox: ObjectId::new(generate_outbox_url(&self.actor_id)?), + outbox: generate_outbox_url(&self.actor_id)?.into(), followers: self.followers_url.clone().into(), endpoints: self.shared_inbox_url.clone().map(|s| Endpoints { shared_inbox: s.into(), }), - public_key: self.get_public_key(), + public_key: self.public_key(), language, published: Some(convert_datetime(self.published)), updated: self.updated.map(convert_datetime), @@ -122,20 +118,18 @@ impl ApubObject for ApubCommunity { async fn verify( group: &Group, expected_domain: &Url, - context: &LemmyContext, - _request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { group.verify(expected_domain, context).await } /// Converts a `Group` to `Community`, inserts it into the database and updates moderators. #[tracing::instrument(skip_all)] - async fn from_apub( + async fn from_json( group: Group, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result { - let instance_id = fetch_instance_actor_for_object(&group.id, context, request_counter).await?; + let instance_id = fetch_instance_actor_for_object(&group.id, context).await?; let form = Group::into_insert_form(group.clone(), instance_id); let languages = LanguageTag::to_language_id_multiple(group.language, context.pool()).await?; @@ -144,20 +138,19 @@ impl ApubObject for ApubCommunity { CommunityLanguage::update(context.pool(), languages, community.id).await?; let community: ApubCommunity = community.into(); - let outbox_data = CommunityContext(community.clone(), context.clone()); // Fetching mods and outbox is not necessary for Lemmy to work, so ignore errors. Besides, // we need to ignore these errors so that tests can work entirely offline. group .outbox - .dereference(&outbox_data, local_instance(context).await, request_counter) + .dereference(&community, context) .await .map_err(|e| debug!("{}", e)) .ok(); if let Some(moderators) = group.attributed_to.or(group.moderators) { moderators - .dereference(&outbox_data, local_instance(context).await, request_counter) + .dereference(&community, context) .await .map_err(|e| debug!("{}", e)) .ok(); @@ -168,10 +161,18 @@ impl ApubObject for ApubCommunity { } impl Actor for ApubCommunity { - fn public_key(&self) -> &str { + fn id(&self) -> Url { + self.actor_id.inner().clone() + } + + fn public_key_pem(&self) -> &str { &self.public_key } + fn private_key_pem(&self) -> Option { + self.private_key.clone() + } + fn inbox(&self) -> Url { self.inbox_url.clone().into() } @@ -181,15 +182,6 @@ impl Actor for ApubCommunity { } } -impl ActorType for ApubCommunity { - fn actor_id(&self) -> Url { - self.actor_id.clone().into() - } - fn private_key(&self) -> Option { - self.private_key.clone() - } -} - impl ApubCommunity { /// For a given community, returns the inboxes of all followers. #[tracing::instrument(skip_all)] @@ -230,27 +222,25 @@ pub(crate) mod tests { objects::{instance::tests::parse_lemmy_instance, tests::init_context}, protocol::tests::file_to_json_object, }; + use activitypub_federation::fetch::collection_id::CollectionId; use lemmy_db_schema::{source::site::Site, traits::Crud}; use serial_test::serial; - pub(crate) async fn parse_lemmy_community(context: &LemmyContext) -> ApubCommunity { + pub(crate) async fn parse_lemmy_community(context: &Data) -> ApubCommunity { + // use separate counter so this doesnt affect tests + let context2 = context.reset_request_count(); let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json").unwrap(); // change these links so they dont fetch over the network json.moderators = None; json.attributed_to = None; json.outbox = - ObjectId::new(Url::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap()); + CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap(); - let mut request_counter = 0; - ApubCommunity::verify(&json, &url, context, &mut request_counter) - .await - .unwrap(); - let community = ApubCommunity::from_apub(json, context, &mut request_counter) - .await - .unwrap(); + ApubCommunity::verify(&json, &url, &context2).await.unwrap(); + let community = ApubCommunity::from_json(json, &context2).await.unwrap(); // this makes one requests to the (intentionally broken) outbox collection - assert_eq!(request_counter, 1); + assert_eq!(context2.request_count(), 1); community } diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs index 8fa3976a..4cb96a0f 100644 --- a/crates/apub/src/objects/instance.rs +++ b/crates/apub/src/objects/instance.rs @@ -1,22 +1,20 @@ use crate::{ check_apub_id_valid_with_strictness, fetch_local_site_data, - local_instance, objects::read_from_string_or_source_opt, protocol::{ objects::{instance::Instance, LanguageTag}, ImageObject, Source, }, - ActorType, }; use activitypub_federation::{ - core::object_id::ObjectId, - deser::values::MediaTypeHtml, - traits::{Actor, ApubObject}, - utils::verify_domains_match, + config::Data, + fetch::object_id::ObjectId, + kinds::actor::ApplicationType, + protocol::{values::MediaTypeHtml, verification::verify_domains_match}, + traits::{Actor, Object}, }; -use activitystreams_kinds::actor::ApplicationType; use chrono::NaiveDateTime; use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex}; use lemmy_db_schema::{ @@ -57,11 +55,10 @@ impl From for ApubSite { } } -#[async_trait::async_trait(?Send)] -impl ApubObject for ApubSite { +#[async_trait::async_trait] +impl Object for ApubSite { type DataType = LemmyContext; - type ApubType = Instance; - type DbType = Site; + type Kind = Instance; type Error = LemmyError; fn last_refreshed_at(&self) -> Option { @@ -69,9 +66,9 @@ impl ApubObject for ApubSite { } #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - data: &Self::DataType, + data: &Data, ) -> Result, LemmyError> { Ok( Site::read_from_apub_id(data.pool(), object_id) @@ -80,19 +77,19 @@ impl ApubObject for ApubSite { ) } - async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> { + async fn delete(self, _data: &Data) -> Result<(), LemmyError> { unimplemented!() } #[tracing::instrument(skip_all)] - async fn into_apub(self, data: &Self::DataType) -> Result { + async fn into_json(self, data: &Data) -> Result { let site_id = self.id; let langs = SiteLanguage::read(data.pool(), site_id).await?; let language = LanguageTag::new_multiple(langs, data.pool()).await?; let instance = Instance { kind: ApplicationType::Application, - id: ObjectId::new(self.actor_id()), + id: self.id().into(), name: self.name.clone(), content: self.sidebar.as_ref().map(|d| markdown_to_html(d)), source: self.sidebar.clone().map(Source::new), @@ -102,7 +99,7 @@ impl ApubObject for ApubSite { image: self.banner.clone().map(ImageObject::new), inbox: self.inbox_url.clone().into(), outbox: Url::parse(&format!("{}/site_outbox", self.actor_id))?, - public_key: self.get_public_key(), + public_key: self.public_key(), language, published: convert_datetime(self.published), updated: self.updated.map(convert_datetime), @@ -112,10 +109,9 @@ impl ApubObject for ApubSite { #[tracing::instrument(skip_all)] async fn verify( - apub: &Self::ApubType, + apub: &Self::Kind, expected_domain: &Url, - data: &Self::DataType, - _request_counter: &mut i32, + data: &Data, ) -> Result<(), LemmyError> { let local_site_data = fetch_local_site_data(data.pool()).await?; @@ -130,11 +126,7 @@ impl ApubObject for ApubSite { } #[tracing::instrument(skip_all)] - async fn from_apub( - apub: Self::ApubType, - data: &Self::DataType, - _request_counter: &mut i32, - ) -> Result { + async fn from_json(apub: Self::Kind, data: &Data) -> Result { let domain = apub.id.inner().domain().expect("group id has domain"); let instance = DbInstance::read_or_create(data.pool(), domain.to_string()).await?; @@ -160,20 +152,19 @@ impl ApubObject for ApubSite { } } -impl ActorType for ApubSite { - fn actor_id(&self) -> Url { - self.actor_id.clone().into() - } - fn private_key(&self) -> Option { - self.private_key.clone() +impl Actor for ApubSite { + fn id(&self) -> Url { + self.actor_id.inner().clone() } -} -impl Actor for ApubSite { - fn public_key(&self) -> &str { + fn public_key_pem(&self) -> &str { &self.public_key } + fn private_key_pem(&self) -> Option { + self.private_key.clone() + } + fn inbox(&self) -> Url { self.inbox_url.clone().into() } @@ -182,13 +173,12 @@ impl Actor for ApubSite { /// Try to fetch the instance actor (to make things like instance rules available). pub(in crate::objects) async fn fetch_instance_actor_for_object + Clone>( object_id: &T, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result { let object_id: Url = object_id.clone().into(); let instance_id = Site::instance_actor_id_from_url(object_id); - let site = ObjectId::::new(instance_id.clone()) - .dereference(context, local_instance(context).await, request_counter) + let site = ObjectId::::from(instance_id.clone()) + .dereference(context) .await; match site { Ok(s) => Ok(s.instance_id), @@ -222,17 +212,12 @@ pub(crate) mod tests { use lemmy_db_schema::traits::Crud; use serial_test::serial; - pub(crate) async fn parse_lemmy_instance(context: &LemmyContext) -> ApubSite { + pub(crate) async fn parse_lemmy_instance(context: &Data) -> ApubSite { let json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap(); let id = Url::parse("https://enterprise.lemmy.ml/").unwrap(); - let mut request_counter = 0; - ApubSite::verify(&json, &id, context, &mut request_counter) - .await - .unwrap(); - let site = ApubSite::from_apub(json, context, &mut request_counter) - .await - .unwrap(); - assert_eq!(request_counter, 0); + ApubSite::verify(&json, &id, context).await.unwrap(); + let site = ApubSite::from_json(json, context).await.unwrap(); + assert_eq!(context.request_count(), 0); site } diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs index 29df3d64..6d2ff291 100644 --- a/crates/apub/src/objects/mod.rs +++ b/crates/apub/src/objects/mod.rs @@ -1,5 +1,5 @@ use crate::protocol::Source; -use activitypub_federation::deser::values::MediaTypeMarkdownOrHtml; +use activitypub_federation::protocol::values::MediaTypeMarkdownOrHtml; use anyhow::anyhow; use html2md::parse_html; use lemmy_utils::{error::LemmyError, settings::structs::Settings}; @@ -54,6 +54,7 @@ pub(crate) fn verify_is_remote_object(id: &Url, settings: &Settings) -> Result<( #[cfg(test)] pub(crate) mod tests { + use activitypub_federation::config::{Data, FederationConfig}; use anyhow::anyhow; use lemmy_api_common::{ context::LemmyContext, @@ -87,7 +88,7 @@ pub(crate) mod tests { } // TODO: would be nice if we didnt have to use a full context for tests. - pub(crate) async fn init_context() -> LemmyContext { + pub(crate) async fn init_context() -> Data { async fn x() -> Result { Ok(String::new()) } @@ -110,13 +111,12 @@ pub(crate) mod tests { let rate_limit_cell = RateLimitCell::new(rate_limit_config).await; let chat_server = Arc::new(ChatServer::startup()); - LemmyContext::create( - pool, - chat_server, - client, - settings, - secret, - rate_limit_cell.clone(), - ) + let context = LemmyContext::create(pool, chat_server, client, secret, rate_limit_cell.clone()); + let config = FederationConfig::builder() + .domain("example.com") + .app_data(context) + .build() + .unwrap(); + config.to_request_data() } } diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index 55bfcd29..e5431d14 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -10,12 +10,11 @@ use crate::{ ImageObject, Source, }, - ActorType, }; use activitypub_federation::{ - core::object_id::ObjectId, - traits::{Actor, ApubObject}, - utils::verify_domains_match, + config::Data, + protocol::verification::verify_domains_match, + traits::{Actor, Object}, }; use chrono::NaiveDateTime; use lemmy_api_common::{ @@ -54,11 +53,10 @@ impl From for ApubPerson { } } -#[async_trait::async_trait(?Send)] -impl ApubObject for ApubPerson { +#[async_trait::async_trait] +impl Object for ApubPerson { type DataType = LemmyContext; - type ApubType = Person; - type DbType = DbPerson; + type Kind = Person; type Error = LemmyError; fn last_refreshed_at(&self) -> Option { @@ -66,9 +64,9 @@ impl ApubObject for ApubPerson { } #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - context: &LemmyContext, + context: &Data, ) -> Result, LemmyError> { Ok( DbPerson::read_from_apub_id(context.pool(), &object_id.into()) @@ -78,14 +76,14 @@ impl ApubObject for ApubPerson { } #[tracing::instrument(skip_all)] - async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { + async fn delete(self, context: &Data) -> Result<(), LemmyError> { let form = PersonUpdateForm::builder().deleted(Some(true)).build(); DbPerson::update(context.pool(), self.id, &form).await?; Ok(()) } #[tracing::instrument(skip_all)] - async fn into_apub(self, _pool: &LemmyContext) -> Result { + async fn into_json(self, _context: &Data) -> Result { let kind = if self.bot_account { UserTypes::Service } else { @@ -94,7 +92,7 @@ impl ApubObject for ApubPerson { let person = Person { kind, - id: ObjectId::new(self.actor_id.clone()), + id: self.actor_id.clone().into(), preferred_username: self.name.clone(), name: self.display_name.clone(), summary: self.bio.as_ref().map(|b| markdown_to_html(b)), @@ -107,7 +105,7 @@ impl ApubObject for ApubPerson { endpoints: self.shared_inbox_url.clone().map(|s| Endpoints { shared_inbox: s.into(), }), - public_key: self.get_public_key(), + public_key: self.public_key(), updated: self.updated.map(convert_datetime), inbox: self.inbox_url.clone().into(), }; @@ -118,8 +116,7 @@ impl ApubObject for ApubPerson { async fn verify( person: &Person, expected_domain: &Url, - context: &LemmyContext, - _request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { let local_site_data = fetch_local_site_data(context.pool()).await?; let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); @@ -141,12 +138,11 @@ impl ApubObject for ApubPerson { } #[tracing::instrument(skip_all)] - async fn from_apub( + async fn from_json( person: Person, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result { - let instance_id = fetch_instance_actor_for_object(&person.id, context, request_counter).await?; + let instance_id = fetch_instance_actor_for_object(&person.id, context).await?; let person_form = PersonInsertForm { name: person.preferred_username, @@ -177,19 +173,17 @@ impl ApubObject for ApubPerson { } } -impl ActorType for ApubPerson { - fn actor_id(&self) -> Url { - self.actor_id.clone().into() +impl Actor for ApubPerson { + fn id(&self) -> Url { + self.actor_id.inner().clone() } - fn private_key(&self) -> Option { - self.private_key.clone() + fn public_key_pem(&self) -> &str { + &self.public_key } -} -impl Actor for ApubPerson { - fn public_key(&self) -> &str { - &self.public_key + fn private_key_pem(&self) -> Option { + self.private_key.clone() } fn inbox(&self) -> Url { @@ -211,21 +205,17 @@ pub(crate) mod tests { }, protocol::{objects::instance::Instance, tests::file_to_json_object}, }; + use activitypub_federation::fetch::object_id::ObjectId; use lemmy_db_schema::{source::site::Site, traits::Crud}; use serial_test::serial; - pub(crate) async fn parse_lemmy_person(context: &LemmyContext) -> (ApubPerson, ApubSite) { + pub(crate) async fn parse_lemmy_person(context: &Data) -> (ApubPerson, ApubSite) { let site = parse_lemmy_instance(context).await; let json = file_to_json_object("assets/lemmy/objects/person.json").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap(); - let mut request_counter = 0; - ApubPerson::verify(&json, &url, context, &mut request_counter) - .await - .unwrap(); - let person = ApubPerson::from_apub(json, context, &mut request_counter) - .await - .unwrap(); - assert_eq!(request_counter, 0); + ApubPerson::verify(&json, &url, context).await.unwrap(); + let person = ApubPerson::from_json(json, context).await.unwrap(); + assert_eq!(context.request_count(), 0); (person, site) } @@ -249,27 +239,19 @@ pub(crate) mod tests { // create and parse a fake pleroma instance actor, to avoid network request during test let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap(); - let id = Url::parse("https://queer.hacktivis.me/").unwrap(); - json.id = ObjectId::new(id); - let mut request_counter = 0; - let site = ApubSite::from_apub(json, &context, &mut request_counter) - .await - .unwrap(); + json.id = ObjectId::parse("https://queer.hacktivis.me/").unwrap(); + let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap(); + ApubSite::verify(&json, &url, &context).await.unwrap(); + let site = ApubSite::from_json(json, &context).await.unwrap(); let json = file_to_json_object("assets/pleroma/objects/person.json").unwrap(); - let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap(); - let mut request_counter = 0; - ApubPerson::verify(&json, &url, &context, &mut request_counter) - .await - .unwrap(); - let person = ApubPerson::from_apub(json, &context, &mut request_counter) - .await - .unwrap(); + ApubPerson::verify(&json, &url, &context).await.unwrap(); + let person = ApubPerson::from_json(json, &context).await.unwrap(); assert_eq!(person.actor_id, url.into()); assert_eq!(person.name, "lanodan"); assert!(!person.local); - assert_eq!(request_counter, 0); + assert_eq!(context.request_count(), 0); assert_eq!(person.bio.as_ref().unwrap().len(), 873); cleanup((person, site), &context).await; diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index dd7291bd..7bc93339 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -2,7 +2,6 @@ use crate::{ activities::{verify_is_public, verify_person_in_community}, check_apub_id_valid_with_strictness, fetch_local_site_data, - local_instance, objects::{read_from_string_or_source_opt, verify_is_remote_object}, protocol::{ objects::{ @@ -15,12 +14,11 @@ use crate::{ }, }; use activitypub_federation::{ - core::object_id::ObjectId, - deser::values::MediaTypeMarkdownOrHtml, - traits::ApubObject, - utils::verify_domains_match, + config::Data, + kinds::public, + protocol::{values::MediaTypeMarkdownOrHtml, verification::verify_domains_match}, + traits::Object, }; -use activitystreams_kinds::public; use anyhow::anyhow; use chrono::NaiveDateTime; use html2md::parse_html; @@ -69,11 +67,10 @@ impl From for ApubPost { } } -#[async_trait::async_trait(?Send)] -impl ApubObject for ApubPost { +#[async_trait::async_trait] +impl Object for ApubPost { type DataType = LemmyContext; - type ApubType = Page; - type DbType = Post; + type Kind = Page; type Error = LemmyError; fn last_refreshed_at(&self) -> Option { @@ -81,9 +78,9 @@ impl ApubObject for ApubPost { } #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - context: &LemmyContext, + context: &Data, ) -> Result, LemmyError> { Ok( Post::read_from_apub_id(context.pool(), object_id) @@ -93,7 +90,7 @@ impl ApubObject for ApubPost { } #[tracing::instrument(skip_all)] - async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { + async fn delete(self, context: &Data) -> Result<(), LemmyError> { if !self.deleted { let form = PostUpdateForm::builder().deleted(Some(true)).build(); Post::update(context.pool(), self.id, &form).await?; @@ -103,7 +100,7 @@ impl ApubObject for ApubPost { // Turn a Lemmy post into an ActivityPub page that can be sent out over the network. #[tracing::instrument(skip_all)] - async fn into_apub(self, context: &LemmyContext) -> Result { + async fn into_json(self, context: &Data) -> Result { let creator_id = self.creator_id; let creator = Person::read(context.pool(), creator_id).await?; let community_id = self.community_id; @@ -112,8 +109,8 @@ impl ApubObject for ApubPost { let page = Page { kind: PageType::Page, - id: ObjectId::new(self.ap_id.clone()), - attributed_to: AttributedTo::Lemmy(ObjectId::new(creator.actor_id)), + id: self.ap_id.clone().into(), + attributed_to: AttributedTo::Lemmy(creator.actor_id.into()), to: vec![community.actor_id.clone().into(), public()], cc: vec![], name: Some(self.name.clone()), @@ -128,7 +125,7 @@ impl ApubObject for ApubPost { language, published: Some(convert_datetime(self.published)), updated: self.updated.map(convert_datetime), - audience: Some(ObjectId::new(community.actor_id)), + audience: Some(community.actor_id.into()), in_reply_to: None, }; Ok(page) @@ -138,8 +135,7 @@ impl ApubObject for ApubPost { async fn verify( page: &Page, expected_domain: &Url, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { // We can't verify the domain in case of mod action, because the mod may be on a different // instance from the post author. @@ -150,14 +146,14 @@ impl ApubObject for ApubPost { let local_site_data = fetch_local_site_data(context.pool()).await?; - let community = page.community(context, request_counter).await?; + let community = page.community(context).await?; check_apub_id_valid_with_strictness( page.id.inner(), community.local, &local_site_data, context.settings(), )?; - verify_person_in_community(&page.creator()?, &community, context, request_counter).await?; + verify_person_in_community(&page.creator()?, &community, context).await?; let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); check_slurs_opt(&page.name, slur_regex)?; @@ -168,16 +164,9 @@ impl ApubObject for ApubPost { } #[tracing::instrument(skip_all)] - async fn from_apub( - page: Page, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let creator = page - .creator()? - .dereference(context, local_instance(context).await, request_counter) - .await?; - let community = page.community(context, request_counter).await?; + async fn from_json(page: Page, context: &Data) -> Result { + let creator = page.creator()?.dereference(context).await?; + let community = page.community(context).await?; if community.posting_restricted_to_mods { is_mod_or_admin(context.pool(), creator.id, community.id).await?; } @@ -257,9 +246,7 @@ impl ApubObject for ApubPost { .build() }; // read existing, local post if any (for generating mod log) - let old_post = ObjectId::::new(page.id.clone()) - .dereference_local(context) - .await; + let old_post = page.id.dereference_local(context).await; let post = Post::create(context.pool(), &form).await?; @@ -310,13 +297,8 @@ mod tests { let json = file_to_json_object("assets/lemmy/objects/page.json").unwrap(); let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap(); - let mut request_counter = 0; - ApubPost::verify(&json, &url, &context, &mut request_counter) - .await - .unwrap(); - let post = ApubPost::from_apub(json, &context, &mut request_counter) - .await - .unwrap(); + ApubPost::verify(&json, &url, &context).await.unwrap(); + let post = ApubPost::from_json(json, &context).await.unwrap(); assert_eq!(post.ap_id, url.into()); assert_eq!(post.name, "Post title"); @@ -324,7 +306,7 @@ mod tests { assert_eq!(post.body.as_ref().unwrap().len(), 45); assert!(!post.locked); assert!(post.featured_community); - assert_eq!(request_counter, 0); + assert_eq!(context.request_count(), 0); Post::delete(context.pool(), post.id).await.unwrap(); Person::delete(context.pool(), person.id).await.unwrap(); diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 233e8eaa..01f576ff 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -1,7 +1,6 @@ use crate::{ check_apub_id_valid_with_strictness, fetch_local_site_data, - local_instance, objects::read_from_string_or_source, protocol::{ objects::chat_message::{ChatMessage, ChatMessageType}, @@ -9,10 +8,9 @@ use crate::{ }, }; use activitypub_federation::{ - core::object_id::ObjectId, - deser::values::MediaTypeHtml, - traits::ApubObject, - utils::verify_domains_match, + config::Data, + protocol::{values::MediaTypeHtml, verification::verify_domains_match}, + traits::Object, }; use chrono::NaiveDateTime; use lemmy_api_common::{context::LemmyContext, utils::check_person_block}; @@ -46,11 +44,10 @@ impl From for ApubPrivateMessage { } } -#[async_trait::async_trait(?Send)] -impl ApubObject for ApubPrivateMessage { +#[async_trait::async_trait] +impl Object for ApubPrivateMessage { type DataType = LemmyContext; - type ApubType = ChatMessage; - type DbType = PrivateMessage; + type Kind = ChatMessage; type Error = LemmyError; fn last_refreshed_at(&self) -> Option { @@ -58,9 +55,9 @@ impl ApubObject for ApubPrivateMessage { } #[tracing::instrument(skip_all)] - async fn read_from_apub_id( + async fn read_from_id( object_id: Url, - context: &LemmyContext, + context: &Data, ) -> Result, LemmyError> { Ok( PrivateMessage::read_from_apub_id(context.pool(), object_id) @@ -69,13 +66,13 @@ impl ApubObject for ApubPrivateMessage { ) } - async fn delete(self, _context: &LemmyContext) -> Result<(), LemmyError> { + async fn delete(self, _context: &Data) -> Result<(), LemmyError> { // do nothing, because pm can't be fetched over http unimplemented!() } #[tracing::instrument(skip_all)] - async fn into_apub(self, context: &LemmyContext) -> Result { + async fn into_json(self, context: &Data) -> Result { let creator_id = self.creator_id; let creator = Person::read(context.pool(), creator_id).await?; @@ -84,9 +81,9 @@ impl ApubObject for ApubPrivateMessage { let note = ChatMessage { r#type: ChatMessageType::ChatMessage, - id: ObjectId::new(self.ap_id.clone()), - attributed_to: ObjectId::new(creator.actor_id), - to: [ObjectId::new(recipient.actor_id)], + id: self.ap_id.clone().into(), + attributed_to: creator.actor_id.into(), + to: [recipient.actor_id.into()], content: markdown_to_html(&self.content), media_type: Some(MediaTypeHtml::Html), source: Some(Source::new(self.content.clone())), @@ -100,8 +97,7 @@ impl ApubObject for ApubPrivateMessage { async fn verify( note: &ChatMessage, expected_domain: &Url, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(), LemmyError> { verify_domains_match(note.id.inner(), expected_domain)?; verify_domains_match(note.attributed_to.inner(), note.id.inner())?; @@ -114,10 +110,7 @@ impl ApubObject for ApubPrivateMessage { &local_site_data, context.settings(), )?; - let person = note - .attributed_to - .dereference(context, local_instance(context).await, request_counter) - .await?; + let person = note.attributed_to.dereference(context).await?; if person.banned { return Err(LemmyError::from_message("Person is banned from site")); } @@ -125,18 +118,12 @@ impl ApubObject for ApubPrivateMessage { } #[tracing::instrument(skip_all)] - async fn from_apub( + async fn from_json( note: ChatMessage, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result { - let creator = note - .attributed_to - .dereference(context, local_instance(context).await, request_counter) - .await?; - let recipient = note.to[0] - .dereference(context, local_instance(context).await, request_counter) - .await?; + let creator = note.attributed_to.dereference(context).await?; + let recipient = note.to[0].dereference(context).await?; check_person_block(creator.id, recipient.id, context.pool()).await?; let form = PrivateMessageInsertForm { @@ -172,28 +159,29 @@ mod tests { async fn prepare_comment_test( url: &Url, - context: &LemmyContext, + context: &Data, ) -> (ApubPerson, ApubPerson, ApubSite) { + let context2 = context.reset_request_count(); let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json").unwrap(); - let site = parse_lemmy_instance(context).await; - ApubPerson::verify(&lemmy_person, url, context, &mut 0) + let site = parse_lemmy_instance(&context2).await; + ApubPerson::verify(&lemmy_person, url, &context2) .await .unwrap(); - let person1 = ApubPerson::from_apub(lemmy_person, context, &mut 0) + let person1 = ApubPerson::from_json(lemmy_person, &context2) .await .unwrap(); let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json").unwrap(); let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap(); - ApubPerson::verify(&pleroma_person, &pleroma_url, context, &mut 0) + ApubPerson::verify(&pleroma_person, &pleroma_url, &context2) .await .unwrap(); - let person2 = ApubPerson::from_apub(pleroma_person, context, &mut 0) + let person2 = ApubPerson::from_json(pleroma_person, &context2) .await .unwrap(); (person1, person2, site) } - async fn cleanup(data: (ApubPerson, ApubPerson, ApubSite), context: &LemmyContext) { + async fn cleanup(data: (ApubPerson, ApubPerson, ApubSite), context: &Data) { Person::delete(context.pool(), data.0.id).await.unwrap(); Person::delete(context.pool(), data.1.id).await.unwrap(); Site::delete(context.pool(), data.2.id).await.unwrap(); @@ -206,20 +194,19 @@ mod tests { let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap(); let data = prepare_comment_test(&url, &context).await; let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json").unwrap(); - let mut request_counter = 0; - ApubPrivateMessage::verify(&json, &url, &context, &mut request_counter) + ApubPrivateMessage::verify(&json, &url, &context) .await .unwrap(); - let pm = ApubPrivateMessage::from_apub(json.clone(), &context, &mut request_counter) + let pm = ApubPrivateMessage::from_json(json.clone(), &context) .await .unwrap(); assert_eq!(pm.ap_id.clone(), url.into()); assert_eq!(pm.content.len(), 20); - assert_eq!(request_counter, 0); + assert_eq!(context.request_count(), 0); let pm_id = pm.id; - let to_apub = pm.into_apub(&context).await.unwrap(); + let to_apub = pm.into_json(&context).await.unwrap(); assert_json_include!(actual: json, expected: to_apub); PrivateMessage::delete(context.pool(), pm_id).await.unwrap(); @@ -234,17 +221,14 @@ mod tests { let data = prepare_comment_test(&url, &context).await; let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2").unwrap(); let json = file_to_json_object("assets/pleroma/objects/chat_message.json").unwrap(); - let mut request_counter = 0; - ApubPrivateMessage::verify(&json, &pleroma_url, &context, &mut request_counter) - .await - .unwrap(); - let pm = ApubPrivateMessage::from_apub(json, &context, &mut request_counter) + ApubPrivateMessage::verify(&json, &pleroma_url, &context) .await .unwrap(); + let pm = ApubPrivateMessage::from_json(json, &context).await.unwrap(); assert_eq!(pm.ap_id, pleroma_url.into()); assert_eq!(pm.content.len(), 3); - assert_eq!(request_counter, 0); + assert_eq!(context.request_count(), 0); PrivateMessage::delete(context.pool(), pm.id).await.unwrap(); cleanup(data, &context).await; diff --git a/crates/apub/src/protocol/activities/block/block_user.rs b/crates/apub/src/protocol/activities/block/block_user.rs index 0812489b..2cad2adc 100644 --- a/crates/apub/src/protocol/activities/block/block_user.rs +++ b/crates/apub/src/protocol/activities/block/block_user.rs @@ -1,11 +1,14 @@ use crate::{ activities::{block::SiteOrCommunity, verify_community_matches}, - local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::BlockType; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + kinds::activity::BlockType, + protocol::helpers::deserialize_one_or_many, +}; use anyhow::anyhow; use chrono::{DateTime, FixedOffset}; use lemmy_api_common::context::LemmyContext; @@ -38,17 +41,10 @@ pub struct BlockUser { pub(crate) expires: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for BlockUser { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let target = self - .target - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn community(&self, context: &Data) -> Result { + let target = self.target.dereference(context).await?; let community = match target { SiteOrCommunity::Community(c) => c, SiteOrCommunity::Site(_) => return Err(anyhow!("activity is not in community").into()), diff --git a/crates/apub/src/protocol/activities/block/undo_block_user.rs b/crates/apub/src/protocol/activities/block/undo_block_user.rs index 9466fedc..758d3fd4 100644 --- a/crates/apub/src/protocol/activities/block/undo_block_user.rs +++ b/crates/apub/src/protocol/activities/block/undo_block_user.rs @@ -3,8 +3,12 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::block::block_user::BlockUser, InCommunity}, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::UndoType; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + kinds::activity::UndoType, + protocol::helpers::deserialize_one_or_many, +}; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; @@ -27,14 +31,10 @@ pub struct UndoBlockUser { pub(crate) audience: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for UndoBlockUser { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let community = self.object.community(context, request_counter).await?; + async fn community(&self, context: &Data) -> Result { + let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/community/announce.rs b/crates/apub/src/protocol/activities/community/announce.rs index 7c2e16ec..e149e5fd 100644 --- a/crates/apub/src/protocol/activities/community/announce.rs +++ b/crates/apub/src/protocol/activities/community/announce.rs @@ -1,6 +1,9 @@ use crate::{objects::community::ApubCommunity, protocol::IdOrNestedObject}; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::AnnounceType; +use activitypub_federation::{ + fetch::object_id::ObjectId, + kinds::activity::AnnounceType, + protocol::helpers::deserialize_one_or_many, +}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use url::Url; diff --git a/crates/apub/src/protocol/activities/community/collection_add.rs b/crates/apub/src/protocol/activities/community/collection_add.rs index a9124418..f3943e31 100644 --- a/crates/apub/src/protocol/activities/community/collection_add.rs +++ b/crates/apub/src/protocol/activities/community/collection_add.rs @@ -3,8 +3,12 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::AddType; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + kinds::activity::AddType, + protocol::helpers::deserialize_one_or_many, +}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::community::Community; use lemmy_utils::error::LemmyError; @@ -27,13 +31,9 @@ pub struct CollectionAdd { pub(crate) audience: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for CollectionAdd { - async fn community( - &self, - context: &LemmyContext, - _request_counter: &mut i32, - ) -> Result { + async fn community(&self, context: &Data) -> Result { let (community, _) = Community::get_by_collection_url(context.pool(), &self.clone().target.into()).await?; if let Some(audience) = &self.audience { diff --git a/crates/apub/src/protocol/activities/community/collection_remove.rs b/crates/apub/src/protocol/activities/community/collection_remove.rs index 6ca8a239..f69fdc6a 100644 --- a/crates/apub/src/protocol/activities/community/collection_remove.rs +++ b/crates/apub/src/protocol/activities/community/collection_remove.rs @@ -3,8 +3,12 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::RemoveType; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + kinds::activity::RemoveType, + protocol::helpers::deserialize_one_or_many, +}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::source::community::Community; use lemmy_utils::error::LemmyError; @@ -17,7 +21,7 @@ pub struct CollectionRemove { pub(crate) actor: ObjectId, #[serde(deserialize_with = "deserialize_one_or_many")] pub(crate) to: Vec, - pub(crate) object: ObjectId, + pub(crate) object: Url, #[serde(deserialize_with = "deserialize_one_or_many")] pub(crate) cc: Vec, #[serde(rename = "type")] @@ -27,13 +31,9 @@ pub struct CollectionRemove { pub(crate) audience: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for CollectionRemove { - async fn community( - &self, - context: &LemmyContext, - _request_counter: &mut i32, - ) -> Result { + async fn community(&self, context: &Data) -> Result { let (community, _) = Community::get_by_collection_url(context.pool(), &self.clone().target.into()).await?; if let Some(audience) = &self.audience { diff --git a/crates/apub/src/protocol/activities/community/lock_page.rs b/crates/apub/src/protocol/activities/community/lock_page.rs index f7b5554c..b19e3974 100644 --- a/crates/apub/src/protocol/activities/community/lock_page.rs +++ b/crates/apub/src/protocol/activities/community/lock_page.rs @@ -1,11 +1,14 @@ use crate::{ activities::verify_community_matches, - local_instance, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::InCommunity, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::UndoType; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + kinds::activity::UndoType, + protocol::helpers::deserialize_one_or_many, +}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{source::community::Community, traits::Crud}; use lemmy_utils::error::LemmyError; @@ -48,17 +51,10 @@ pub struct UndoLockPage { pub(crate) audience: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for LockPage { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let post = self - .object - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn community(&self, context: &Data) -> Result { + let post = self.object.dereference(context).await?; let community = Community::read(context.pool(), post.community_id).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; @@ -67,14 +63,10 @@ impl InCommunity for LockPage { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for UndoLockPage { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let community = self.object.community(context, request_counter).await?; + async fn community(&self, context: &Data) -> Result { + let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/community/report.rs b/crates/apub/src/protocol/activities/community/report.rs index 4d786b39..6e8a1bbf 100644 --- a/crates/apub/src/protocol/activities/community/report.rs +++ b/crates/apub/src/protocol/activities/community/report.rs @@ -1,12 +1,15 @@ use crate::{ activities::verify_community_matches, fetcher::post_or_comment::PostOrComment, - local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one}; -use activitystreams_kinds::activity::FlagType; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + kinds::activity::FlagType, + protocol::helpers::deserialize_one, +}; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; @@ -26,16 +29,10 @@ pub struct Report { pub(crate) audience: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for Report { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let community = self.to[0] - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn community(&self, context: &Data) -> Result { + let community = self.to[0].dereference(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/community/update.rs b/crates/apub/src/protocol/activities/community/update.rs index a8934717..49ec1f5d 100644 --- a/crates/apub/src/protocol/activities/community/update.rs +++ b/crates/apub/src/protocol/activities/community/update.rs @@ -1,11 +1,14 @@ use crate::{ activities::verify_community_matches, - local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{objects::group::Group, InCommunity}, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::UpdateType; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + kinds::activity::UpdateType, + protocol::helpers::deserialize_one_or_many, +}; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; @@ -29,16 +32,10 @@ pub struct UpdateCommunity { pub(crate) audience: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for UpdateCommunity { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let community: ApubCommunity = ObjectId::new(self.object.id.clone()) - .dereference(context, local_instance(context).await, request_counter) - .await?; + async fn community(&self, context: &Data) -> Result { + let community: ApubCommunity = self.object.id.clone().dereference(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/create_or_update/chat_message.rs b/crates/apub/src/protocol/activities/create_or_update/chat_message.rs index 07a71d25..30e94a28 100644 --- a/crates/apub/src/protocol/activities/create_or_update/chat_message.rs +++ b/crates/apub/src/protocol/activities/create_or_update/chat_message.rs @@ -2,7 +2,7 @@ use crate::{ objects::person::ApubPerson, protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage}, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one}; +use activitypub_federation::{fetch::object_id::ObjectId, protocol::helpers::deserialize_one}; use serde::{Deserialize, Serialize}; use url::Url; diff --git a/crates/apub/src/protocol/activities/create_or_update/note.rs b/crates/apub/src/protocol/activities/create_or_update/note.rs index 2cfcd2d8..07bbc7c8 100644 --- a/crates/apub/src/protocol/activities/create_or_update/note.rs +++ b/crates/apub/src/protocol/activities/create_or_update/note.rs @@ -4,7 +4,11 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::CreateOrUpdateType, objects::note::Note, InCommunity}, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + protocol::helpers::deserialize_one_or_many, +}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{source::community::Community, traits::Crud}; use lemmy_utils::error::LemmyError; @@ -28,14 +32,10 @@ pub struct CreateOrUpdateNote { pub(crate) audience: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for CreateOrUpdateNote { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let post = self.object.get_parents(context, request_counter).await?.0; + async fn community(&self, context: &Data) -> Result { + let post = self.object.get_parents(context).await?.0; let community = Community::read(context.pool(), post.community_id).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/activities/create_or_update/page.rs b/crates/apub/src/protocol/activities/create_or_update/page.rs index ad17d738..ec64c31a 100644 --- a/crates/apub/src/protocol/activities/create_or_update/page.rs +++ b/crates/apub/src/protocol/activities/create_or_update/page.rs @@ -3,7 +3,11 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::CreateOrUpdateType, objects::page::Page, InCommunity}, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + protocol::helpers::deserialize_one_or_many, +}; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; @@ -24,14 +28,10 @@ pub struct CreateOrUpdatePage { pub(crate) audience: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for CreateOrUpdatePage { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let community = self.object.community(context, request_counter).await?; + async fn community(&self, context: &Data) -> Result { + let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/deletion/delete.rs b/crates/apub/src/protocol/activities/deletion/delete.rs index 162e595f..89ee26d9 100644 --- a/crates/apub/src/protocol/activities/deletion/delete.rs +++ b/crates/apub/src/protocol/activities/deletion/delete.rs @@ -3,8 +3,12 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::{objects::tombstone::Tombstone, IdOrNestedObject, InCommunity}, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::DeleteType; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + kinds::activity::DeleteType, + protocol::helpers::deserialize_one_or_many, +}; use anyhow::anyhow; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ @@ -38,13 +42,9 @@ pub struct Delete { pub(crate) summary: Option, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for Delete { - async fn community( - &self, - context: &LemmyContext, - _request_counter: &mut i32, - ) -> Result { + async fn community(&self, context: &Data) -> Result { let community_id = match DeletableObjects::read_from_db(self.object.id(), context).await? { DeletableObjects::Community(c) => c.id, DeletableObjects::Comment(c) => { diff --git a/crates/apub/src/protocol/activities/deletion/delete_user.rs b/crates/apub/src/protocol/activities/deletion/delete_user.rs index d455be99..f5f9908d 100644 --- a/crates/apub/src/protocol/activities/deletion/delete_user.rs +++ b/crates/apub/src/protocol/activities/deletion/delete_user.rs @@ -1,6 +1,9 @@ use crate::objects::person::ApubPerson; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::DeleteType; +use activitypub_federation::{ + fetch::object_id::ObjectId, + kinds::activity::DeleteType, + protocol::helpers::deserialize_one_or_many, +}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use url::Url; diff --git a/crates/apub/src/protocol/activities/deletion/undo_delete.rs b/crates/apub/src/protocol/activities/deletion/undo_delete.rs index 6c584ccf..35d9951b 100644 --- a/crates/apub/src/protocol/activities/deletion/undo_delete.rs +++ b/crates/apub/src/protocol/activities/deletion/undo_delete.rs @@ -3,8 +3,12 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::deletion::delete::Delete, InCommunity}, }; -use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; -use activitystreams_kinds::activity::UndoType; +use activitypub_federation::{ + config::Data, + fetch::object_id::ObjectId, + kinds::activity::UndoType, + protocol::helpers::deserialize_one_or_many, +}; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; @@ -29,14 +33,10 @@ pub struct UndoDelete { pub(crate) cc: Vec, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for UndoDelete { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let community = self.object.community(context, request_counter).await?; + async fn community(&self, context: &Data) -> Result { + let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/following/accept.rs b/crates/apub/src/protocol/activities/following/accept.rs index bc65ea91..786827ed 100644 --- a/crates/apub/src/protocol/activities/following/accept.rs +++ b/crates/apub/src/protocol/activities/following/accept.rs @@ -1,6 +1,5 @@ use crate::{objects::community::ApubCommunity, protocol::activities::following::follow::Follow}; -use activitypub_federation::core::object_id::ObjectId; -use activitystreams_kinds::activity::AcceptType; +use activitypub_federation::{fetch::object_id::ObjectId, kinds::activity::AcceptType}; use serde::{Deserialize, Serialize}; use url::Url; diff --git a/crates/apub/src/protocol/activities/following/follow.rs b/crates/apub/src/protocol/activities/following/follow.rs index d3f4fb59..27a3ba4b 100644 --- a/crates/apub/src/protocol/activities/following/follow.rs +++ b/crates/apub/src/protocol/activities/following/follow.rs @@ -1,6 +1,5 @@ use crate::{fetcher::user_or_community::UserOrCommunity, objects::person::ApubPerson}; -use activitypub_federation::core::object_id::ObjectId; -use activitystreams_kinds::activity::FollowType; +use activitypub_federation::{fetch::object_id::ObjectId, kinds::activity::FollowType}; use serde::{Deserialize, Serialize}; use url::Url; diff --git a/crates/apub/src/protocol/activities/following/undo_follow.rs b/crates/apub/src/protocol/activities/following/undo_follow.rs index 33308c24..4660db40 100644 --- a/crates/apub/src/protocol/activities/following/undo_follow.rs +++ b/crates/apub/src/protocol/activities/following/undo_follow.rs @@ -1,6 +1,5 @@ use crate::{objects::person::ApubPerson, protocol::activities::following::follow::Follow}; -use activitypub_federation::core::object_id::ObjectId; -use activitystreams_kinds::activity::UndoType; +use activitypub_federation::{fetch::object_id::ObjectId, kinds::activity::UndoType}; use serde::{Deserialize, Serialize}; use url::Url; diff --git a/crates/apub/src/protocol/activities/voting/undo_vote.rs b/crates/apub/src/protocol/activities/voting/undo_vote.rs index a8432c82..746ae68d 100644 --- a/crates/apub/src/protocol/activities/voting/undo_vote.rs +++ b/crates/apub/src/protocol/activities/voting/undo_vote.rs @@ -3,8 +3,7 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::voting::vote::Vote, InCommunity}, }; -use activitypub_federation::core::object_id::ObjectId; -use activitystreams_kinds::activity::UndoType; +use activitypub_federation::{config::Data, fetch::object_id::ObjectId, kinds::activity::UndoType}; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; @@ -21,14 +20,10 @@ pub struct UndoVote { pub(crate) audience: Option>, } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for UndoVote { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let community = self.object.community(context, request_counter).await?; + async fn community(&self, context: &Data) -> Result { + let community = self.object.community(context).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/apub/src/protocol/activities/voting/vote.rs b/crates/apub/src/protocol/activities/voting/vote.rs index cf79fc9a..4450fed8 100644 --- a/crates/apub/src/protocol/activities/voting/vote.rs +++ b/crates/apub/src/protocol/activities/voting/vote.rs @@ -1,11 +1,10 @@ use crate::{ activities::verify_community_matches, fetcher::post_or_comment::PostOrComment, - local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, }; -use activitypub_federation::core::object_id::ObjectId; +use activitypub_federation::{config::Data, fetch::object_id::ObjectId}; use lemmy_api_common::context::LemmyContext; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; @@ -51,19 +50,14 @@ impl From<&VoteType> for i16 { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for Vote { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let local_instance = local_instance(context).await; + async fn community(&self, context: &Data) -> Result { let community = self .object - .dereference(context, local_instance, request_counter) + .dereference(context) .await? - .community(context, request_counter) + .community(context) .await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/collections/empty_outbox.rs b/crates/apub/src/protocol/collections/empty_outbox.rs index b4731f31..3801c04e 100644 --- a/crates/apub/src/protocol/collections/empty_outbox.rs +++ b/crates/apub/src/protocol/collections/empty_outbox.rs @@ -1,4 +1,4 @@ -use activitystreams_kinds::collection::OrderedCollectionType; +use activitypub_federation::kinds::collection::OrderedCollectionType; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; use url::Url; diff --git a/crates/apub/src/protocol/collections/group_featured.rs b/crates/apub/src/protocol/collections/group_featured.rs index cbd7dce4..a36b5291 100644 --- a/crates/apub/src/protocol/collections/group_featured.rs +++ b/crates/apub/src/protocol/collections/group_featured.rs @@ -1,5 +1,5 @@ use crate::protocol::objects::page::Page; -use activitystreams_kinds::collection::OrderedCollectionType; +use activitypub_federation::kinds::collection::OrderedCollectionType; use serde::{Deserialize, Serialize}; use url::Url; diff --git a/crates/apub/src/protocol/collections/group_followers.rs b/crates/apub/src/protocol/collections/group_followers.rs index b9cf8518..c7df8df2 100644 --- a/crates/apub/src/protocol/collections/group_followers.rs +++ b/crates/apub/src/protocol/collections/group_followers.rs @@ -1,4 +1,4 @@ -use activitystreams_kinds::collection::CollectionType; +use activitypub_federation::kinds::collection::CollectionType; use lemmy_api_common::{context::LemmyContext, utils::generate_followers_url}; use lemmy_db_schema::source::community::Community; use lemmy_db_views_actor::structs::CommunityFollowerView; diff --git a/crates/apub/src/protocol/collections/group_moderators.rs b/crates/apub/src/protocol/collections/group_moderators.rs index 4d104282..3e41e124 100644 --- a/crates/apub/src/protocol/collections/group_moderators.rs +++ b/crates/apub/src/protocol/collections/group_moderators.rs @@ -1,6 +1,8 @@ use crate::objects::person::ApubPerson; -use activitypub_federation::core::object_id::ObjectId; -use activitystreams_kinds::collection::OrderedCollectionType; +use activitypub_federation::{ + fetch::object_id::ObjectId, + kinds::collection::OrderedCollectionType, +}; use serde::{Deserialize, Serialize}; use url::Url; diff --git a/crates/apub/src/protocol/collections/group_outbox.rs b/crates/apub/src/protocol/collections/group_outbox.rs index 2806d269..77e82348 100644 --- a/crates/apub/src/protocol/collections/group_outbox.rs +++ b/crates/apub/src/protocol/collections/group_outbox.rs @@ -1,5 +1,5 @@ use crate::protocol::activities::community::announce::AnnounceActivity; -use activitystreams_kinds::collection::OrderedCollectionType; +use activitypub_federation::kinds::collection::OrderedCollectionType; use serde::{Deserialize, Serialize}; use url::Url; diff --git a/crates/apub/src/protocol/mod.rs b/crates/apub/src/protocol/mod.rs index ef0f2a87..bfc9df77 100644 --- a/crates/apub/src/protocol/mod.rs +++ b/crates/apub/src/protocol/mod.rs @@ -1,6 +1,10 @@ -use crate::{local_instance, objects::community::ApubCommunity}; -use activitypub_federation::{deser::values::MediaTypeMarkdown, utils::fetch_object_http}; -use activitystreams_kinds::object::ImageType; +use crate::objects::community::ApubCommunity; +use activitypub_federation::{ + config::Data, + fetch::fetch_object_http, + kinds::object::ImageType, + protocol::values::MediaTypeMarkdown, +}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::newtypes::DbUrl; use lemmy_utils::error::LemmyError; @@ -60,41 +64,32 @@ pub(crate) enum IdOrNestedObject { NestedObject(Kind), } -impl IdOrNestedObject { +impl IdOrNestedObject { pub(crate) fn id(&self) -> &Url { match self { IdOrNestedObject::Id(i) => i, IdOrNestedObject::NestedObject(n) => n.object_id(), } } - pub(crate) async fn object( - self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { + pub(crate) async fn object(self, context: &Data) -> Result { match self { - IdOrNestedObject::Id(i) => { - Ok(fetch_object_http(&i, local_instance(context).await, request_counter).await?) - } + // TODO: move IdOrNestedObject struct to library and make fetch_object_http private + IdOrNestedObject::Id(i) => Ok(fetch_object_http(&i, context).await?), IdOrNestedObject::NestedObject(o) => Ok(o), } } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] pub trait InCommunity { // TODO: after we use audience field and remove backwards compat, it should be possible to change // this to simply `fn community(&self) -> Result, LemmyError>` - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result; + async fn community(&self, context: &Data) -> Result; } #[cfg(test)] pub(crate) mod tests { - use activitypub_federation::deser::context::WithContext; + use activitypub_federation::protocol::context::WithContext; use assert_json_diff::assert_json_include; use lemmy_utils::error::LemmyError; use serde::{de::DeserializeOwned, Serialize}; diff --git a/crates/apub/src/protocol/objects/chat_message.rs b/crates/apub/src/protocol/objects/chat_message.rs index 152e8508..22bdd916 100644 --- a/crates/apub/src/protocol/objects/chat_message.rs +++ b/crates/apub/src/protocol/objects/chat_message.rs @@ -3,8 +3,8 @@ use crate::{ protocol::Source, }; use activitypub_federation::{ - core::object_id::ObjectId, - deser::{ + fetch::object_id::ObjectId, + protocol::{ helpers::{deserialize_one, deserialize_skip_error}, values::MediaTypeHtml, }, diff --git a/crates/apub/src/protocol/objects/group.rs b/crates/apub/src/protocol/objects/group.rs index 9c4aa35d..520070cc 100644 --- a/crates/apub/src/protocol/objects/group.rs +++ b/crates/apub/src/protocol/objects/group.rs @@ -14,11 +14,14 @@ use crate::{ }, }; use activitypub_federation::{ - core::{object_id::ObjectId, signatures::PublicKey}, - deser::helpers::deserialize_skip_error, - utils::verify_domains_match, + fetch::{collection_id::CollectionId, object_id::ObjectId}, + kinds::actor::GroupType, + protocol::{ + helpers::deserialize_skip_error, + public_key::PublicKey, + verification::verify_domains_match, + }, }; -use activitystreams_kinds::actor::GroupType; use chrono::{DateTime, FixedOffset}; use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex}; use lemmy_db_schema::{ @@ -59,14 +62,14 @@ pub struct Group { // lemmy extension pub(crate) sensitive: Option, // deprecated, use attributed_to instead - pub(crate) moderators: Option>, + pub(crate) moderators: Option>, #[serde(deserialize_with = "deserialize_skip_error", default)] - pub(crate) attributed_to: Option>, + pub(crate) attributed_to: Option>, // lemmy extension pub(crate) posting_restricted_to_mods: Option, - pub(crate) outbox: ObjectId, + pub(crate) outbox: CollectionId, pub(crate) endpoints: Option, - pub(crate) featured: Option>, + pub(crate) featured: Option>, #[serde(default)] pub(crate) language: Vec, pub(crate) published: Option>, diff --git a/crates/apub/src/protocol/objects/instance.rs b/crates/apub/src/protocol/objects/instance.rs index 00c9c8be..4a3d6ea2 100644 --- a/crates/apub/src/protocol/objects/instance.rs +++ b/crates/apub/src/protocol/objects/instance.rs @@ -3,10 +3,10 @@ use crate::{ protocol::{objects::LanguageTag, ImageObject, Source}, }; use activitypub_federation::{ - core::{object_id::ObjectId, signatures::PublicKey}, - deser::{helpers::deserialize_skip_error, values::MediaTypeHtml}, + fetch::object_id::ObjectId, + kinds::actor::ApplicationType, + protocol::{helpers::deserialize_skip_error, public_key::PublicKey, values::MediaTypeHtml}, }; -use activitystreams_kinds::actor::ApplicationType; use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; diff --git a/crates/apub/src/protocol/objects/note.rs b/crates/apub/src/protocol/objects/note.rs index f93a41ef..de90636e 100644 --- a/crates/apub/src/protocol/objects/note.rs +++ b/crates/apub/src/protocol/objects/note.rs @@ -1,19 +1,19 @@ use crate::{ activities::verify_community_matches, fetcher::post_or_comment::PostOrComment, - local_instance, mentions::MentionOrValue, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::{objects::LanguageTag, InCommunity, Source}, }; use activitypub_federation::{ - core::object_id::ObjectId, - deser::{ + config::Data, + fetch::object_id::ObjectId, + kinds::object::NoteType, + protocol::{ helpers::{deserialize_one_or_many, deserialize_skip_error}, values::MediaTypeMarkdownOrHtml, }, }; -use activitystreams_kinds::object::NoteType; use chrono::{DateTime, FixedOffset}; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ @@ -56,16 +56,10 @@ pub struct Note { impl Note { pub(crate) async fn get_parents( &self, - context: &LemmyContext, - request_counter: &mut i32, + context: &Data, ) -> Result<(ApubPost, Option), LemmyError> { // Fetch parent comment chain in a box, otherwise it can cause a stack overflow. - let parent = Box::pin( - self - .in_reply_to - .dereference(context, local_instance(context).await, request_counter) - .await?, - ); + let parent = Box::pin(self.in_reply_to.dereference(context).await?); match parent.deref() { PostOrComment::Post(p) => Ok((p.clone(), None)), PostOrComment::Comment(c) => { @@ -77,14 +71,10 @@ impl Note { } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for Note { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let (post, _) = self.get_parents(context, request_counter).await?; + async fn community(&self, context: &Data) -> Result { + let (post, _) = self.get_parents(context).await?; let community = Community::read(context.pool(), post.community_id).await?; if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; diff --git a/crates/apub/src/protocol/objects/page.rs b/crates/apub/src/protocol/objects/page.rs index 65a164ec..9bf8dbe5 100644 --- a/crates/apub/src/protocol/objects/page.rs +++ b/crates/apub/src/protocol/objects/page.rs @@ -1,22 +1,21 @@ use crate::{ activities::verify_community_matches, fetcher::user_or_community::{PersonOrGroupType, UserOrCommunity}, - local_instance, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::{objects::LanguageTag, ImageObject, InCommunity, Source}, }; use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, - deser::{ + config::Data, + fetch::object_id::ObjectId, + kinds::{ + link::LinkType, + object::{DocumentType, ImageType}, + }, + protocol::{ helpers::{deserialize_one_or_many, deserialize_skip_error}, values::MediaTypeMarkdownOrHtml, }, - traits::{ActivityHandler, ApubObject}, -}; -use activitystreams_kinds::{ - link::LinkType, - object::{DocumentType, ImageType}, + traits::{ActivityHandler, Object}, }; use chrono::{DateTime, FixedOffset}; use itertools::Itertools; @@ -136,10 +135,11 @@ impl Page { /// the current value, it is a mod action and needs to be verified as such. /// /// Both stickied and locked need to be false on a newly created post (verified in [[CreatePost]]. - pub(crate) async fn is_mod_action(&self, context: &LemmyContext) -> Result { - let old_post = ObjectId::::new(self.id.clone()) - .dereference_local(context) - .await; + pub(crate) async fn is_mod_action( + &self, + context: &Data, + ) -> Result { + let old_post = self.id.clone().dereference_local(context).await; let featured_changed = Page::is_featured_changed(&old_post, &self.stickied); let locked_changed = Page::is_locked_changed(&old_post, &self.comments_enabled); @@ -178,7 +178,7 @@ impl Page { AttributedTo::Peertube(p) => p .iter() .find(|a| a.kind == PersonOrGroupType::Person) - .map(|a| ObjectId::::new(a.id.clone().into_inner())) + .map(|a| ObjectId::::from(a.id.clone().into_inner())) .ok_or_else(|| LemmyError::from_message("page does not specify creator person")), } } @@ -194,7 +194,7 @@ impl Attachment { } // Used for community outbox, so that it can be compatible with Pleroma/Mastodon. -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl ActivityHandler for Page { type DataType = LemmyContext; type Error = LemmyError; @@ -204,38 +204,25 @@ impl ActivityHandler for Page { fn actor(&self) -> &Url { unimplemented!() } - async fn verify( - &self, - data: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - ApubPost::verify(self, self.id.inner(), data, request_counter).await + async fn verify(&self, data: &Data) -> Result<(), LemmyError> { + ApubPost::verify(self, self.id.inner(), data).await } - async fn receive( - self, - data: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - ApubPost::from_apub(self, data, request_counter).await?; + async fn receive(self, data: &Data) -> Result<(), LemmyError> { + ApubPost::from_json(self, data).await?; Ok(()) } } -#[async_trait::async_trait(?Send)] +#[async_trait::async_trait] impl InCommunity for Page { - async fn community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let instance = local_instance(context).await; + async fn community(&self, context: &Data) -> Result { let community = match &self.attributed_to { AttributedTo::Lemmy(_) => { let mut iter = self.to.iter().merge(self.cc.iter()); loop { if let Some(cid) = iter.next() { - let cid = ObjectId::new(cid.clone()); - if let Ok(c) = cid.dereference(context, instance, request_counter).await { + let cid = ObjectId::from(cid.clone()); + if let Ok(c) = cid.dereference(context).await { break c; } } else { @@ -246,9 +233,9 @@ impl InCommunity for Page { AttributedTo::Peertube(p) => { p.iter() .find(|a| a.kind == PersonOrGroupType::Group) - .map(|a| ObjectId::::new(a.id.clone().into_inner())) + .map(|a| ObjectId::::from(a.id.clone().into_inner())) .ok_or_else(|| LemmyError::from_message("page does not specify group"))? - .dereference(context, instance, request_counter) + .dereference(context) .await? } }; diff --git a/crates/apub/src/protocol/objects/person.rs b/crates/apub/src/protocol/objects/person.rs index eb473250..ecedbcd7 100644 --- a/crates/apub/src/protocol/objects/person.rs +++ b/crates/apub/src/protocol/objects/person.rs @@ -3,8 +3,8 @@ use crate::{ protocol::{objects::Endpoints, ImageObject, Source}, }; use activitypub_federation::{ - core::{object_id::ObjectId, signatures::PublicKey}, - deser::helpers::deserialize_skip_error, + fetch::object_id::ObjectId, + protocol::{helpers::deserialize_skip_error, public_key::PublicKey}, }; use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; diff --git a/crates/apub/src/protocol/objects/tombstone.rs b/crates/apub/src/protocol/objects/tombstone.rs index 1b66f2a7..f07a7e93 100644 --- a/crates/apub/src/protocol/objects/tombstone.rs +++ b/crates/apub/src/protocol/objects/tombstone.rs @@ -1,5 +1,5 @@ use crate::protocol::Id; -use activitystreams_kinds::object::TombstoneType; +use activitypub_federation::kinds::object::TombstoneType; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use url::Url; diff --git a/crates/db_schema/src/impls/activity.rs b/crates/db_schema/src/impls/activity.rs index bdf0f1dc..a19d20f3 100644 --- a/crates/db_schema/src/impls/activity.rs +++ b/crates/db_schema/src/impls/activity.rs @@ -5,14 +5,8 @@ use crate::{ traits::Crud, utils::{get_conn, DbPool}, }; -use diesel::{ - dsl::insert_into, - result::{DatabaseErrorKind, Error}, - ExpressionMethods, - QueryDsl, -}; +use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; -use serde_json::Value; #[async_trait] impl Crud for Activity { @@ -52,33 +46,6 @@ impl Crud for Activity { } impl Activity { - /// Returns true if the insert was successful - // TODO this should probably just be changed to an upsert on_conflict, rather than an error - pub async fn insert( - pool: &DbPool, - ap_id_: DbUrl, - data_: Value, - local_: bool, - sensitive_: Option, - ) -> Result { - let activity_form = ActivityInsertForm { - ap_id: ap_id_, - data: data_, - local: Some(local_), - sensitive: sensitive_, - updated: None, - }; - match Activity::create(pool, &activity_form).await { - Ok(_) => Ok(true), - Err(e) => { - if let Error::DatabaseError(DatabaseErrorKind::UniqueViolation, _) = e { - return Ok(false); - } - Err(e) - } - } - } - pub async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result { let conn = &mut get_conn(pool).await?; activity diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs index da2c9b9e..97b0926a 100644 --- a/crates/db_schema/src/newtypes.rs +++ b/crates/db_schema/src/newtypes.rs @@ -1,5 +1,10 @@ #[cfg(feature = "full")] -use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject}; +use activitypub_federation::{ + fetch::collection_id::CollectionId, + fetch::object_id::ObjectId, + traits::Collection, + traits::Object, +}; #[cfg(feature = "full")] use diesel_ltree::Ltree; use serde::{Deserialize, Serialize}; @@ -118,6 +123,12 @@ pub struct CustomEmojiId(i32); #[cfg_attr(feature = "full", diesel(sql_type = diesel::sql_types::Text))] pub struct DbUrl(pub(crate) Box); +impl DbUrl { + pub fn inner(&self) -> &Url { + &self.0 + } +} + #[cfg(feature = "full")] #[derive(Serialize, Deserialize)] #[serde(remote = "Ltree")] @@ -143,14 +154,40 @@ impl Into for DbUrl { *self.0 } } + #[cfg(feature = "full")] impl From for ObjectId where - T: ApubObject + Send, - for<'de2> ::ApubType: Deserialize<'de2>, + T: Object + Send + 'static, + for<'de2> ::Kind: Deserialize<'de2>, { fn from(value: DbUrl) -> Self { - ObjectId::new(value) + let url: Url = value.into(); + ObjectId::from(url) + } +} + +#[cfg(feature = "full")] +impl From for CollectionId +where + T: Collection + Send + 'static, + for<'de2> ::Kind: Deserialize<'de2>, +{ + fn from(value: DbUrl) -> Self { + let url: Url = value.into(); + CollectionId::from(url) + } +} + +#[cfg(feature = "full")] +impl From> for DbUrl +where + T: Collection, + for<'de2> ::Kind: Deserialize<'de2>, +{ + fn from(value: CollectionId) -> Self { + let url: Url = value.into(); + url.into() } } diff --git a/crates/db_schema/src/traits.rs b/crates/db_schema/src/traits.rs index d4e01547..12b92b3d 100644 --- a/crates/db_schema/src/traits.rs +++ b/crates/db_schema/src/traits.rs @@ -149,7 +149,6 @@ pub trait JoinView { #[async_trait] pub trait ApubActor { - // TODO: this should be in a trait ApubObject (and implemented for Post, Comment, PrivateMessage as well) async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result, Error> where Self: Sized; diff --git a/crates/db_schema/src/utils.rs b/crates/db_schema/src/utils.rs index c05cd565..acedab97 100644 --- a/crates/db_schema/src/utils.rs +++ b/crates/db_schema/src/utils.rs @@ -5,7 +5,7 @@ use crate::{ CommentSortType, SortType, }; -use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject}; +use activitypub_federation::{fetch::object_id::ObjectId, traits::Object}; use chrono::NaiveDateTime; use deadpool::Runtime; use diesel::{ @@ -237,8 +237,8 @@ where impl From> for DbUrl where - Kind: ApubObject + Send + 'static, - for<'de2> ::ApubType: serde::Deserialize<'de2>, + Kind: Object + Send + 'static, + for<'de2> ::Kind: serde::Deserialize<'de2>, { fn from(id: ObjectId) -> Self { DbUrl(Box::new(id.into())) diff --git a/crates/routes/Cargo.toml b/crates/routes/Cargo.toml index bc2b573e..c9056abf 100644 --- a/crates/routes/Cargo.toml +++ b/crates/routes/Cargo.toml @@ -17,6 +17,7 @@ lemmy_db_views = { workspace = true } lemmy_db_views_actor = { workspace = true } lemmy_db_schema = { workspace = true } lemmy_api_common = { workspace = true, features = ["full"] } +activitypub_federation = { workspace = true } diesel = { workspace = true } actix-web = { workspace = true } anyhow = { workspace = true } diff --git a/crates/routes/src/webfinger.rs b/crates/routes/src/webfinger.rs index 4d744de9..deade197 100644 --- a/crates/routes/src/webfinger.rs +++ b/crates/routes/src/webfinger.rs @@ -1,3 +1,4 @@ +use activitypub_federation::fetch::webfinger::{Webfinger, WebfingerLink}; use actix_web::{web, web::Query, HttpResponse}; use anyhow::Context; use lemmy_api_common::context::LemmyContext; @@ -5,7 +6,7 @@ use lemmy_db_schema::{ source::{community::Community, person::Person}, traits::ApubActor, }; -use lemmy_utils::{error::LemmyError, location_info, WebfingerLink, WebfingerResponse}; +use lemmy_utils::{error::LemmyError, location_info}; use serde::Deserialize; use std::collections::HashMap; use url::Url; @@ -61,9 +62,10 @@ async fn get_webfinger_response( .flatten() .collect(); - let json = WebfingerResponse { + let json = Webfinger { subject: info.resource.clone(), links, + ..Default::default() }; Ok(HttpResponse::Ok().json(json)) @@ -73,7 +75,9 @@ fn webfinger_link_for_actor(url: Option, kind: &str) -> Vec if let Some(url) = url { let mut properties = HashMap::new(); properties.insert( - "https://www.w3.org/ns/activitystreams#type".to_string(), + "https://www.w3.org/ns/activitystreams#type" + .parse() + .expect("parse url"), kind.to_string(), ); vec![ @@ -81,7 +85,7 @@ fn webfinger_link_for_actor(url: Option, kind: &str) -> Vec rel: Some("http://webfinger.net/rel/profile-page".to_string()), kind: Some("text/html".to_string()), href: Some(url.clone()), - properties: Default::default(), + ..Default::default() }, WebfingerLink { rel: Some("self".to_string()), diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index cdc8c83a..f3213390 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -14,9 +14,7 @@ pub mod request; pub mod utils; pub mod version; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fmt, time::Duration}; -use url::Url; +use std::{fmt, time::Duration}; pub type ConnectionId = usize; @@ -31,22 +29,6 @@ impl fmt::Display for IpAddr { } } -#[derive(Serialize, Deserialize, Debug)] -pub struct WebfingerLink { - pub rel: Option, - #[serde(rename = "type")] - pub kind: Option, - pub href: Option, - #[serde(default)] - pub properties: HashMap, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct WebfingerResponse { - pub subject: String, - pub links: Vec, -} - #[macro_export] macro_rules! location_info { () => { @@ -58,22 +40,3 @@ macro_rules! location_info { ) }; } - -#[cfg(test)] -mod tests { - use super::*; - use reqwest::Client; - - #[tokio::test] - #[ignore] - async fn test_webfinger() { - let client = Client::default(); - let fetch_url = - "https://kino.schuerz.at/.well-known/webfinger?resource=acct:h0e@kino.schuerz.at"; - - let response = client.get(fetch_url).send().await.unwrap(); - - let res = response.json::().await; - assert!(res.is_ok()); - } -} diff --git a/docker/Dockerfile b/docker/Dockerfile index 068f55c2..4da87a1d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -8,12 +8,12 @@ ARG RUST_RELEASE_MODE="debug" COPY . . # Build the project -RUN echo "pub const VERSION: &str = \"$(git describe --tag)\";" > "crates/utils/src/version.rs" # Debug mode build RUN --mount=type=cache,target=/app/target \ if [ "$RUST_RELEASE_MODE" = "debug" ] ; then \ - cargo build --target ${CARGO_BUILD_TARGET} \ + echo "pub const VERSION: &str = \"$(git describe --tag)\";" > "crates/utils/src/version.rs" \ + && cargo build --target ${CARGO_BUILD_TARGET} \ && cp ./target/$CARGO_BUILD_TARGET/$RUST_RELEASE_MODE/lemmy_server /app/lemmy_server; \ fi diff --git a/docker/docker_update.sh b/docker/docker_update.sh index 29127b24..64578fc9 100755 --- a/docker/docker_update.sh +++ b/docker/docker_update.sh @@ -1,8 +1,4 @@ #!/bin/sh - -# This script uses a docker file that builds with musl, and runs on linux alpine -# Its a bit slower for development than the volume mount. - set -e mkdir -p volumes/pictrs diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs index 8ab6980e..d59253de 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_http.rs @@ -370,6 +370,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { async fn perform<'a, Data>( data: Data, context: web::Data, + apub_data: activitypub_federation::config::Data, ) -> Result where Data: Perform @@ -380,13 +381,14 @@ where + 'static, { let res = data.perform(&context, None).await?; - SendActivity::send_activity(&data, &res, &context).await?; + SendActivity::send_activity(&data, &res, &apub_data).await?; Ok(HttpResponse::Ok().json(res)) } async fn route_get<'a, Data>( data: web::Query, context: web::Data, + apub_data: activitypub_federation::config::Data, ) -> Result where Data: Perform @@ -396,12 +398,12 @@ where + Send + 'static, { - perform::(data.0, context).await + perform::(data.0, context, apub_data).await } async fn route_get_apub<'a, Data>( data: web::Query, - context: web::Data, + context: activitypub_federation::config::Data, ) -> Result where Data: PerformApub @@ -419,6 +421,7 @@ where async fn route_post<'a, Data>( data: web::Json, context: web::Data, + apub_data: activitypub_federation::config::Data, ) -> Result where Data: Perform @@ -428,12 +431,13 @@ where + Send + 'static, { - perform::(data.0, context).await + perform::(data.0, context, apub_data).await } async fn perform_crud<'a, Data>( data: Data, context: web::Data, + apub_data: activitypub_federation::config::Data, ) -> Result where Data: PerformCrud @@ -444,13 +448,14 @@ where + 'static, { let res = data.perform(&context, None).await?; - SendActivity::send_activity(&data, &res, &context).await?; + SendActivity::send_activity(&data, &res, &apub_data).await?; Ok(HttpResponse::Ok().json(res)) } async fn route_get_crud<'a, Data>( data: web::Query, context: web::Data, + apub_data: activitypub_federation::config::Data, ) -> Result where Data: PerformCrud @@ -460,12 +465,13 @@ where + Send + 'static, { - perform_crud::(data.0, context).await + perform_crud::(data.0, context, apub_data).await } async fn route_post_crud<'a, Data>( data: web::Json, context: web::Data, + apub_data: activitypub_federation::config::Data, ) -> Result where Data: PerformCrud @@ -475,5 +481,5 @@ where + Send + 'static, { - perform_crud::(data.0, context).await + perform_crud::(data.0, context, apub_data).await } diff --git a/src/api_routes_websocket.rs b/src/api_routes_websocket.rs index b8d8f2c2..3ab1efc7 100644 --- a/src/api_routes_websocket.rs +++ b/src/api_routes_websocket.rs @@ -1,3 +1,4 @@ +use activitypub_federation::config::Data as ContextData; use actix_web::{web, Error, HttpRequest, HttpResponse}; use actix_web_actors::ws; use actix_ws::{MessageStream, Session}; @@ -113,6 +114,7 @@ use lemmy_utils::{error::LemmyError, rate_limit::RateLimitCell, ConnectionId, Ip use serde::Deserialize; use serde_json::Value; use std::{ + ops::Deref, result, str::FromStr, sync::{Arc, Mutex}, @@ -126,6 +128,7 @@ pub async fn websocket( body: web::Payload, context: web::Data, rate_limiter: web::Data, + apub_data: ContextData, ) -> Result { let (response, session, stream) = actix_ws::handle(&req, body)?; @@ -160,7 +163,7 @@ pub async fn websocket( connection_id, alive, rate_limiter, - context, + apub_data, )); Ok(response) @@ -173,7 +176,7 @@ async fn handle_messages( connection_id: ConnectionId, alive: Arc>, rate_limiter: web::Data, - context: web::Data, + context: ContextData, ) -> Result<(), LemmyError> { while let Some(Ok(msg)) = stream.next().await { match msg { @@ -195,7 +198,7 @@ async fn handle_messages( client_ip.clone(), connection_id, rate_limiter.get_ref(), - context.get_ref().clone(), + context.reset_request_count(), ) .await; @@ -246,7 +249,7 @@ async fn parse_json_message( ip: IpAddr, connection_id: ConnectionId, rate_limiter: &RateLimitCell, - context: LemmyContext, + context: ContextData, ) -> Result { let json: Value = serde_json::from_str(&msg)?; let data = json @@ -301,7 +304,7 @@ fn check_rate_limit_passed(passed: bool) -> Result<(), LemmyError> { } pub async fn match_websocket_operation_crud( - context: LemmyContext, + context: ContextData, id: ConnectionId, op: UserOperationCrud, data: Value, @@ -404,25 +407,25 @@ pub async fn match_websocket_operation_crud( } async fn do_websocket_operation_crud<'a, 'b, Data>( - context: LemmyContext, + context: ContextData, id: ConnectionId, op: UserOperationCrud, data: Value, ) -> result::Result where - Data: PerformCrud + SendActivity::Response>, + Data: PerformCrud + SendActivity::Response> + Send, for<'de> Data: Deserialize<'de>, { let parsed_data: Data = serde_json::from_value(data)?; let res = parsed_data - .perform(&web::Data::new(context.clone()), Some(id)) + .perform(&web::Data::new(context.deref().clone()), Some(id)) .await?; SendActivity::send_activity(&parsed_data, &res, &context).await?; serialize_websocket_message(&op, &res) } pub async fn match_websocket_operation_apub( - context: LemmyContext, + context: ContextData, id: ConnectionId, op: UserOperationApub, data: Value, @@ -448,25 +451,23 @@ pub async fn match_websocket_operation_apub( } async fn do_websocket_operation_apub<'a, 'b, Data>( - context: LemmyContext, + context: ContextData, id: ConnectionId, op: UserOperationApub, data: Value, ) -> result::Result where - Data: PerformApub + SendActivity::Response>, + Data: PerformApub + SendActivity::Response> + Send, for<'de> Data: Deserialize<'de>, { let parsed_data: Data = serde_json::from_value(data)?; - let res = parsed_data - .perform(&web::Data::new(context.clone()), Some(id)) - .await?; + let res = parsed_data.perform(&context, Some(id)).await?; SendActivity::send_activity(&parsed_data, &res, &context).await?; serialize_websocket_message(&op, &res) } pub async fn match_websocket_operation( - context: LemmyContext, + context: ContextData, id: ConnectionId, op: UserOperation, data: Value, @@ -626,18 +627,18 @@ pub async fn match_websocket_operation( } async fn do_websocket_operation<'a, 'b, Data>( - context: LemmyContext, + context: ContextData, id: ConnectionId, op: UserOperation, data: Value, ) -> result::Result where - Data: Perform + SendActivity::Response>, + Data: Perform + SendActivity::Response> + Send, for<'de> Data: Deserialize<'de>, { let parsed_data: Data = serde_json::from_value(data)?; let res = parsed_data - .perform(&web::Data::new(context.clone()), Some(id)) + .perform(&web::Data::new(context.deref().clone()), Some(id)) .await?; SendActivity::send_activity(&parsed_data, &res, &context).await?; serialize_websocket_message(&op, &res) diff --git a/src/code_migrations.rs b/src/code_migrations.rs index 07c29bea..c69ce591 100644 --- a/src/code_migrations.rs +++ b/src/code_migrations.rs @@ -1,5 +1,5 @@ // This is for db migrations that require code -use activitypub_federation::core::signatures::generate_actor_keypair; +use activitypub_federation::http_signatures::generate_actor_keypair; use diesel::{ sql_types::{Nullable, Text}, ExpressionMethods, diff --git a/src/lib.rs b/src/lib.rs index d30b214c..152e3ded 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod scheduled_tasks; pub mod telemetry; use crate::{code_migrations::run_advanced_migrations, root_span_builder::QuieterRootSpanBuilder}; +use activitypub_federation::config::{FederationConfig, FederationMiddleware}; use actix_web::{middleware, web::Data, App, HttpServer, Result}; use doku::json::{AutoComments, CommentsStyle, Formatting, ObjectsStyle}; use lemmy_api_common::{ @@ -19,6 +20,7 @@ use lemmy_api_common::{ }, websocket::chat_server::ChatServer, }; +use lemmy_apub::{VerifyUrlData, FEDERATION_HTTP_FETCH_LIMIT}; use lemmy_db_schema::{ source::secret::Secret, utils::{build_db_pool, get_database_url, run_migrations}, @@ -140,15 +142,28 @@ pub async fn start_lemmy_server() -> Result<(), LemmyError> { pool.clone(), chat_server.clone(), client.clone(), - settings.clone(), secret.clone(), rate_limit_cell.clone(), ); + + let federation_config = FederationConfig::builder() + .domain(settings.hostname.clone()) + .app_data(context.clone()) + .client(client.clone()) + .http_fetch_limit(FEDERATION_HTTP_FETCH_LIMIT) + .worker_count(local_site.federation_worker_count as u64) + .debug(cfg!(debug_assertions)) + .http_signature_compat(true) + .url_verifier(Box::new(VerifyUrlData(context.pool().clone()))) + .build() + .expect("configure federation"); + App::new() .wrap(middleware::Logger::default()) .wrap(TracingLogger::::new()) .app_data(Data::new(context)) .app_data(Data::new(rate_limit_cell.clone())) + .wrap(FederationMiddleware::new(federation_config)) // The routes .configure(|cfg| api_routes_http::config(cfg, rate_limit_cell)) .configure(|cfg| {