From bb085189e0c2b7bcf523545f0695d336cace07e4 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 26 Oct 2021 21:05:26 +0200 Subject: [PATCH 01/17] Merge traits ToApub and FromApub into ApubObject --- crates/apub/src/http/community.rs | 8 ++------ crates/apub/src/objects/community.rs | 6 ++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index dcaf551f..6a1c02d7 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -6,18 +6,14 @@ use crate::{ report::Report, }, collections::{ - community_moderators::ApubCommunityModerators, - community_outbox::ApubCommunityOutbox, + community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, CommunityContext, }, context::lemmy_context, fetcher::object_id::ObjectId, generate_outbox_url, http::{ - create_apub_response, - create_apub_tombstone_response, - payload_to_string, - receive_activity, + create_apub_response, create_apub_tombstone_response, payload_to_string, receive_activity, }, objects::community::ApubCommunity, }; diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index e71addba..21ae614f 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -1,14 +1,12 @@ use crate::{ check_is_apub_id_valid, collections::{ - community_moderators::ApubCommunityModerators, - community_outbox::ApubCommunityOutbox, + community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, CommunityContext, }, context::lemmy_context, fetcher::object_id::ObjectId, - generate_moderators_url, - generate_outbox_url, + generate_moderators_url, generate_outbox_url, objects::{get_summary_from_string_or_source, tombstone::Tombstone, ImageObject, Source}, CommunityType, }; From 6792e376b435ac80ac68633978b5ec0e0346b96f Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Mon, 25 Oct 2021 16:15:03 +0200 Subject: [PATCH 02/17] Rewrite community outbox to use new fetcher --- crates/apub/src/collections/community_outbox.rs | 3 ++- crates/apub/src/collections/mod.rs | 1 + crates/apub/src/http/community.rs | 8 ++++++-- crates/apub/src/objects/community.rs | 6 ++++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index 24465c95..4b5e7618 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -9,6 +9,7 @@ use activitystreams::{ base::AnyBase, chrono::NaiveDateTime, collection::kind::OrderedCollectionType, + object::Tombstone, primitives::OneOrMany, url::Url, }; @@ -43,7 +44,7 @@ pub(crate) struct ApubCommunityOutbox(Vec); #[async_trait::async_trait(?Send)] impl ApubObject for ApubCommunityOutbox { type DataType = CommunityContext; - type TombstoneType = (); + type TombstoneType = Tombstone; type ApubType = GroupOutbox; fn last_refreshed_at(&self) -> Option { diff --git a/crates/apub/src/collections/mod.rs b/crates/apub/src/collections/mod.rs index 948824e2..e2a274a0 100644 --- a/crates/apub/src/collections/mod.rs +++ b/crates/apub/src/collections/mod.rs @@ -1,5 +1,6 @@ use crate::objects::community::ApubCommunity; use lemmy_websocket::LemmyContext; + pub(crate) mod community_moderators; pub(crate) mod community_outbox; diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index 6a1c02d7..dcaf551f 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -6,14 +6,18 @@ use crate::{ report::Report, }, collections::{ - community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, + community_moderators::ApubCommunityModerators, + community_outbox::ApubCommunityOutbox, CommunityContext, }, context::lemmy_context, fetcher::object_id::ObjectId, generate_outbox_url, http::{ - create_apub_response, create_apub_tombstone_response, payload_to_string, receive_activity, + create_apub_response, + create_apub_tombstone_response, + payload_to_string, + receive_activity, }, objects::community::ApubCommunity, }; diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index 21ae614f..e71addba 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -1,12 +1,14 @@ use crate::{ check_is_apub_id_valid, collections::{ - community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, + community_moderators::ApubCommunityModerators, + community_outbox::ApubCommunityOutbox, CommunityContext, }, context::lemmy_context, fetcher::object_id::ObjectId, - generate_moderators_url, generate_outbox_url, + generate_moderators_url, + generate_outbox_url, objects::{get_summary_from_string_or_source, tombstone::Tombstone, ImageObject, Source}, CommunityType, }; From 0af047dd833bd7777237daf68bab6af8a4516c68 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 27 Oct 2021 12:56:07 +0200 Subject: [PATCH 03/17] Rewrite community moderators collection --- crates/apub/src/collections/community_outbox.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index 4b5e7618..faf33849 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -6,12 +6,8 @@ use crate::{ objects::{person::ApubPerson, post::ApubPost}, }; use activitystreams::{ - base::AnyBase, - chrono::NaiveDateTime, - collection::kind::OrderedCollectionType, - object::Tombstone, - primitives::OneOrMany, - url::Url, + base::AnyBase, chrono::NaiveDateTime, collection::kind::OrderedCollectionType, + primitives::OneOrMany, url::Url, }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -44,7 +40,7 @@ pub(crate) struct ApubCommunityOutbox(Vec); #[async_trait::async_trait(?Send)] impl ApubObject for ApubCommunityOutbox { type DataType = CommunityContext; - type TombstoneType = Tombstone; + type TombstoneType = (); type ApubType = GroupOutbox; fn last_refreshed_at(&self) -> Option { From 614490d29b01f431682fffd9caa050115b40494e Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Mon, 25 Oct 2021 18:09:21 +0200 Subject: [PATCH 04/17] Fix problem that prevented viewing of pleroma user profiles --- Cargo.lock | 1 + crates/api/Cargo.toml | 1 + crates/api/src/site.rs | 16 ++++--- crates/api_crud/src/comment/create.rs | 4 +- crates/api_crud/src/comment/read.rs | 15 ++++--- crates/api_crud/src/community/create.rs | 4 +- crates/api_crud/src/community/read.rs | 6 +-- crates/api_crud/src/post/create.rs | 4 +- crates/api_crud/src/post/read.rs | 15 ++++--- crates/api_crud/src/private_message/create.rs | 4 +- crates/api_crud/src/user/create.rs | 6 +-- crates/api_crud/src/user/read.rs | 7 ++- crates/apub/src/lib.rs | 45 ++++++++++--------- src/code_migrations.rs | 12 ++--- 14 files changed, 76 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9931aef1..0b8f6413 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1728,6 +1728,7 @@ dependencies = [ "lazy_static", "lemmy_api_common", "lemmy_apub", + "lemmy_apub_lib", "lemmy_db_schema", "lemmy_db_views", "lemmy_db_views_actor", diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index 57a9406f..39be5b5a 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -14,6 +14,7 @@ doctest = false [dependencies] lemmy_apub = { version = "=0.13.5-rc.7", path = "../apub" } +lemmy_apub_lib = { version = "=0.13.5-rc.7", path = "../apub_lib" } lemmy_utils = { version = "=0.13.5-rc.7", path = "../utils" } lemmy_db_schema = { version = "=0.13.5-rc.7", path = "../db_schema" } lemmy_db_views = { version = "=0.13.5-rc.7", path = "../db_views" } diff --git a/crates/api/src/site.rs b/crates/api/src/site.rs index 2797955a..8d89d5db 100644 --- a/crates/api/src/site.rs +++ b/crates/api/src/site.rs @@ -11,10 +11,10 @@ use lemmy_api_common::{ site::*, }; use lemmy_apub::{ - build_actor_id_from_shortname, fetcher::search::{search_by_apub_id, SearchableObjects}, - EndpointType, + get_actor_id_from_name, }; +use lemmy_apub_lib::webfinger::WebfingerType; use lemmy_db_schema::{ from_opt_str_to_opt_enum, newtypes::PersonId, @@ -174,11 +174,13 @@ impl Perform for Search { let listing_type: Option = from_opt_str_to_opt_enum(&data.listing_type); let search_type: SearchType = from_opt_str_to_opt_enum(&data.type_).unwrap_or(SearchType::All); let community_id = data.community_id; - let community_actor_id = data - .community_name - .as_ref() - .map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok()) - .unwrap_or(None); + let community_actor_id = if let Some(name) = &data.community_name { + get_actor_id_from_name(WebfingerType::Group, name, context) + .await + .ok() + } else { + None + }; let creator_id = data.creator_id; match search_type { SearchType::Posts => { diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index 690a5082..6c90e94e 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -17,7 +17,7 @@ use lemmy_apub::{ CreateOrUpdateType, }, fetcher::post_or_comment::PostOrComment, - generate_apub_endpoint, + generate_local_apub_endpoint, EndpointType, }; use lemmy_db_schema::{ @@ -109,7 +109,7 @@ impl PerformCrud for CreateComment { let updated_comment: Comment = blocking(context.pool(), move |conn| -> Result { - let apub_id = generate_apub_endpoint( + let apub_id = generate_local_apub_endpoint( EndpointType::Comment, &inserted_comment_id.to_string(), &protocol_and_hostname, diff --git a/crates/api_crud/src/comment/read.rs b/crates/api_crud/src/comment/read.rs index 490c9670..05f9f90f 100644 --- a/crates/api_crud/src/comment/read.rs +++ b/crates/api_crud/src/comment/read.rs @@ -1,7 +1,8 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{blocking, comment::*, get_local_user_view_from_jwt_opt}; -use lemmy_apub::{build_actor_id_from_shortname, EndpointType}; +use lemmy_apub::get_actor_id_from_name; +use lemmy_apub_lib::webfinger::WebfingerType; use lemmy_db_schema::{ from_opt_str_to_opt_enum, traits::DeleteableOrRemoveable, @@ -34,11 +35,13 @@ impl PerformCrud for GetComments { let listing_type: Option = from_opt_str_to_opt_enum(&data.type_); let community_id = data.community_id; - let community_actor_id = data - .community_name - .as_ref() - .map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok()) - .unwrap_or(None); + let community_actor_id = if let Some(name) = &data.community_name { + get_actor_id_from_name(WebfingerType::Group, name, context) + .await + .ok() + } else { + None + }; let saved_only = data.saved_only; let page = data.page; let limit = data.limit; diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index 8b7daeb7..523ccf00 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -8,9 +8,9 @@ use lemmy_api_common::{ }; use lemmy_apub::{ fetcher::object_id::ObjectId, - generate_apub_endpoint, generate_followers_url, generate_inbox_url, + generate_local_apub_endpoint, generate_shared_inbox_url, objects::community::ApubCommunity, EndpointType, @@ -67,7 +67,7 @@ impl PerformCrud for CreateCommunity { } // Double check for duplicate community actor_ids - let community_actor_id = generate_apub_endpoint( + let community_actor_id = generate_local_apub_endpoint( EndpointType::Community, &data.name, &context.settings().get_protocol_and_hostname(), diff --git a/crates/api_crud/src/community/read.rs b/crates/api_crud/src/community/read.rs index 91effae8..feaf110f 100644 --- a/crates/api_crud/src/community/read.rs +++ b/crates/api_crud/src/community/read.rs @@ -2,11 +2,11 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt}; use lemmy_apub::{ - build_actor_id_from_shortname, fetcher::object_id::ObjectId, + get_actor_id_from_name, objects::community::ApubCommunity, - EndpointType, }; +use lemmy_apub_lib::webfinger::WebfingerType; use lemmy_db_schema::{ from_opt_str_to_opt_enum, traits::DeleteableOrRemoveable, @@ -39,7 +39,7 @@ impl PerformCrud for GetCommunity { None => { let name = data.name.to_owned().unwrap_or_else(|| "main".to_string()); let community_actor_id = - build_actor_id_from_shortname(EndpointType::Community, &name, &context.settings())?; + get_actor_id_from_name(WebfingerType::Group, &name, context).await?; ObjectId::::new(community_actor_id) .dereference(context, &mut 0) diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 3e5fb41e..a6641034 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -16,7 +16,7 @@ use lemmy_apub::{ CreateOrUpdateType, }, fetcher::post_or_comment::PostOrComment, - generate_apub_endpoint, + generate_local_apub_endpoint, EndpointType, }; use lemmy_db_schema::{ @@ -98,7 +98,7 @@ impl PerformCrud for CreatePost { let inserted_post_id = inserted_post.id; let protocol_and_hostname = context.settings().get_protocol_and_hostname(); let updated_post = blocking(context.pool(), move |conn| -> Result { - let apub_id = generate_apub_endpoint( + let apub_id = generate_local_apub_endpoint( EndpointType::Post, &inserted_post_id.to_string(), &protocol_and_hostname, diff --git a/crates/api_crud/src/post/read.rs b/crates/api_crud/src/post/read.rs index 901d1e93..d2a46f53 100644 --- a/crates/api_crud/src/post/read.rs +++ b/crates/api_crud/src/post/read.rs @@ -1,7 +1,8 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, mark_post_as_read, post::*}; -use lemmy_apub::{build_actor_id_from_shortname, EndpointType}; +use lemmy_apub::get_actor_id_from_name; +use lemmy_apub_lib::webfinger::WebfingerType; use lemmy_db_schema::{ from_opt_str_to_opt_enum, traits::DeleteableOrRemoveable, @@ -136,11 +137,13 @@ impl PerformCrud for GetPosts { let page = data.page; let limit = data.limit; let community_id = data.community_id; - let community_actor_id = data - .community_name - .as_ref() - .map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok()) - .unwrap_or(None); + let community_actor_id = if let Some(name) = &data.community_name { + get_actor_id_from_name(WebfingerType::Group, name, context) + .await + .ok() + } else { + None + }; let saved_only = data.saved_only; let mut posts = blocking(context.pool(), move |conn| { diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index d6560be2..c7bca4e0 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -11,7 +11,7 @@ use lemmy_apub::{ private_message::create_or_update::CreateOrUpdatePrivateMessage, CreateOrUpdateType, }, - generate_apub_endpoint, + generate_local_apub_endpoint, EndpointType, }; use lemmy_db_schema::{ @@ -67,7 +67,7 @@ impl PerformCrud for CreatePrivateMessage { let updated_private_message = blocking( context.pool(), move |conn| -> Result { - let apub_id = generate_apub_endpoint( + let apub_id = generate_local_apub_endpoint( EndpointType::PrivateMessage, &inserted_private_message_id.to_string(), &protocol_and_hostname, diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index ce37cd6a..78b204ff 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -2,9 +2,9 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{blocking, honeypot_check, password_length_check, person::*}; use lemmy_apub::{ - generate_apub_endpoint, generate_followers_url, generate_inbox_url, + generate_local_apub_endpoint, generate_shared_inbox_url, EndpointType, }; @@ -96,7 +96,7 @@ impl PerformCrud for Register { if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) { return Err(ApiError::err_plain("invalid_username").into()); } - let actor_id = generate_apub_endpoint( + let actor_id = generate_local_apub_endpoint( EndpointType::Person, &data.username, &context.settings().get_protocol_and_hostname(), @@ -179,7 +179,7 @@ impl PerformCrud for Register { Ok(c) => c, Err(_e) => { let default_community_name = "main"; - let actor_id = generate_apub_endpoint( + let actor_id = generate_local_apub_endpoint( EndpointType::Community, default_community_name, &protocol_and_hostname, diff --git a/crates/api_crud/src/user/read.rs b/crates/api_crud/src/user/read.rs index a5b69ba4..b649c8fb 100644 --- a/crates/api_crud/src/user/read.rs +++ b/crates/api_crud/src/user/read.rs @@ -2,11 +2,11 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*}; use lemmy_apub::{ - build_actor_id_from_shortname, fetcher::object_id::ObjectId, + get_actor_id_from_name, objects::person::ApubPerson, - EndpointType, }; +use lemmy_apub_lib::webfinger::WebfingerType; use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType}; use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder}; use lemmy_db_views_actor::{ @@ -46,8 +46,7 @@ impl PerformCrud for GetPersonDetails { .username .to_owned() .unwrap_or_else(|| "admin".to_string()); - let actor_id = - build_actor_id_from_shortname(EndpointType::Person, &name, &context.settings())?; + let actor_id = get_actor_id_from_name(WebfingerType::Person, &name, context).await?; let person = ObjectId::::new(actor_id) .dereference(context, &mut 0) diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index 675798b6..a900ad3d 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -12,7 +12,11 @@ extern crate lazy_static; use crate::fetcher::post_or_comment::PostOrComment; use anyhow::{anyhow, Context}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{activity_queue::send_activity, traits::ActorType}; +use lemmy_apub_lib::{ + activity_queue::send_activity, + traits::ActorType, + webfinger::{webfinger_resolve_actor, WebfingerType}, +}; use lemmy_db_schema::{ newtypes::{CommunityId, DbUrl}, source::{activity::Activity, person::Person}, @@ -111,7 +115,7 @@ pub enum EndpointType { } /// Generates an apub endpoint for a given domain, IE xyz.tld -fn generate_apub_endpoint_for_domain( +pub fn generate_local_apub_endpoint( endpoint_type: EndpointType, name: &str, domain: &str, @@ -127,15 +131,6 @@ fn generate_apub_endpoint_for_domain( Ok(Url::parse(&format!("{}/{}/{}", domain, point, name))?.into()) } -/// Generates the ActivityPub ID for a given object type and ID. -pub fn generate_apub_endpoint( - endpoint_type: EndpointType, - name: &str, - protocol_and_hostname: &str, -) -> Result { - generate_apub_endpoint_for_domain(endpoint_type, name, protocol_and_hostname) -} - pub fn generate_followers_url(actor_id: &DbUrl) -> Result { Ok(Url::parse(&format!("{}/followers", actor_id))?.into()) } @@ -169,23 +164,31 @@ fn generate_moderators_url(community_id: &DbUrl) -> Result { /// Takes in a shortname of the type dessalines@xyz.tld or dessalines (assumed to be local), and outputs the actor id. /// Used in the API for communities and users. -pub fn build_actor_id_from_shortname( - endpoint_type: EndpointType, +pub async fn get_actor_id_from_name( + webfinger_type: WebfingerType, short_name: &str, - settings: &Settings, -) -> Result { + context: &LemmyContext, +) -> Result { let split = short_name.split('@').collect::>(); let name = split[0]; // If there's no @, its local - let domain = if split.len() == 1 { - settings.get_protocol_and_hostname() + if split.len() == 1 { + let domain = context.settings().get_protocol_and_hostname(); + let endpoint_type = match webfinger_type { + WebfingerType::Person => EndpointType::Person, + WebfingerType::Group => EndpointType::Community, + }; + Ok(generate_local_apub_endpoint(endpoint_type, name, &domain)?) } else { - format!("{}://{}", settings.get_protocol_string(), split[1]) - }; - - generate_apub_endpoint_for_domain(endpoint_type, name, &domain) + let protocol = context.settings().get_protocol_string(); + Ok( + webfinger_resolve_actor(name, split[1], webfinger_type, context.client(), protocol) + .await? + .into(), + ) + } } /// Store a sent or received activity in the database, for logging purposes. These records are not diff --git a/src/code_migrations.rs b/src/code_migrations.rs index 8910733a..2feefbdf 100644 --- a/src/code_migrations.rs +++ b/src/code_migrations.rs @@ -4,9 +4,9 @@ use diesel::{ *, }; use lemmy_apub::{ - generate_apub_endpoint, generate_followers_url, generate_inbox_url, + generate_local_apub_endpoint, generate_shared_inbox_url, EndpointType, }; @@ -58,7 +58,7 @@ fn user_updates_2020_04_02( let form = PersonForm { name: cperson.name.to_owned(), - actor_id: Some(generate_apub_endpoint( + actor_id: Some(generate_local_apub_endpoint( EndpointType::Person, &cperson.name, protocol_and_hostname, @@ -93,7 +93,7 @@ fn community_updates_2020_04_02( for ccommunity in &incorrect_communities { let keypair = generate_actor_keypair()?; - let community_actor_id = generate_apub_endpoint( + let community_actor_id = generate_local_apub_endpoint( EndpointType::Community, &ccommunity.name, protocol_and_hostname, @@ -143,7 +143,7 @@ fn post_updates_2020_04_03( .load::(conn)?; for cpost in &incorrect_posts { - let apub_id = generate_apub_endpoint( + let apub_id = generate_local_apub_endpoint( EndpointType::Post, &cpost.id.to_string(), protocol_and_hostname, @@ -171,7 +171,7 @@ fn comment_updates_2020_04_03( .load::(conn)?; for ccomment in &incorrect_comments { - let apub_id = generate_apub_endpoint( + let apub_id = generate_local_apub_endpoint( EndpointType::Comment, &ccomment.id.to_string(), protocol_and_hostname, @@ -199,7 +199,7 @@ fn private_message_updates_2020_05_05( .load::(conn)?; for cpm in &incorrect_pms { - let apub_id = generate_apub_endpoint( + let apub_id = generate_local_apub_endpoint( EndpointType::PrivateMessage, &cpm.id.to_string(), protocol_and_hostname, From 03b8ae72152c781db5f8834891feca1096a18a25 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Mon, 25 Oct 2021 19:22:34 +0200 Subject: [PATCH 05/17] Activity.to should always be a vec (and unspecified size for public activities) --- .../src/activities/comment/create_or_update.rs | 15 +++++++++++---- crates/apub/src/activities/community/add_mod.rs | 8 +++++--- crates/apub/src/activities/community/announce.rs | 8 +++++--- .../apub/src/activities/community/block_user.rs | 8 +++++--- .../apub/src/activities/community/remove_mod.rs | 8 +++++--- .../src/activities/community/undo_block_user.rs | 8 +++++--- crates/apub/src/activities/community/update.rs | 8 +++++--- crates/apub/src/activities/deletion/delete.rs | 8 +++++--- .../apub/src/activities/deletion/undo_delete.rs | 8 +++++--- crates/apub/src/activities/following/accept.rs | 10 +++++----- crates/apub/src/activities/following/follow.rs | 9 ++++----- crates/apub/src/activities/following/undo.rs | 8 ++++---- .../apub/src/activities/post/create_or_update.rs | 9 +++++---- .../private_message/create_or_update.rs | 4 ++-- .../apub/src/activities/private_message/delete.rs | 4 ++-- .../src/activities/private_message/undo_delete.rs | 4 ++-- crates/apub/src/activities/voting/undo_vote.rs | 8 +++++--- crates/apub/src/activities/voting/vote.rs | 9 +++++---- crates/apub/src/http/community.rs | 4 ++-- crates/apub/src/http/mod.rs | 4 ++-- crates/apub/src/http/person.rs | 4 ++-- crates/apub_lib/src/values.rs | 9 --------- 22 files changed, 91 insertions(+), 74 deletions(-) diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 0dcf3f7d..9ccf49f0 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -6,6 +6,7 @@ use crate::{ extract_community, generate_activity_id, verify_activity, + verify_is_public, verify_person_in_community, CreateOrUpdateType, }, @@ -17,12 +18,17 @@ use crate::{ person::ApubPerson, }, }; -use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed}; +use activitystreams::{ + base::AnyBase, + link::Mention, + primitives::OneOrMany, + public, + unparsed::Unparsed, +}; use lemmy_api_common::{blocking, check_post_deleted_or_removed}; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, - values::PublicUrl, verify::verify_domains_match, }; use lemmy_db_schema::{ @@ -38,7 +44,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct CreateOrUpdateComment { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, object: Note, cc: Vec, tag: Vec, @@ -76,7 +82,7 @@ impl CreateOrUpdateComment { let create_or_update = CreateOrUpdateComment { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object: comment.to_apub(context).await?, cc: maa.ccs, tag: maa.tags, @@ -100,6 +106,7 @@ impl ActivityHandler for CreateOrUpdateComment { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; let community = extract_community(&self.cc, context, request_counter).await?; let community_id = ObjectId::new(community.actor_id()); let post = self.object.get_parents(context, request_counter).await?.0; diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index e1cf03e0..6df6f812 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -4,6 +4,7 @@ use crate::{ generate_activity_id, verify_activity, verify_add_remove_moderator_target, + verify_is_public, verify_mod_action, verify_person_in_community, }, @@ -16,13 +17,13 @@ use activitystreams::{ activity::kind::AddType, base::AnyBase, primitives::OneOrMany, + public, unparsed::Unparsed, }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - values::PublicUrl, }; use lemmy_db_schema::{ source::community::{CommunityModerator, CommunityModeratorForm}, @@ -37,7 +38,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct AddMod { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, object: ObjectId, target: Url, cc: [ObjectId; 1], @@ -63,7 +64,7 @@ impl AddMod { )?; let add = AddMod { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object: ObjectId::new(added_mod.actor_id()), target: generate_moderators_url(&community.actor_id)?.into(), cc: [ObjectId::new(community.actor_id())], @@ -88,6 +89,7 @@ impl ActivityHandler for AddMod { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index f3dc72c5..deca2b73 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -14,6 +14,7 @@ use crate::{ post::create_or_update::CreateOrUpdatePost, verify_activity, verify_community, + verify_is_public, voting::{undo_vote::UndoVote, vote::Vote}, }, context::lemmy_context, @@ -28,12 +29,12 @@ use activitystreams::{ activity::kind::AnnounceType, base::AnyBase, primitives::OneOrMany, + public, unparsed::Unparsed, }; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - values::PublicUrl, }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; @@ -61,7 +62,7 @@ pub enum AnnouncableActivities { #[serde(rename_all = "camelCase")] pub struct AnnounceActivity { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, object: AnnouncableActivities, cc: Vec, #[serde(rename = "type")] @@ -82,7 +83,7 @@ impl AnnounceActivity { ) -> Result<(), LemmyError> { let announce = AnnounceActivity { actor: ObjectId::new(community.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object, cc: vec![community.followers_url()], kind: AnnounceType::Announce, @@ -106,6 +107,7 @@ impl ActivityHandler for AnnounceActivity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; verify_community(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs index a1c76c23..64a030c8 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/community/block_user.rs @@ -3,6 +3,7 @@ use crate::{ community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, + verify_is_public, verify_mod_action, verify_person_in_community, }, @@ -14,13 +15,13 @@ use activitystreams::{ activity::kind::BlockType, base::AnyBase, primitives::OneOrMany, + public, unparsed::Unparsed, }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - values::PublicUrl, }; use lemmy_db_schema::{ source::community::{ @@ -40,7 +41,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct BlockUserFromCommunity { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, pub(in crate::activities::community) object: ObjectId, cc: [ObjectId; 1], #[serde(rename = "type")] @@ -61,7 +62,7 @@ impl BlockUserFromCommunity { ) -> Result { Ok(BlockUserFromCommunity { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object: ObjectId::new(target.actor_id()), cc: [ObjectId::new(community.actor_id())], kind: BlockType::Block, @@ -97,6 +98,7 @@ impl ActivityHandler for BlockUserFromCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index 9f4c9ae6..d1a5675a 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -4,6 +4,7 @@ use crate::{ generate_activity_id, verify_activity, verify_add_remove_moderator_target, + verify_is_public, verify_mod_action, verify_person_in_community, }, @@ -16,13 +17,13 @@ use activitystreams::{ activity::kind::RemoveType, base::AnyBase, primitives::OneOrMany, + public, unparsed::Unparsed, }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - values::PublicUrl, }; use lemmy_db_schema::{ source::community::{CommunityModerator, CommunityModeratorForm}, @@ -37,7 +38,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct RemoveMod { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, pub(in crate::activities) object: ObjectId, cc: [ObjectId; 1], #[serde(rename = "type")] @@ -64,7 +65,7 @@ impl RemoveMod { )?; let remove = RemoveMod { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object: ObjectId::new(removed_mod.actor_id()), target: generate_moderators_url(&community.actor_id)?.into(), id: id.clone(), @@ -88,6 +89,7 @@ impl ActivityHandler for RemoveMod { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs index 1614de67..ee36d1ae 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/community/undo_block_user.rs @@ -7,6 +7,7 @@ use crate::{ }, generate_activity_id, verify_activity, + verify_is_public, verify_mod_action, verify_person_in_community, }, @@ -18,13 +19,13 @@ use activitystreams::{ activity::kind::UndoType, base::AnyBase, primitives::OneOrMany, + public, unparsed::Unparsed, }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - values::PublicUrl, }; use lemmy_db_schema::{ source::community::{CommunityPersonBan, CommunityPersonBanForm}, @@ -39,7 +40,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct UndoBlockUserFromCommunity { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, object: BlockUserFromCommunity, cc: [ObjectId; 1], #[serde(rename = "type")] @@ -66,7 +67,7 @@ impl UndoBlockUserFromCommunity { )?; let undo = UndoBlockUserFromCommunity { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object: block, cc: [ObjectId::new(community.actor_id())], kind: UndoType::Undo, @@ -89,6 +90,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index f6d693dd..6c78dbfb 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -3,6 +3,7 @@ use crate::{ community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, + verify_is_public, verify_mod_action, verify_person_in_community, }, @@ -17,13 +18,13 @@ use activitystreams::{ activity::kind::UpdateType, base::AnyBase, primitives::OneOrMany, + public, unparsed::Unparsed, }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, - values::PublicUrl, }; use lemmy_db_schema::{ source::community::{Community, CommunityForm}, @@ -40,7 +41,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct UpdateCommunity { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, // TODO: would be nice to use a separate struct here, which only contains the fields updated here object: Group, cc: [ObjectId; 1], @@ -65,7 +66,7 @@ impl UpdateCommunity { )?; let update = UpdateCommunity { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object: community.to_apub(context).await?, cc: [ObjectId::new(community.actor_id())], kind: UpdateType::Update, @@ -87,6 +88,7 @@ impl ActivityHandler for UpdateCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 02df7dc3..7593c5bf 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -9,6 +9,7 @@ use crate::{ }, generate_activity_id, verify_activity, + verify_is_public, }, context::lemmy_context, fetcher::object_id::ObjectId, @@ -18,6 +19,7 @@ use activitystreams::{ activity::kind::DeleteType, base::AnyBase, primitives::OneOrMany, + public, unparsed::Unparsed, }; use anyhow::anyhow; @@ -25,7 +27,6 @@ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - values::PublicUrl, }; use lemmy_db_schema::{ source::{ @@ -66,7 +67,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct Delete { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, pub(in crate::activities::deletion) object: Url, pub(in crate::activities::deletion) cc: [ObjectId; 1], #[serde(rename = "type")] @@ -89,6 +90,7 @@ impl ActivityHandler for Delete { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; verify_delete_activity( &self.object, @@ -144,7 +146,7 @@ impl Delete { ) -> Result { Ok(Delete { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object: object_id, cc: [ObjectId::new(community.actor_id())], kind: DeleteType::Delete, diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index 327bf86c..87c8eae7 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -10,6 +10,7 @@ use crate::{ }, generate_activity_id, verify_activity, + verify_is_public, }, context::lemmy_context, fetcher::object_id::ObjectId, @@ -19,6 +20,7 @@ use activitystreams::{ activity::kind::UndoType, base::AnyBase, primitives::OneOrMany, + public, unparsed::Unparsed, }; use anyhow::anyhow; @@ -26,7 +28,6 @@ use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - values::PublicUrl, }; use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post}; use lemmy_utils::LemmyError; @@ -42,7 +43,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct UndoDelete { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, object: Delete, cc: [ObjectId; 1], #[serde(rename = "type")] @@ -62,6 +63,7 @@ impl ActivityHandler for UndoDelete { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; self.object.verify(context, request_counter).await?; verify_delete_activity( @@ -117,7 +119,7 @@ impl UndoDelete { )?; let undo = UndoDelete { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object, cc: [ObjectId::new(community.actor_id())], kind: UndoType::Undo, diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index 6fa65e7b..78d12b52 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -32,7 +32,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct AcceptFollowCommunity { actor: ObjectId, - to: ObjectId, + to: [ObjectId; 1], object: FollowCommunity, #[serde(rename = "type")] kind: AcceptType, @@ -57,7 +57,7 @@ impl AcceptFollowCommunity { .await?; let accept = AcceptFollowCommunity { actor: ObjectId::new(community.actor_id()), - to: ObjectId::new(person.actor_id()), + to: [ObjectId::new(person.actor_id())], object: follow, kind: AcceptType::Accept, id: generate_activity_id( @@ -82,8 +82,8 @@ impl ActivityHandler for AcceptFollowCommunity { request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_activity(self, &context.settings())?; - verify_urls_match(self.to.inner(), self.object.actor())?; - verify_urls_match(self.actor(), self.object.to.inner())?; + verify_urls_match(self.to[0].inner(), self.object.actor())?; + verify_urls_match(self.actor(), self.object.to[0].inner())?; verify_community(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) @@ -95,7 +95,7 @@ impl ActivityHandler for AcceptFollowCommunity { request_counter: &mut i32, ) -> Result<(), LemmyError> { let actor = self.actor.dereference(context, request_counter).await?; - let to = self.to.dereference(context, request_counter).await?; + let to = self.to[0].dereference(context, request_counter).await?; // This will throw an error if no follow was requested blocking(context.pool(), move |conn| { CommunityFollower::follow_accepted(conn, actor.id, to.id) diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 14424381..82d2d540 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -35,8 +35,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct FollowCommunity { pub(in crate::activities::following) actor: ObjectId, - // TODO: is there any reason to put the same community id twice, in to and object? - pub(in crate::activities::following) to: ObjectId, + pub(in crate::activities::following) to: [ObjectId; 1], pub(in crate::activities::following) object: ObjectId, #[serde(rename = "type")] kind: FollowType, @@ -55,7 +54,7 @@ impl FollowCommunity { ) -> Result { Ok(FollowCommunity { actor: ObjectId::new(actor.actor_id()), - to: ObjectId::new(community.actor_id()), + to: [ObjectId::new(community.actor_id())], object: ObjectId::new(community.actor_id()), kind: FollowType::Follow, id: generate_activity_id( @@ -96,7 +95,7 @@ impl ActivityHandler for FollowCommunity { request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_activity(self, &context.settings())?; - verify_urls_match(self.to.inner(), self.object.inner())?; + verify_urls_match(self.to[0].inner(), self.object.inner())?; verify_person(&self.actor, context, request_counter).await?; Ok(()) } @@ -107,7 +106,7 @@ impl ActivityHandler for FollowCommunity { request_counter: &mut i32, ) -> Result<(), LemmyError> { let actor = self.actor.dereference(context, request_counter).await?; - let community = self.object.dereference(context, request_counter).await?; + let community = self.to[0].dereference(context, request_counter).await?; let community_follower_form = CommunityFollowerForm { community_id: community.id, person_id: actor.id, diff --git a/crates/apub/src/activities/following/undo.rs b/crates/apub/src/activities/following/undo.rs index 5c548ae4..7abb6117 100644 --- a/crates/apub/src/activities/following/undo.rs +++ b/crates/apub/src/activities/following/undo.rs @@ -35,7 +35,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct UndoFollowCommunity { actor: ObjectId, - to: ObjectId, + to: [ObjectId; 1], object: FollowCommunity, #[serde(rename = "type")] kind: UndoType, @@ -55,7 +55,7 @@ impl UndoFollowCommunity { let object = FollowCommunity::new(actor, community, context)?; let undo = UndoFollowCommunity { actor: ObjectId::new(actor.actor_id()), - to: ObjectId::new(community.actor_id()), + to: [ObjectId::new(community.actor_id())], object, kind: UndoType::Undo, id: generate_activity_id( @@ -79,7 +79,7 @@ impl ActivityHandler for UndoFollowCommunity { request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_activity(self, &context.settings())?; - verify_urls_match(self.to.inner(), self.object.object.inner())?; + verify_urls_match(self.to[0].inner(), self.object.object.inner())?; verify_urls_match(self.actor(), self.object.actor())?; verify_person(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; @@ -92,7 +92,7 @@ impl ActivityHandler for UndoFollowCommunity { request_counter: &mut i32, ) -> Result<(), LemmyError> { let actor = self.actor.dereference(context, request_counter).await?; - let community = self.to.dereference(context, request_counter).await?; + let community = self.to[0].dereference(context, request_counter).await?; let community_follower_form = CommunityFollowerForm { community_id: community.id, diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index 4a048a96..b3c0b694 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -4,6 +4,7 @@ use crate::{ community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, + verify_is_public, verify_mod_action, verify_person_in_community, CreateOrUpdateType, @@ -16,13 +17,12 @@ use crate::{ post::{ApubPost, Page}, }, }; -use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; +use activitystreams::{base::AnyBase, primitives::OneOrMany, public, unparsed::Unparsed}; use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, - values::PublicUrl, verify::{verify_domains_match, verify_urls_match}, }; use lemmy_db_schema::{source::community::Community, traits::Crud}; @@ -35,7 +35,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct CreateOrUpdatePost { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, object: Page, cc: [ObjectId; 1], #[serde(rename = "type")] @@ -61,7 +61,7 @@ impl CreateOrUpdatePost { )?; Ok(CreateOrUpdatePost { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object: post.to_apub(context).await?, cc: [ObjectId::new(community.actor_id())], kind, @@ -97,6 +97,7 @@ impl ActivityHandler for CreateOrUpdatePost { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; let community = self.cc[0].dereference(context, request_counter).await?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs index 9ef22f0e..d6552b37 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -28,7 +28,7 @@ pub struct CreateOrUpdatePrivateMessage { pub context: OneOrMany, id: Url, actor: ObjectId, - to: ObjectId, + to: [ObjectId; 1], object: Note, #[serde(rename = "type")] kind: CreateOrUpdateType, @@ -57,7 +57,7 @@ impl CreateOrUpdatePrivateMessage { context: lemmy_context(), id: id.clone(), actor: ObjectId::new(actor.actor_id()), - to: ObjectId::new(recipient.actor_id()), + to: [ObjectId::new(recipient.actor_id())], object: private_message.to_apub(context).await?, kind, unparsed: Default::default(), diff --git a/crates/apub/src/activities/private_message/delete.rs b/crates/apub/src/activities/private_message/delete.rs index bb374eb4..bcd9542c 100644 --- a/crates/apub/src/activities/private_message/delete.rs +++ b/crates/apub/src/activities/private_message/delete.rs @@ -30,7 +30,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct DeletePrivateMessage { actor: ObjectId, - to: ObjectId, + to: [ObjectId; 1], pub(in crate::activities::private_message) object: ObjectId, #[serde(rename = "type")] kind: DeleteType, @@ -49,7 +49,7 @@ impl DeletePrivateMessage { ) -> Result { Ok(DeletePrivateMessage { actor: ObjectId::new(actor.actor_id()), - to: ObjectId::new(actor.actor_id()), + to: [ObjectId::new(actor.actor_id())], object: ObjectId::new(pm.ap_id.clone()), kind: DeleteType::Delete, id: generate_activity_id( diff --git a/crates/apub/src/activities/private_message/undo_delete.rs b/crates/apub/src/activities/private_message/undo_delete.rs index 0263b811..cdabaa9a 100644 --- a/crates/apub/src/activities/private_message/undo_delete.rs +++ b/crates/apub/src/activities/private_message/undo_delete.rs @@ -35,7 +35,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct UndoDeletePrivateMessage { actor: ObjectId, - to: ObjectId, + to: [ObjectId; 1], object: DeletePrivateMessage, #[serde(rename = "type")] kind: UndoType, @@ -65,7 +65,7 @@ impl UndoDeletePrivateMessage { )?; let undo = UndoDeletePrivateMessage { actor: ObjectId::new(actor.actor_id()), - to: ObjectId::new(recipient.actor_id()), + to: [ObjectId::new(recipient.actor_id())], object, kind: UndoType::Undo, id: id.clone(), diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index d72b5245..1ac1d4d8 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -3,6 +3,7 @@ use crate::{ community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, + verify_is_public, verify_person_in_community, voting::{ undo_vote_comment, @@ -19,13 +20,13 @@ use activitystreams::{ activity::kind::UndoType, base::AnyBase, primitives::OneOrMany, + public, unparsed::Unparsed, }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - values::PublicUrl, verify::verify_urls_match, }; use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud}; @@ -39,7 +40,7 @@ use url::Url; #[serde(rename_all = "camelCase")] pub struct UndoVote { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, object: Vote, cc: [ObjectId; 1], #[serde(rename = "type")] @@ -72,7 +73,7 @@ impl UndoVote { )?; let undo_vote = UndoVote { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object, cc: [ObjectId::new(community.actor_id())], kind: UndoType::Undo, @@ -93,6 +94,7 @@ impl ActivityHandler for UndoVote { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_urls_match(self.actor(), self.object.actor())?; diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 5d4c9066..166ce733 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -3,6 +3,7 @@ use crate::{ community::{announce::AnnouncableActivities, send_to_community}, generate_activity_id, verify_activity, + verify_is_public, verify_person_in_community, voting::{vote_comment, vote_post}, }, @@ -11,13 +12,12 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, PostOrComment, }; -use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; +use activitystreams::{base::AnyBase, primitives::OneOrMany, public, unparsed::Unparsed}; use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - values::PublicUrl, }; use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud}; use lemmy_utils::LemmyError; @@ -58,7 +58,7 @@ impl From<&VoteType> for i16 { #[serde(rename_all = "camelCase")] pub struct Vote { actor: ObjectId, - to: [PublicUrl; 1], + to: Vec, pub(in crate::activities::voting) object: ObjectId, cc: [ObjectId; 1], #[serde(rename = "type")] @@ -80,7 +80,7 @@ impl Vote { ) -> Result { Ok(Vote { actor: ObjectId::new(actor.actor_id()), - to: [PublicUrl::Public], + to: vec![public()], object: ObjectId::new(object.ap_id()), cc: [ObjectId::new(community.actor_id())], kind: kind.clone(), @@ -118,6 +118,7 @@ impl ActivityHandler for Vote { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; Ok(()) diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index dcaf551f..a7e66d8a 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -32,7 +32,7 @@ use lemmy_db_schema::source::community::Community; use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use log::trace; +use log::info; use serde::{Deserialize, Serialize}; #[derive(Deserialize)] @@ -78,7 +78,7 @@ pub async fn community_inbox( context: web::Data, ) -> Result { let unparsed = payload_to_string(payload).await?; - trace!("Received community inbox activity {}", unparsed); + info!("Received community inbox activity {}", unparsed); let activity = serde_json::from_str::(&unparsed)?; receive_group_inbox(activity.clone(), request, &context).await?; diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index b82a6d89..fe44d5e3 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -27,7 +27,7 @@ use lemmy_apub_lib::{ use lemmy_db_schema::{source::activity::Activity, DbPool}; use lemmy_utils::{location_info, LemmyError}; use lemmy_websocket::LemmyContext; -use log::{info, trace}; +use log::info; use serde::{Deserialize, Serialize}; use std::{fmt::Debug, io::Read}; use url::Url; @@ -54,7 +54,7 @@ pub async fn shared_inbox( context: web::Data, ) -> Result { let unparsed = payload_to_string(payload).await?; - trace!("Received shared inbox activity {}", unparsed); + info!("Received shared inbox activity {}", unparsed); let activity = serde_json::from_str::(&unparsed)?; match activity { SharedInboxActivities::GroupInboxActivities(g) => { diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index a7e94020..88ffa120 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -28,7 +28,7 @@ use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject}; use lemmy_db_schema::source::person::Person; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use log::trace; +use log::info; use serde::{Deserialize, Serialize}; use url::Url; @@ -79,7 +79,7 @@ pub async fn person_inbox( context: web::Data, ) -> Result { let unparsed = payload_to_string(payload).await?; - trace!("Received person inbox activity {}", unparsed); + info!("Received person inbox activity {}", unparsed); let activity = serde_json::from_str::(&unparsed)?; receive_person_inbox(activity, request, &context).await } diff --git a/crates/apub_lib/src/values.rs b/crates/apub_lib/src/values.rs index 2c9f65ff..bd014c12 100644 --- a/crates/apub_lib/src/values.rs +++ b/crates/apub_lib/src/values.rs @@ -33,15 +33,6 @@ use serde::{Deserialize, Serialize}; -/// The identifier used to address activities to the public. -/// -/// -#[derive(Debug, Clone, Deserialize, Serialize)] -pub enum PublicUrl { - #[serde(rename = "https://www.w3.org/ns/activitystreams#Public")] - Public, -} - /// Media type for markdown text. /// /// From 74523fb534e3c701fb1f42807b3173543b957d88 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 26 Oct 2021 12:47:26 +0200 Subject: [PATCH 06/17] Make Note.context optional for pleroma compat (hack) --- .../apub/assets/pleroma-create-comment.json | 52 +++++++++++++++++++ .../activities/comment/create_or_update.rs | 14 +++++ crates/apub/src/objects/comment.rs | 15 +++--- crates/apub/src/objects/mod.rs | 2 +- 4 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 crates/apub/assets/pleroma-create-comment.json diff --git a/crates/apub/assets/pleroma-create-comment.json b/crates/apub/assets/pleroma-create-comment.json new file mode 100644 index 00000000..55594ced --- /dev/null +++ b/crates/apub/assets/pleroma-create-comment.json @@ -0,0 +1,52 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://greenish.red/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "actor": "https://greenish.red/users/nutomic", + "cc": [ + "https://greenish.red/users/nutomic/followers" + ], + "context": "https://greenish.red/contexts/f6244742-0526-4b84-ac4f-ceadf1fb4e56", + "context_id": 6336544, + "directMessage": false, + "id": "https://greenish.red/activities/db61d52b-9c35-486a-bf27-bbd4edc6c6a1", + "object": { + "actor": "https://greenish.red/users/nutomic", + "attachment": [], + "attributedTo": "https://greenish.red/users/nutomic", + "cc": [ + "https://greenish.red/users/nutomic/followers" + ], + "content": "@lanodan test", + "context": "https://greenish.red/contexts/f6244742-0526-4b84-ac4f-ceadf1fb4e56", + "conversation": "https://greenish.red/contexts/f6244742-0526-4b84-ac4f-ceadf1fb4e56", + "id": "https://greenish.red/objects/1a522f2e-d5ab-454b-93d7-e58bc0650c2a", + "inReplyTo": "https://enterprise.lemmy.ml/post/55143", + "published": "2021-10-26T10:28:35.602455Z", + "sensitive": false, + "source": "@lanodan@ds9.lemmy.ml test", + "summary": "", + "tag": [ + { + "href": "https://enterprise.lemmy.ml/u/picard", + "name": "@lanodan@ds9.lemmy.ml", + "type": "Mention" + } + ], + "to": [ + "https://enterprise.lemmy.ml/u/picard", + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Note" + }, + "published": "2021-10-26T10:28:35.595650Z", + "to": [ + "https://enterprise.lemmy.ml/u/picard", + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Create" +} \ No newline at end of file diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 9ccf49f0..537563e9 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -47,6 +47,7 @@ pub struct CreateOrUpdateComment { to: Vec, object: Note, cc: Vec, + #[serde(default)] tag: Vec, #[serde(rename = "type")] kind: CreateOrUpdateType, @@ -142,3 +143,16 @@ impl ActivityHandler for CreateOrUpdateComment { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::objects::tests::file_to_json_object; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_pleroma_create_comment() { + let _: CreateOrUpdateComment = file_to_json_object("assets/pleroma-create-comment.json"); + } +} diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index b79f53e0..927127d8 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -46,8 +46,11 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Note { + /// Necessary to make this optional to make Pleroma Create/Note work. + /// TODO: change this so that context is not defined in the struct itself, but added in activity + /// queue and http handlers #[serde(rename = "@context")] - context: OneOrMany, + context: Option>, r#type: NoteType, id: Url, pub(crate) attributed_to: ObjectId, @@ -202,7 +205,7 @@ impl ApubObject for ApubComment { }; let note = Note { - context: lemmy_context(), + context: Some(lemmy_context()), r#type: NoteType::Note, id: self.ap_id.to_owned().into_inner(), attributed_to: ObjectId::new(creator.actor_id), @@ -274,7 +277,7 @@ impl ApubObject for ApubComment { } #[cfg(test)] -mod tests { +pub(crate) mod tests { use super::*; use crate::objects::{ community::ApubCommunity, @@ -283,7 +286,7 @@ mod tests { use assert_json_diff::assert_json_include; use serial_test::serial; - async fn prepare_comment_test( + pub(crate) async fn prepare_comment_test( url: &Url, context: &LemmyContext, ) -> (ApubPerson, ApubCommunity, ApubPost) { @@ -310,9 +313,7 @@ mod tests { #[actix_rt::test] #[serial] - async fn test_parse_lemmy_comment() { - // TODO: changed ObjectId::dereference() so that it always fetches if - // last_refreshed_at() == None. But post doesnt store that and expects to never be refetched + pub(crate) async fn test_parse_lemmy_comment() { let context = init_context(); let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap(); let data = prepare_comment_test(&url, &context).await; diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs index 96700cb7..d0cb1341 100644 --- a/crates/apub/src/objects/mod.rs +++ b/crates/apub/src/objects/mod.rs @@ -38,7 +38,7 @@ fn get_summary_from_string_or_source( } #[cfg(test)] -mod tests { +pub(crate) mod tests { use actix::Actor; use diesel::{ r2d2::{ConnectionManager, Pool}, From 271785b7fbe824628d79187f33be3c2e75f42d1b Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 28 Oct 2021 13:46:48 +0200 Subject: [PATCH 07/17] When receiving activity, dont read community from cc (for pleroma compat and better verification) --- .../activities/comment/create_or_update.rs | 27 +++++++++-- .../apub/src/activities/community/add_mod.rs | 22 +++++++-- .../apub/src/activities/community/announce.rs | 41 ++++++++++++++-- .../src/activities/community/block_user.rs | 23 +++++++-- crates/apub/src/activities/community/mod.rs | 13 ++++- .../src/activities/community/remove_mod.rs | 23 +++++++-- .../activities/community/undo_block_user.rs | 18 +++++-- .../apub/src/activities/community/update.rs | 23 +++++++-- crates/apub/src/activities/deletion/delete.rs | 28 ++++++++++- crates/apub/src/activities/deletion/mod.rs | 5 +- .../src/activities/deletion/undo_delete.rs | 16 ++++++- .../apub/src/activities/following/accept.rs | 8 +--- .../apub/src/activities/following/follow.rs | 3 ++ crates/apub/src/activities/mod.rs | 47 ++++++------------- .../src/activities/post/create_or_update.rs | 23 +++++++-- .../private_message/create_or_update.rs | 4 +- crates/apub/src/activities/report.rs | 3 +- .../apub/src/activities/voting/undo_vote.rs | 19 +++++++- crates/apub/src/activities/voting/vote.rs | 35 ++++++++++++-- .../apub/src/collections/community_outbox.rs | 7 ++- crates/apub/src/http/community.rs | 18 +++++-- crates/apub/src/lib.rs | 35 +------------- crates/apub/src/objects/comment.rs | 33 +++++++++---- crates/apub/src/objects/community.rs | 20 ++------ crates/apub/src/objects/post.rs | 44 ++++++++++++----- crates/apub/src/objects/private_message.rs | 12 ++--- crates/apub_lib/src/traits.rs | 1 - crates/apub_lib_derive/src/lib.rs | 20 +------- 28 files changed, 386 insertions(+), 185 deletions(-) diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 537563e9..f8469be9 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -2,8 +2,10 @@ use crate::{ activities::{ check_community_deleted_or_removed, comment::{collect_non_local_mentions, get_notif_recipients}, - community::{announce::AnnouncableActivities, send_to_community}, - extract_community, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -108,12 +110,11 @@ impl ActivityHandler for CreateOrUpdateComment { request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; - let community = extract_community(&self.cc, context, request_counter).await?; - let community_id = ObjectId::new(community.actor_id()); let post = self.object.get_parents(context, request_counter).await?.0; + let community = self.get_community(context, request_counter).await?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &community_id, context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_domains_match(self.actor.inner(), self.object.id_unchecked())?; check_community_deleted_or_removed(&community)?; check_post_deleted_or_removed(&post)?; @@ -144,6 +145,22 @@ impl ActivityHandler for CreateOrUpdateComment { } } +#[async_trait::async_trait(?Send)] +impl GetCommunity for CreateOrUpdateComment { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let post = self.object.get_parents(context, request_counter).await?.0; + let community = blocking(context.pool(), move |conn| { + Community::read(conn, post.community_id) + }) + .await??; + Ok(community.into()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index 6df6f812..876fcc94 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -1,6 +1,10 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + get_community_from_moderators_url, + send_to_community, + }, generate_activity_id, verify_activity, verify_add_remove_moderator_target, @@ -91,7 +95,8 @@ impl ActivityHandler for AddMod { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; verify_add_remove_moderator_target(&self.target, &self.cc[0])?; Ok(()) @@ -102,7 +107,7 @@ impl ActivityHandler for AddMod { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = self.cc[0].dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let new_mod = self.object.dereference(context, request_counter).await?; // If we had to refetch the community while parsing the activity, then the new mod has already @@ -126,3 +131,14 @@ impl ActivityHandler for AddMod { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for AddMod { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + get_community_from_moderators_url(&self.target, context, request_counter).await + } +} diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index deca2b73..6165eb2f 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -13,7 +13,6 @@ use crate::{ generate_activity_id, post::create_or_update::CreateOrUpdatePost, verify_activity, - verify_community, verify_is_public, voting::{undo_vote::UndoVote, vote::Vote}, }, @@ -23,7 +22,6 @@ use crate::{ insert_activity, objects::community::ApubCommunity, send_lemmy_activity, - CommunityType, }; use activitystreams::{ activity::kind::AnnounceType, @@ -35,6 +33,7 @@ use activitystreams::{ use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, + verify::verify_urls_match, }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; @@ -58,6 +57,41 @@ pub enum AnnouncableActivities { RemoveMod(RemoveMod), } +#[async_trait::async_trait(?Send)] +pub(crate) trait GetCommunity { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result; +} + +#[async_trait::async_trait(?Send)] +impl GetCommunity for AnnouncableActivities { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + use AnnouncableActivities::*; + let community = match self { + CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?, + CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?, + Vote(a) => a.get_community(context, request_counter).await?, + UndoVote(a) => a.get_community(context, request_counter).await?, + Delete(a) => a.get_community(context, request_counter).await?, + UndoDelete(a) => a.get_community(context, request_counter).await?, + UpdateCommunity(a) => a.get_community(context, request_counter).await?, + BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, + UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, + AddMod(a) => a.get_community(context, request_counter).await?, + RemoveMod(a) => a.get_community(context, request_counter).await?, + }; + verify_urls_match(self.actor(), &community.actor_id())?; + Ok(community) + } +} + #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct AnnounceActivity { @@ -85,7 +119,7 @@ impl AnnounceActivity { actor: ObjectId::new(community.actor_id()), to: vec![public()], object, - cc: vec![community.followers_url()], + cc: vec![community.followers_url.clone().into_inner()], kind: AnnounceType::Announce, id: generate_activity_id( &AnnounceType::Announce, @@ -109,7 +143,6 @@ impl ActivityHandler for AnnounceActivity { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_community(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs index 64a030c8..35cde2e6 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/community/block_user.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -44,6 +47,7 @@ pub struct BlockUserFromCommunity { to: Vec, pub(in crate::activities::community) object: ObjectId, cc: [ObjectId; 1], + target: ObjectId, #[serde(rename = "type")] kind: BlockType, id: Url, @@ -65,6 +69,7 @@ impl BlockUserFromCommunity { to: vec![public()], object: ObjectId::new(target.actor_id()), cc: [ObjectId::new(community.actor_id())], + target: ObjectId::new(community.actor_id()), kind: BlockType::Block, id: generate_activity_id( BlockType::Block, @@ -100,7 +105,8 @@ impl ActivityHandler for BlockUserFromCommunity { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; Ok(()) } @@ -110,7 +116,7 @@ impl ActivityHandler for BlockUserFromCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = self.cc[0].dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let blocked_user = self.object.dereference(context, request_counter).await?; let community_user_ban_form = CommunityPersonBanForm { @@ -138,3 +144,14 @@ impl ActivityHandler for BlockUserFromCommunity { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for BlockUserFromCommunity { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self.target.dereference(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index f69ee026..9db9d814 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -1,10 +1,10 @@ use crate::{ activities::community::announce::{AnnouncableActivities, AnnounceActivity}, check_is_apub_id_valid, + fetcher::object_id::ObjectId, insert_activity, objects::community::ApubCommunity, send_lemmy_activity, - CommunityType, }; use itertools::Itertools; use lemmy_apub_lib::traits::ActorType; @@ -61,3 +61,14 @@ pub(crate) async fn send_to_community( Ok(()) } + +async fn get_community_from_moderators_url( + moderators: &Url, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result { + let community_id = Url::parse(&moderators.to_string().replace("/moderators", ""))?; + ObjectId::new(community_id) + .dereference(context, request_counter) + .await +} diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index d1a5675a..f08ae964 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -1,6 +1,10 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + get_community_from_moderators_url, + send_to_community, + }, generate_activity_id, verify_activity, verify_add_remove_moderator_target, @@ -43,7 +47,6 @@ pub struct RemoveMod { cc: [ObjectId; 1], #[serde(rename = "type")] kind: RemoveType, - // if target is set, this is means remove mod from community pub(in crate::activities) target: Url, id: Url, #[serde(rename = "@context")] @@ -91,7 +94,8 @@ impl ActivityHandler for RemoveMod { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; verify_add_remove_moderator_target(&self.target, &self.cc[0])?; Ok(()) @@ -102,7 +106,7 @@ impl ActivityHandler for RemoveMod { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = self.cc[0].dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let remove_mod = self.object.dereference(context, request_counter).await?; let form = CommunityModeratorForm { @@ -117,3 +121,14 @@ impl ActivityHandler for RemoveMod { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for RemoveMod { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + get_community_from_moderators_url(&self.target, context, request_counter).await + } +} diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs index ee36d1ae..c33c81e6 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/community/undo_block_user.rs @@ -1,7 +1,7 @@ use crate::{ activities::{ community::{ - announce::AnnouncableActivities, + announce::{AnnouncableActivities, GetCommunity}, block_user::BlockUserFromCommunity, send_to_community, }, @@ -92,7 +92,8 @@ impl ActivityHandler for UndoBlockUserFromCommunity { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) @@ -103,7 +104,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = self.cc[0].dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let blocked_user = self .object .object @@ -123,3 +124,14 @@ impl ActivityHandler for UndoBlockUserFromCommunity { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for UndoBlockUserFromCommunity { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self.object.get_community(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index 6c78dbfb..13837433 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -90,7 +93,8 @@ impl ActivityHandler for UpdateCommunity { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; Ok(()) } @@ -100,8 +104,7 @@ impl ActivityHandler for UpdateCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let cc = self.cc[0].clone(); - let community = cc.dereference(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; let updated_community = Group::from_apub_to_form( &self.object, @@ -135,3 +138,15 @@ impl ActivityHandler for UpdateCommunity { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for UpdateCommunity { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let cid = ObjectId::new(self.object.id.clone()); + cid.dereference(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 7593c5bf..8797ed6b 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, deletion::{ receive_delete_action, verify_delete_activity, @@ -245,3 +248,26 @@ pub(in crate::activities) async fn receive_remove_action( } Ok(()) } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for Delete { + async fn get_community( + &self, + context: &LemmyContext, + _request_counter: &mut i32, + ) -> Result { + let community_id = match DeletableObjects::read_from_db(&self.object, context).await? { + DeletableObjects::Community(c) => c.id, + DeletableObjects::Comment(c) => { + let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??; + post.community_id + } + DeletableObjects::Post(p) => p.community_id, + }; + let community = blocking(context.pool(), move |conn| { + Community::read(conn, community_id) + }) + .await??; + Ok(community.into()) + } +} diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index 4352afe8..f523ca71 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -94,7 +94,7 @@ pub(in crate::activities) async fn verify_delete_activity( if c.local { // can only do this check for local community, in remote case it would try to fetch the // deleted community (which fails) - verify_person_in_community(&actor, community_id, context, request_counter).await?; + verify_person_in_community(&actor, &c, context, request_counter).await?; } // community deletion is always a mod (or admin) action verify_mod_action( @@ -140,7 +140,8 @@ async fn verify_delete_activity_post_or_comment( request_counter: &mut i32, ) -> Result<(), LemmyError> { let actor = ObjectId::new(activity.actor().clone()); - verify_person_in_community(&actor, community_id, context, request_counter).await?; + let community = community_id.dereference(context, request_counter).await?; + verify_person_in_community(&actor, &community, context, request_counter).await?; if is_mod_action { verify_mod_action(&actor, community_id, context, request_counter).await?; } else { diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index 87c8eae7..d2816b11 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, deletion::{ delete::Delete, receive_delete_action, @@ -166,3 +169,14 @@ impl UndoDelete { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for UndoDelete { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self.object.get_community(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index 78d12b52..92f53184 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -1,10 +1,5 @@ use crate::{ - activities::{ - following::follow::FollowCommunity, - generate_activity_id, - verify_activity, - verify_community, - }, + activities::{following::follow::FollowCommunity, generate_activity_id, verify_activity}, context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, @@ -84,7 +79,6 @@ impl ActivityHandler for AcceptFollowCommunity { verify_activity(self, &context.settings())?; verify_urls_match(self.to[0].inner(), self.object.actor())?; verify_urls_match(self.actor(), self.object.to[0].inner())?; - verify_community(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 82d2d540..4710f70f 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -4,6 +4,7 @@ use crate::{ generate_activity_id, verify_activity, verify_person, + verify_person_in_community, }, context::lemmy_context, fetcher::object_id::ObjectId, @@ -97,6 +98,8 @@ impl ActivityHandler for FollowCommunity { verify_activity(self, &context.settings())?; verify_urls_match(self.to[0].inner(), self.object.inner())?; verify_person(&self.actor, context, request_counter).await?; + let community = self.to[0].dereference(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 14a67394..b180b6f3 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -1,5 +1,4 @@ use crate::{ - check_community_or_site_ban, check_is_apub_id_valid, fetcher::object_id::ObjectId, generate_moderators_url, @@ -10,11 +9,13 @@ use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{traits::ActivityFields, verify::verify_domains_match}; use lemmy_db_schema::source::community::Community; -use lemmy_db_views_actor::community_view::CommunityView; +use lemmy_db_views_actor::{ + community_person_ban_view::CommunityPersonBanView, + community_view::CommunityView, +}; use lemmy_utils::{settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; -use std::ops::Deref; use strum_macros::ToString; use url::{ParseError, Url}; use uuid::Uuid; @@ -48,44 +49,26 @@ async fn verify_person( Ok(()) } -pub(crate) async fn extract_community( - cc: &[Url], - context: &LemmyContext, - request_counter: &mut i32, -) -> Result { - let mut cc_iter = cc.iter(); - loop { - if let Some(cid) = cc_iter.next() { - let cid = ObjectId::new(cid.clone()); - if let Ok(c) = cid.dereference(context, request_counter).await { - break Ok(c); - } - } else { - return Err(anyhow!("No community found in cc").into()); - } - } -} - /// Fetches the person and community to verify their type, then checks if person is banned from site /// or community. pub(crate) async fn verify_person_in_community( person_id: &ObjectId, - community_id: &ObjectId, + community: &ApubCommunity, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = community_id.dereference(context, request_counter).await?; let person = person_id.dereference(context, request_counter).await?; - check_community_or_site_ban(person.deref(), community.id, context.pool()).await -} + if person.banned { + return Err(anyhow!("Person is banned from site").into()); + } + let person_id = person.id; + let community_id = community.id; + let is_banned = + move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok(); + if blocking(context.pool(), is_banned).await? { + return Err(anyhow!("Person is banned from community").into()); + } -/// Simply check that the url actually refers to a valid group. -async fn verify_community( - community_id: &ObjectId, - context: &LemmyContext, - request_counter: &mut i32, -) -> Result<(), LemmyError> { - community_id.dereference(context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index b3c0b694..b18d100e 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -1,7 +1,10 @@ use crate::{ activities::{ check_community_deleted_or_removed, - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -99,8 +102,8 @@ impl ActivityHandler for CreateOrUpdatePost { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - let community = self.cc[0].dereference(context, request_counter).await?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; check_community_deleted_or_removed(&community)?; match self.kind { @@ -148,3 +151,17 @@ impl ActivityHandler for CreateOrUpdatePost { Ok(()) } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for CreateOrUpdatePost { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self + .object + .extract_community(context, request_counter) + .await + } +} diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs index d6552b37..22f8edb8 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -4,7 +4,7 @@ use crate::{ fetcher::object_id::ObjectId, objects::{ person::ApubPerson, - private_message::{ApubPrivateMessage, Note}, + private_message::{ApubPrivateMessage, ChatMessage}, }, send_lemmy_activity, }; @@ -29,7 +29,7 @@ pub struct CreateOrUpdatePrivateMessage { id: Url, actor: ObjectId, to: [ObjectId; 1], - object: Note, + object: ChatMessage, #[serde(rename = "type")] kind: CreateOrUpdateType, #[serde(flatten)] diff --git a/crates/apub/src/activities/report.rs b/crates/apub/src/activities/report.rs index d0b1f971..24041b0f 100644 --- a/crates/apub/src/activities/report.rs +++ b/crates/apub/src/activities/report.rs @@ -91,7 +91,8 @@ impl ActivityHandler for Report { request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.to[0], context, request_counter).await?; + let community = self.to[0].dereference(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index 1ac1d4d8..6d63e905 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -96,7 +99,8 @@ impl ActivityHandler for UndoVote { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_urls_match(self.actor(), self.object.actor())?; self.object.verify(context, request_counter).await?; Ok(()) @@ -119,3 +123,14 @@ impl ActivityHandler for UndoVote { } } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for UndoVote { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + self.object.get_community(context, request_counter).await + } +} diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 166ce733..4d669728 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -1,6 +1,9 @@ use crate::{ activities::{ - community::{announce::AnnouncableActivities, send_to_community}, + community::{ + announce::{AnnouncableActivities, GetCommunity}, + send_to_community, + }, generate_activity_id, verify_activity, verify_is_public, @@ -19,7 +22,11 @@ use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, }; -use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud}; +use lemmy_db_schema::{ + newtypes::CommunityId, + source::{community::Community, post::Post}, + traits::Crud, +}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; @@ -120,7 +127,8 @@ impl ActivityHandler for Vote { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; - verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; + verify_person_in_community(&self.actor, &community, context, request_counter).await?; Ok(()) } @@ -137,3 +145,24 @@ impl ActivityHandler for Vote { } } } + +#[async_trait::async_trait(?Send)] +impl GetCommunity for Vote { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let object = self.object.dereference(context, request_counter).await?; + let cid = match object { + PostOrComment::Post(p) => p.community_id, + PostOrComment::Comment(c) => { + blocking(context.pool(), move |conn| Post::read(conn, c.post_id)) + .await?? + .community_id + } + }; + let community = blocking(context.pool(), move |conn| Community::read(conn, cid)).await??; + Ok(community.into()) + } +} diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index faf33849..24465c95 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -6,8 +6,11 @@ use crate::{ objects::{person::ApubPerson, post::ApubPost}, }; use activitystreams::{ - base::AnyBase, chrono::NaiveDateTime, collection::kind::OrderedCollectionType, - primitives::OneOrMany, url::Url, + base::AnyBase, + chrono::NaiveDateTime, + collection::kind::OrderedCollectionType, + primitives::OneOrMany, + url::Url, }; use lemmy_api_common::blocking; use lemmy_apub_lib::{ diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index a7e66d8a..f39f7b6f 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -1,9 +1,9 @@ use crate::{ activities::{ - community::announce::{AnnouncableActivities, AnnounceActivity}, - extract_community, + community::announce::{AnnouncableActivities, AnnounceActivity, GetCommunity}, following::{follow::FollowCommunity, undo::UndoFollowCommunity}, report::Report, + verify_person_in_community, }, collections::{ community_moderators::ApubCommunityModerators, @@ -27,7 +27,10 @@ use activitystreams::{ }; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use lemmy_api_common::blocking; -use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject}; +use lemmy_apub_lib::{ + traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, + verify::verify_domains_match, +}; use lemmy_db_schema::source::community::Community; use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; use lemmy_utils::LemmyError; @@ -92,12 +95,17 @@ pub(in crate::http) async fn receive_group_inbox( context: &LemmyContext, ) -> Result { let res = receive_activity(request, activity.clone(), context).await; - if let GroupInboxActivities::AnnouncableActivities(announcable) = activity.clone() { - let community = extract_community(&announcable.cc(), context, &mut 0).await?; + + if let GroupInboxActivities::AnnouncableActivities(announcable) = activity { + let community = announcable.get_community(context, &mut 0).await?; + let actor_id = ObjectId::new(announcable.actor().clone()); + verify_domains_match(&community.actor_id(), announcable.id_unchecked())?; + verify_person_in_community(&actor_id, &community, context, &mut 0).await?; if community.local { AnnounceActivity::send(announcable, &community, vec![], context).await?; } } + res } diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index a900ad3d..3bf3ff25 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -17,12 +17,7 @@ use lemmy_apub_lib::{ traits::ActorType, webfinger::{webfinger_resolve_actor, WebfingerType}, }; -use lemmy_db_schema::{ - newtypes::{CommunityId, DbUrl}, - source::{activity::Activity, person::Person}, - DbPool, -}; -use lemmy_db_views_actor::community_person_ban_view::CommunityPersonBanView; +use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, DbPool}; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; use log::info; @@ -96,16 +91,6 @@ pub(crate) fn check_is_apub_id_valid( Ok(()) } -#[async_trait::async_trait(?Send)] -pub trait CommunityType { - fn followers_url(&self) -> Url; - async fn get_follower_inboxes( - &self, - pool: &DbPool, - settings: &Settings, - ) -> Result, LemmyError>; -} - pub enum EndpointType { Community, Person, @@ -211,24 +196,6 @@ where Ok(()) } -async fn check_community_or_site_ban( - person: &Person, - community_id: CommunityId, - pool: &DbPool, -) -> Result<(), LemmyError> { - if person.banned { - return Err(anyhow!("Person is banned from site").into()); - } - let person_id = person.id; - let is_banned = - move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok(); - if blocking(pool, is_banned).await? { - return Err(anyhow!("Person is banned from community").into()); - } - - Ok(()) -} - pub(crate) async fn send_lemmy_activity( context: &LemmyContext, activity: &T, diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 927127d8..335605a6 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -2,7 +2,13 @@ use crate::{ activities::{verify_is_public, verify_person_in_community}, context::lemmy_context, fetcher::object_id::ObjectId, - objects::{person::ApubPerson, post::ApubPost, tombstone::Tombstone, Source}, + objects::{ + community::ApubCommunity, + person::ApubPerson, + post::ApubPost, + tombstone::Tombstone, + Source, + }, PostOrComment, }; use activitystreams::{ @@ -121,22 +127,17 @@ impl Note { ) -> Result<(), LemmyError> { let (post, _parent_comment_id) = self.get_parents(context, request_counter).await?; let community_id = post.community_id; - let community = blocking(context.pool(), move |conn| { + let community: ApubCommunity = blocking(context.pool(), move |conn| { Community::read(conn, community_id) }) - .await??; + .await?? + .into(); if post.locked { return Err(anyhow!("Post is locked").into()); } verify_domains_match(self.attributed_to.inner(), &self.id)?; - verify_person_in_community( - &self.attributed_to, - &ObjectId::new(community.actor_id), - context, - request_counter, - ) - .await?; + verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; verify_is_public(&self.to)?; Ok(()) } @@ -247,6 +248,18 @@ impl ApubObject for ApubComment { .dereference(context, request_counter) .await?; let (post, parent_comment_id) = note.get_parents(context, request_counter).await?; + let community_id = post.community_id; + let community = blocking(context.pool(), move |conn| { + Community::read(conn, community_id) + }) + .await??; + verify_person_in_community( + ¬e.attributed_to, + &community.into(), + context, + request_counter, + ) + .await?; if post.locked { return Err(anyhow!("Post is locked").into()); } diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index e71addba..a38e759f 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -10,7 +10,6 @@ use crate::{ generate_moderators_url, generate_outbox_url, objects::{get_summary_from_string_or_source, tombstone::Tombstone, ImageObject, Source}, - CommunityType, }; use activitystreams::{ actor::{kind::GroupType, Endpoints}, @@ -55,7 +54,7 @@ pub struct Group { context: OneOrMany, #[serde(rename = "type")] kind: GroupType, - id: Url, + pub(crate) id: Url, /// username, set at account creation and can never be changed preferred_username: String, /// title (can be changed at any time) @@ -81,16 +80,12 @@ pub struct Group { } impl Group { - pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> { - verify_domains_match(&self.id, expected_domain)?; - Ok(&self.id) - } pub(crate) async fn from_apub_to_form( group: &Group, expected_domain: &Url, settings: &Settings, ) -> Result { - let actor_id = Some(group.id(expected_domain)?.clone().into()); + verify_domains_match(expected_domain, &group.id)?; let name = group.preferred_username.clone(); let title = group.name.clone(); let description = get_summary_from_string_or_source(&group.summary, &group.source); @@ -110,7 +105,7 @@ impl Group { updated: group.updated.map(|u| u.naive_local()), deleted: None, nsfw: Some(group.sensitive.unwrap_or(false)), - actor_id, + actor_id: Some(group.id.clone().into()), local: Some(false), private_key: None, public_key: Some(group.public_key.public_key_pem.clone()), @@ -283,14 +278,9 @@ impl ActorType for ApubCommunity { } } -#[async_trait::async_trait(?Send)] -impl CommunityType for Community { - fn followers_url(&self) -> Url { - self.followers_url.clone().into() - } - +impl ApubCommunity { /// For a given community, returns the inboxes of all followers. - async fn get_follower_inboxes( + pub(crate) async fn get_follower_inboxes( &self, pool: &DbPool, settings: &Settings, diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 8b016188..f15bb5ba 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -1,8 +1,14 @@ use crate::{ - activities::{extract_community, verify_is_public, verify_person_in_community}, + activities::{verify_is_public, verify_person_in_community}, context::lemmy_context, fetcher::object_id::ObjectId, - objects::{person::ApubPerson, tombstone::Tombstone, ImageObject, Source}, + objects::{ + community::ApubCommunity, + person::ApubPerson, + tombstone::Tombstone, + ImageObject, + Source, + }, }; use activitystreams::{ base::AnyBase, @@ -11,10 +17,11 @@ use activitystreams::{ public, unparsed::Unparsed, }; +use anyhow::anyhow; use chrono::{DateTime, FixedOffset, NaiveDateTime}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ - traits::{ActorType, ApubObject}, + traits::ApubObject, values::{MediaTypeHtml, MediaTypeMarkdown}, verify::verify_domains_match, }; @@ -94,20 +101,32 @@ impl Page { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = extract_community(&self.to, context, request_counter).await?; + let community = self.extract_community(context, request_counter).await?; check_slurs(&self.name, &context.settings().slur_regex())?; verify_domains_match(self.attributed_to.inner(), &self.id.clone())?; - verify_person_in_community( - &self.attributed_to, - &ObjectId::new(community.actor_id()), - context, - request_counter, - ) - .await?; + verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; verify_is_public(&self.to.clone())?; Ok(()) } + + pub(crate) async fn extract_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let mut to_iter = self.to.iter(); + loop { + if let Some(cid) = to_iter.next() { + let cid = ObjectId::new(cid.clone()); + if let Ok(c) = cid.dereference(context, request_counter).await { + break Ok(c); + } + } else { + return Err(anyhow!("No community found in cc").into()); + } + } + } } #[derive(Clone, Debug)] @@ -223,7 +242,8 @@ impl ApubObject for ApubPost { .attributed_to .dereference(context, request_counter) .await?; - let community = extract_community(&page.to, context, request_counter).await?; + let community = page.extract_community(context, request_counter).await?; + verify_person_in_community(&page.attributed_to, &community, context, request_counter).await?; let thumbnail_url: Option = page.image.clone().map(|i| i.url); let (metadata_res, pictrs_thumbnail) = if let Some(url) = &page.url { diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 407ae810..56200d1b 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -36,7 +36,7 @@ use url::Url; #[skip_serializing_none] #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct Note { +pub struct ChatMessage { #[serde(rename = "@context")] context: OneOrMany, r#type: ChatMessageType, @@ -58,7 +58,7 @@ pub enum ChatMessageType { ChatMessage, } -impl Note { +impl ChatMessage { pub(crate) fn id_unchecked(&self) -> &Url { &self.id } @@ -103,7 +103,7 @@ impl From for ApubPrivateMessage { #[async_trait::async_trait(?Send)] impl ApubObject for ApubPrivateMessage { type DataType = LemmyContext; - type ApubType = Note; + type ApubType = ChatMessage; type TombstoneType = Tombstone; fn last_refreshed_at(&self) -> Option { @@ -128,7 +128,7 @@ impl ApubObject for ApubPrivateMessage { unimplemented!() } - async fn to_apub(&self, context: &LemmyContext) -> Result { + async fn to_apub(&self, context: &LemmyContext) -> Result { let creator_id = self.creator_id; let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??; @@ -136,7 +136,7 @@ impl ApubObject for ApubPrivateMessage { let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; - let note = Note { + let note = ChatMessage { context: lemmy_context(), r#type: ChatMessageType::ChatMessage, id: self.ap_id.clone().into(), @@ -160,7 +160,7 @@ impl ApubObject for ApubPrivateMessage { } async fn from_apub( - note: &Note, + note: &ChatMessage, context: &LemmyContext, expected_domain: &Url, request_counter: &mut i32, diff --git a/crates/apub_lib/src/traits.rs b/crates/apub_lib/src/traits.rs index 2240946d..8d819a27 100644 --- a/crates/apub_lib/src/traits.rs +++ b/crates/apub_lib/src/traits.rs @@ -8,7 +8,6 @@ use url::Url; pub trait ActivityFields { fn id_unchecked(&self) -> &Url; fn actor(&self) -> &Url; - fn cc(&self) -> Vec; } #[async_trait::async_trait(?Send)] diff --git a/crates/apub_lib_derive/src/lib.rs b/crates/apub_lib_derive/src/lib.rs index 44185d14..1f8d12a7 100644 --- a/crates/apub_lib_derive/src/lib.rs +++ b/crates/apub_lib_derive/src/lib.rs @@ -145,36 +145,18 @@ pub fn derive_activity_fields(input: proc_macro::TokenStream) -> proc_macro::Tok let impl_actor = variants .iter() .map(|v| generate_match_arm(&name, v, "e! {a.actor()})); - let impl_cc = variants - .iter() - .map(|v| generate_match_arm(&name, v, "e! {a.cc()})); quote! { impl #impl_generics lemmy_apub_lib::traits::ActivityFields for #name #ty_generics #where_clause { fn id_unchecked(&self) -> &url::Url { match self { #(#impl_id)* } } fn actor(&self) -> &url::Url { match self { #(#impl_actor)* } } - fn cc(&self) -> Vec { match self { #(#impl_cc)* } } } } } - Data::Struct(s) => { - // check if the struct has a field "cc", and generate impl for cc() function depending on that - let has_cc = if let syn::Fields::Named(n) = s.fields { - n.named - .iter() - .any(|i| format!("{}", i.ident.as_ref().unwrap()) == "cc") - } else { - unimplemented!() - }; - let cc_impl = if has_cc { - quote! {self.cc.iter().map(|i| i.clone().into()).collect()} - } else { - quote! {vec![]} - }; + Data::Struct(_) => { quote! { impl #impl_generics lemmy_apub_lib::traits::ActivityFields for #name #ty_generics #where_clause { fn id_unchecked(&self) -> &url::Url { &self.id } fn actor(&self) -> &url::Url { &self.actor.inner() } - fn cc(&self) -> Vec { #cc_impl } } } } From 2d04ff93f5ccdb8d5e7af54aaabec796d8fe96a8 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 28 Oct 2021 14:47:56 +0200 Subject: [PATCH 08/17] Change activity.cc to Vec in public activities --- .../apub/src/activities/community/add_mod.rs | 8 +++--- .../src/activities/community/block_user.rs | 6 ++--- .../src/activities/community/remove_mod.rs | 8 +++--- .../activities/community/undo_block_user.rs | 6 ++--- .../apub/src/activities/community/update.rs | 6 ++--- crates/apub/src/activities/deletion/delete.rs | 7 ++--- crates/apub/src/activities/deletion/mod.rs | 27 +++++++------------ .../src/activities/deletion/undo_delete.rs | 7 ++--- crates/apub/src/activities/mod.rs | 8 +++--- .../src/activities/post/create_or_update.rs | 6 ++--- .../apub/src/activities/voting/undo_vote.rs | 4 +-- crates/apub/src/activities/voting/vote.rs | 4 +-- 12 files changed, 45 insertions(+), 52 deletions(-) diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index 876fcc94..11abb5ba 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -45,7 +45,7 @@ pub struct AddMod { to: Vec, object: ObjectId, target: Url, - cc: [ObjectId; 1], + cc: Vec, #[serde(rename = "type")] kind: AddType, id: Url, @@ -71,7 +71,7 @@ impl AddMod { to: vec![public()], object: ObjectId::new(added_mod.actor_id()), target: generate_moderators_url(&community.actor_id)?.into(), - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], kind: AddType::Add, id: id.clone(), context: lemmy_context(), @@ -97,8 +97,8 @@ impl ActivityHandler for AddMod { verify_activity(self, &context.settings())?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; - verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; - verify_add_remove_moderator_target(&self.target, &self.cc[0])?; + verify_mod_action(&self.actor, &community, context, request_counter).await?; + verify_add_remove_moderator_target(&self.target, &community)?; Ok(()) } diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs index 35cde2e6..21493214 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/community/block_user.rs @@ -46,7 +46,7 @@ pub struct BlockUserFromCommunity { actor: ObjectId, to: Vec, pub(in crate::activities::community) object: ObjectId, - cc: [ObjectId; 1], + cc: Vec, target: ObjectId, #[serde(rename = "type")] kind: BlockType, @@ -68,7 +68,7 @@ impl BlockUserFromCommunity { actor: ObjectId::new(actor.actor_id()), to: vec![public()], object: ObjectId::new(target.actor_id()), - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], target: ObjectId::new(community.actor_id()), kind: BlockType::Block, id: generate_activity_id( @@ -107,7 +107,7 @@ impl ActivityHandler for BlockUserFromCommunity { verify_activity(self, &context.settings())?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; - verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; + verify_mod_action(&self.actor, &community, context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index f08ae964..786740d5 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -44,7 +44,7 @@ pub struct RemoveMod { actor: ObjectId, to: Vec, pub(in crate::activities) object: ObjectId, - cc: [ObjectId; 1], + cc: Vec, #[serde(rename = "type")] kind: RemoveType, pub(in crate::activities) target: Url, @@ -73,7 +73,7 @@ impl RemoveMod { target: generate_moderators_url(&community.actor_id)?.into(), id: id.clone(), context: lemmy_context(), - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], kind: RemoveType::Remove, unparsed: Default::default(), }; @@ -96,8 +96,8 @@ impl ActivityHandler for RemoveMod { verify_activity(self, &context.settings())?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; - verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; - verify_add_remove_moderator_target(&self.target, &self.cc[0])?; + verify_mod_action(&self.actor, &community, context, request_counter).await?; + verify_add_remove_moderator_target(&self.target, &community)?; Ok(()) } diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs index c33c81e6..b5e2a69c 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/community/undo_block_user.rs @@ -42,7 +42,7 @@ pub struct UndoBlockUserFromCommunity { actor: ObjectId, to: Vec, object: BlockUserFromCommunity, - cc: [ObjectId; 1], + cc: Vec, #[serde(rename = "type")] kind: UndoType, id: Url, @@ -69,7 +69,7 @@ impl UndoBlockUserFromCommunity { actor: ObjectId::new(actor.actor_id()), to: vec![public()], object: block, - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], kind: UndoType::Undo, id: id.clone(), context: lemmy_context(), @@ -94,7 +94,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity { verify_activity(self, &context.settings())?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; - verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; + verify_mod_action(&self.actor, &community, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index 13837433..b5ad7f92 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -47,7 +47,7 @@ pub struct UpdateCommunity { to: Vec, // TODO: would be nice to use a separate struct here, which only contains the fields updated here object: Group, - cc: [ObjectId; 1], + cc: Vec, #[serde(rename = "type")] kind: UpdateType, id: Url, @@ -71,7 +71,7 @@ impl UpdateCommunity { actor: ObjectId::new(actor.actor_id()), to: vec![public()], object: community.to_apub(context).await?, - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], kind: UpdateType::Update, id: id.clone(), context: lemmy_context(), @@ -95,7 +95,7 @@ impl ActivityHandler for UpdateCommunity { verify_activity(self, &context.settings())?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; - verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; + verify_mod_action(&self.actor, &community, context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 8797ed6b..2677ea90 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -72,7 +72,7 @@ pub struct Delete { actor: ObjectId, to: Vec, pub(in crate::activities::deletion) object: Url, - pub(in crate::activities::deletion) cc: [ObjectId; 1], + pub(in crate::activities::deletion) cc: Vec, #[serde(rename = "type")] kind: DeleteType, /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user @@ -95,10 +95,11 @@ impl ActivityHandler for Delete { ) -> Result<(), LemmyError> { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; + let community = self.get_community(context, request_counter).await?; verify_delete_activity( &self.object, self, - &self.cc[0], + &community, self.summary.is_some(), context, request_counter, @@ -151,7 +152,7 @@ impl Delete { actor: ObjectId::new(actor.actor_id()), to: vec![public()], object: object_id, - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], kind: DeleteType::Delete, summary, id: generate_activity_id( diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index f523ca71..1af02245 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -82,7 +82,7 @@ impl DeletableObjects { pub(in crate::activities) async fn verify_delete_activity( object: &Url, activity: &dyn ActivityFields, - community_id: &ObjectId, + community: &ApubCommunity, is_mod_action: bool, context: &LemmyContext, request_counter: &mut i32, @@ -90,26 +90,20 @@ pub(in crate::activities) async fn verify_delete_activity( let object = DeletableObjects::read_from_db(object, context).await?; let actor = ObjectId::new(activity.actor().clone()); match object { - DeletableObjects::Community(c) => { - if c.local { + DeletableObjects::Community(community) => { + 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(&actor, &c, context, request_counter).await?; + verify_person_in_community(&actor, &community, context, request_counter).await?; } // community deletion is always a mod (or admin) action - verify_mod_action( - &actor, - &ObjectId::new(c.actor_id()), - context, - request_counter, - ) - .await?; + verify_mod_action(&actor, &community, context, request_counter).await?; } DeletableObjects::Post(p) => { verify_delete_activity_post_or_comment( activity, &p.ap_id.clone().into(), - community_id, + community, is_mod_action, context, request_counter, @@ -120,7 +114,7 @@ pub(in crate::activities) async fn verify_delete_activity( verify_delete_activity_post_or_comment( activity, &c.ap_id.clone().into(), - community_id, + community, is_mod_action, context, request_counter, @@ -134,16 +128,15 @@ pub(in crate::activities) async fn verify_delete_activity( async fn verify_delete_activity_post_or_comment( activity: &dyn ActivityFields, object_id: &Url, - community_id: &ObjectId, + community: &ApubCommunity, is_mod_action: bool, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { let actor = ObjectId::new(activity.actor().clone()); - let community = community_id.dereference(context, request_counter).await?; - verify_person_in_community(&actor, &community, context, request_counter).await?; + verify_person_in_community(&actor, community, context, request_counter).await?; if is_mod_action { - verify_mod_action(&actor, community_id, context, request_counter).await?; + verify_mod_action(&actor, community, context, request_counter).await?; } else { // domain of post ap_id and post.creator ap_id are identical, so we just check the former verify_domains_match(activity.actor(), object_id)?; diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index d2816b11..d881ab42 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -48,7 +48,7 @@ pub struct UndoDelete { actor: ObjectId, to: Vec, object: Delete, - cc: [ObjectId; 1], + cc: Vec, #[serde(rename = "type")] kind: UndoType, id: Url, @@ -69,10 +69,11 @@ impl ActivityHandler for UndoDelete { verify_is_public(&self.to)?; verify_activity(self, &context.settings())?; self.object.verify(context, request_counter).await?; + let community = self.get_community(context, request_counter).await?; verify_delete_activity( &self.object.object, self, - &self.cc[0], + &community, self.object.summary.is_some(), context, request_counter, @@ -124,7 +125,7 @@ impl UndoDelete { actor: ObjectId::new(actor.actor_id()), to: vec![public()], object, - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], kind: UndoType::Undo, id: id.clone(), context: lemmy_context(), diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index b180b6f3..63f79caa 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -83,12 +83,10 @@ fn verify_activity(activity: &dyn ActivityFields, settings: &Settings) -> Result /// is not federated, we cant verify their actions remotely. pub(crate) async fn verify_mod_action( actor_id: &ObjectId, - community_id: &ObjectId, + community: &ApubCommunity, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let community = community_id.dereference_local(context).await?; - if community.local { let actor = actor_id.dereference(context, request_counter).await?; @@ -111,9 +109,9 @@ pub(crate) async fn verify_mod_action( /// /c/community/moderators. Any different values are unsupported. fn verify_add_remove_moderator_target( target: &Url, - community: &ObjectId, + community: &ApubCommunity, ) -> Result<(), LemmyError> { - if target != &generate_moderators_url(&community.clone().into())?.into_inner() { + if target != &generate_moderators_url(&community.actor_id)?.into_inner() { return Err(anyhow!("Unkown target url").into()); } Ok(()) diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index b18d100e..44498823 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -40,7 +40,7 @@ pub struct CreateOrUpdatePost { actor: ObjectId, to: Vec, object: Page, - cc: [ObjectId; 1], + cc: Vec, #[serde(rename = "type")] kind: CreateOrUpdateType, id: Url, @@ -66,7 +66,7 @@ impl CreateOrUpdatePost { actor: ObjectId::new(actor.actor_id()), to: vec![public()], object: post.to_apub(context).await?, - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], kind, id: id.clone(), context: lemmy_context(), @@ -123,7 +123,7 @@ impl ActivityHandler for CreateOrUpdatePost { CreateOrUpdateType::Update => { let is_mod_action = self.object.is_mod_action(context).await?; if is_mod_action { - verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?; + verify_mod_action(&self.actor, &community, context, request_counter).await?; } else { verify_domains_match(self.actor.inner(), self.object.id_unchecked())?; verify_urls_match(self.actor(), self.object.attributed_to.inner())?; diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index 6d63e905..144cbfc1 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -45,7 +45,7 @@ pub struct UndoVote { actor: ObjectId, to: Vec, object: Vote, - cc: [ObjectId; 1], + cc: Vec, #[serde(rename = "type")] kind: UndoType, id: Url, @@ -78,7 +78,7 @@ impl UndoVote { actor: ObjectId::new(actor.actor_id()), to: vec![public()], object, - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], kind: UndoType::Undo, id: id.clone(), context: lemmy_context(), diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 4d669728..5b08390f 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -67,7 +67,7 @@ pub struct Vote { actor: ObjectId, to: Vec, pub(in crate::activities::voting) object: ObjectId, - cc: [ObjectId; 1], + cc: Vec, #[serde(rename = "type")] pub(in crate::activities::voting) kind: VoteType, id: Url, @@ -89,7 +89,7 @@ impl Vote { actor: ObjectId::new(actor.actor_id()), to: vec![public()], object: ObjectId::new(object.ap_id()), - cc: [ObjectId::new(community.actor_id())], + cc: vec![community.actor_id()], kind: kind.clone(), id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?, context: lemmy_context(), From d89156810d1299117d877c75d1d3193a413d6997 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 28 Oct 2021 17:25:26 +0200 Subject: [PATCH 09/17] Move @context out of object/activity definitions --- crates/apub/assets/lemmy-comment.json | 19 ----- crates/apub/assets/lemmy-community.json | 19 ----- crates/apub/assets/lemmy-person.json | 19 ----- crates/apub/assets/lemmy-post.json | 19 ----- crates/apub/assets/lemmy-private-message.json | 19 ----- .../activities/comment/create_or_update.rs | 12 +--- .../apub/src/activities/community/add_mod.rs | 12 +--- .../apub/src/activities/community/announce.rs | 14 +--- .../src/activities/community/block_user.rs | 12 +--- crates/apub/src/activities/community/mod.rs | 6 +- .../src/activities/community/remove_mod.rs | 12 +--- .../activities/community/undo_block_user.rs | 12 +--- .../apub/src/activities/community/update.rs | 12 +--- crates/apub/src/activities/deletion/delete.rs | 12 +--- .../src/activities/deletion/undo_delete.rs | 12 +--- .../apub/src/activities/following/accept.rs | 19 ++--- .../apub/src/activities/following/follow.rs | 13 +--- crates/apub/src/activities/following/undo.rs | 13 +--- crates/apub/src/activities/mod.rs | 53 +++++++++++++- .../src/activities/post/create_or_update.rs | 6 +- .../private_message/create_or_update.rs | 15 ++-- .../src/activities/private_message/delete.rs | 14 +--- .../activities/private_message/undo_delete.rs | 13 +--- crates/apub/src/activities/report.rs | 19 ++--- .../apub/src/activities/voting/undo_vote.rs | 12 +--- crates/apub/src/activities/voting/vote.rs | 6 +- .../src/collections/community_moderators.rs | 13 +--- .../apub/src/collections/community_outbox.rs | 14 +--- crates/apub/src/context.rs | 69 ++++++++++++------- crates/apub/src/fetcher/object_id.rs | 1 - crates/apub/src/fetcher/post_or_comment.rs | 2 +- crates/apub/src/fetcher/search.rs | 2 +- crates/apub/src/http/community.rs | 8 +-- crates/apub/src/http/mod.rs | 9 +-- crates/apub/src/http/person.rs | 8 +-- crates/apub/src/lib.rs | 50 +------------- crates/apub/src/objects/comment.rs | 18 +---- crates/apub/src/objects/community.rs | 9 +-- crates/apub/src/objects/person.rs | 19 ++--- crates/apub/src/objects/post.rs | 6 -- crates/apub/src/objects/private_message.rs | 18 ++--- crates/apub/src/objects/tombstone.rs | 12 +--- 42 files changed, 179 insertions(+), 473 deletions(-) diff --git a/crates/apub/assets/lemmy-comment.json b/crates/apub/assets/lemmy-comment.json index c40e893c..2a20be29 100644 --- a/crates/apub/assets/lemmy-comment.json +++ b/crates/apub/assets/lemmy-comment.json @@ -1,23 +1,4 @@ { - "@context": [ - "https://www.w3.org/ns/activitystreams", - { - "comments_enabled": { - "id": "pt:commentsEnabled", - "type": "sc:Boolean" - }, - "matrixUserId": { - "id": "as:alsoKnownAs", - "type": "sc:Text" - }, - "moderators": "as:moderators", - "pt": "https://join-lemmy.org#", - "sc": "http://schema.org#", - "sensitive": "as:sensitive", - "stickied": "as:stickied" - }, - "https://w3id.org/security/v1" - ], "id": "https://enterprise.lemmy.ml/comment/38741", "type": "Note", "attributedTo": "https://enterprise.lemmy.ml/u/picard", diff --git a/crates/apub/assets/lemmy-community.json b/crates/apub/assets/lemmy-community.json index 3f56ed8e..7eddd86b 100644 --- a/crates/apub/assets/lemmy-community.json +++ b/crates/apub/assets/lemmy-community.json @@ -1,23 +1,4 @@ { - "@context": [ - "https://www.w3.org/ns/activitystreams", - { - "comments_enabled": { - "id": "pt:commentsEnabled", - "type": "sc:Boolean" - }, - "matrixUserId": { - "id": "as:alsoKnownAs", - "type": "sc:Text" - }, - "moderators": "as:moderators", - "pt": "https://join-lemmy.org#", - "sc": "http://schema.org#", - "sensitive": "as:sensitive", - "stickied": "as:stickied" - }, - "https://w3id.org/security/v1" - ], "id": "https://enterprise.lemmy.ml/c/tenforward", "type": "Group", "preferredUsername": "main", diff --git a/crates/apub/assets/lemmy-person.json b/crates/apub/assets/lemmy-person.json index 25c93517..769f2f3e 100644 --- a/crates/apub/assets/lemmy-person.json +++ b/crates/apub/assets/lemmy-person.json @@ -1,23 +1,4 @@ { - "@context": [ - "https://www.w3.org/ns/activitystreams", - { - "comments_enabled": { - "id": "pt:commentsEnabled", - "type": "sc:Boolean" - }, - "matrixUserId": { - "id": "as:alsoKnownAs", - "type": "sc:Text" - }, - "moderators": "as:moderators", - "pt": "https://join-lemmy.org#", - "sc": "http://schema.org#", - "sensitive": "as:sensitive", - "stickied": "as:stickied" - }, - "https://w3id.org/security/v1" - ], "id": "https://enterprise.lemmy.ml/u/picard", "type": "Person", "preferredUsername": "picard", diff --git a/crates/apub/assets/lemmy-post.json b/crates/apub/assets/lemmy-post.json index baf28ea0..2c39acce 100644 --- a/crates/apub/assets/lemmy-post.json +++ b/crates/apub/assets/lemmy-post.json @@ -1,23 +1,4 @@ { - "@context": [ - "https://www.w3.org/ns/activitystreams", - { - "comments_enabled": { - "id": "pt:commentsEnabled", - "type": "sc:Boolean" - }, - "matrixUserId": { - "id": "as:alsoKnownAs", - "type": "sc:Text" - }, - "moderators": "as:moderators", - "pt": "https://join-lemmy.org#", - "sc": "http://schema.org#", - "sensitive": "as:sensitive", - "stickied": "as:stickied" - }, - "https://w3id.org/security/v1" - ], "id": "https://enterprise.lemmy.ml/post/55143", "type": "Page", "attributedTo": "https://enterprise.lemmy.ml/u/picard", diff --git a/crates/apub/assets/lemmy-private-message.json b/crates/apub/assets/lemmy-private-message.json index 37475421..45790231 100644 --- a/crates/apub/assets/lemmy-private-message.json +++ b/crates/apub/assets/lemmy-private-message.json @@ -1,23 +1,4 @@ { - "@context": [ - "https://www.w3.org/ns/activitystreams", - { - "comments_enabled": { - "id": "pt:commentsEnabled", - "type": "sc:Boolean" - }, - "matrixUserId": { - "id": "as:alsoKnownAs", - "type": "sc:Text" - }, - "moderators": "as:moderators", - "pt": "https://join-lemmy.org#", - "sc": "http://schema.org#", - "sensitive": "as:sensitive", - "stickied": "as:stickied" - }, - "https://w3id.org/security/v1" - ], "id": "https://enterprise.lemmy.ml/private_message/1621", "type": "ChatMessage", "attributedTo": "https://enterprise.lemmy.ml/u/picard", diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index f8469be9..18661cbf 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -12,7 +12,6 @@ use crate::{ verify_person_in_community, CreateOrUpdateType, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{ comment::{ApubComment, Note}, @@ -20,13 +19,7 @@ use crate::{ person::ApubPerson, }, }; -use activitystreams::{ - base::AnyBase, - link::Mention, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{link::Mention, public, unparsed::Unparsed}; use lemmy_api_common::{blocking, check_post_deleted_or_removed}; use lemmy_apub_lib::{ data::Data, @@ -54,8 +47,6 @@ pub struct CreateOrUpdateComment { #[serde(rename = "type")] kind: CreateOrUpdateType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -91,7 +82,6 @@ impl CreateOrUpdateComment { tag: maa.tags, kind, id: id.clone(), - context: lemmy_context(), unparsed: Default::default(), }; diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index 11abb5ba..b65fa48e 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -12,18 +12,11 @@ use crate::{ verify_mod_action, verify_person_in_community, }, - context::lemmy_context, fetcher::object_id::ObjectId, generate_moderators_url, objects::{community::ApubCommunity, person::ApubPerson}, }; -use activitystreams::{ - activity::kind::AddType, - base::AnyBase, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{activity::kind::AddType, public, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -49,8 +42,6 @@ pub struct AddMod { #[serde(rename = "type")] kind: AddType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -74,7 +65,6 @@ impl AddMod { cc: vec![community.actor_id()], kind: AddType::Add, id: id.clone(), - context: lemmy_context(), unparsed: Default::default(), }; diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 6165eb2f..007fcbb3 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -12,24 +12,17 @@ use crate::{ deletion::{delete::Delete, undo_delete::UndoDelete}, generate_activity_id, post::create_or_update::CreateOrUpdatePost, + send_lemmy_activity, verify_activity, verify_is_public, voting::{undo_vote::UndoVote, vote::Vote}, }, - context::lemmy_context, fetcher::object_id::ObjectId, http::is_activity_already_known, insert_activity, objects::community::ApubCommunity, - send_lemmy_activity, -}; -use activitystreams::{ - activity::kind::AnnounceType, - base::AnyBase, - primitives::OneOrMany, - public, - unparsed::Unparsed, }; +use activitystreams::{activity::kind::AnnounceType, public, unparsed::Unparsed}; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, @@ -102,8 +95,6 @@ pub struct AnnounceActivity { #[serde(rename = "type")] kind: AnnounceType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -125,7 +116,6 @@ impl AnnounceActivity { &AnnounceType::Announce, &context.settings().get_protocol_and_hostname(), )?, - context: lemmy_context(), unparsed: Default::default(), }; let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?; diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs index 21493214..0a716228 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/community/block_user.rs @@ -10,17 +10,10 @@ use crate::{ verify_mod_action, verify_person_in_community, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, }; -use activitystreams::{ - activity::kind::BlockType, - base::AnyBase, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{activity::kind::BlockType, public, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -51,8 +44,6 @@ pub struct BlockUserFromCommunity { #[serde(rename = "type")] kind: BlockType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -75,7 +66,6 @@ impl BlockUserFromCommunity { BlockType::Block, &context.settings().get_protocol_and_hostname(), )?, - context: lemmy_context(), unparsed: Default::default(), }) } diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 9db9d814..1f51c033 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -1,10 +1,12 @@ use crate::{ - activities::community::announce::{AnnouncableActivities, AnnounceActivity}, + activities::{ + community::announce::{AnnouncableActivities, AnnounceActivity}, + send_lemmy_activity, + }, check_is_apub_id_valid, fetcher::object_id::ObjectId, insert_activity, objects::community::ApubCommunity, - send_lemmy_activity, }; use itertools::Itertools; use lemmy_apub_lib::traits::ActorType; diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index 786740d5..f0c0b90e 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -12,18 +12,11 @@ use crate::{ verify_mod_action, verify_person_in_community, }, - context::lemmy_context, fetcher::object_id::ObjectId, generate_moderators_url, objects::{community::ApubCommunity, person::ApubPerson}, }; -use activitystreams::{ - activity::kind::RemoveType, - base::AnyBase, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{activity::kind::RemoveType, public, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -49,8 +42,6 @@ pub struct RemoveMod { kind: RemoveType, pub(in crate::activities) target: Url, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -72,7 +63,6 @@ impl RemoveMod { object: ObjectId::new(removed_mod.actor_id()), target: generate_moderators_url(&community.actor_id)?.into(), id: id.clone(), - context: lemmy_context(), cc: vec![community.actor_id()], kind: RemoveType::Remove, unparsed: Default::default(), diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs index b5e2a69c..ad220c96 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/community/undo_block_user.rs @@ -11,17 +11,10 @@ use crate::{ verify_mod_action, verify_person_in_community, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, }; -use activitystreams::{ - activity::kind::UndoType, - base::AnyBase, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{activity::kind::UndoType, public, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -46,8 +39,6 @@ pub struct UndoBlockUserFromCommunity { #[serde(rename = "type")] kind: UndoType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -72,7 +63,6 @@ impl UndoBlockUserFromCommunity { cc: vec![community.actor_id()], kind: UndoType::Undo, id: id.clone(), - context: lemmy_context(), unparsed: Default::default(), }; diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index b5ad7f92..78fce324 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -10,20 +10,13 @@ use crate::{ verify_mod_action, verify_person_in_community, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{ community::{ApubCommunity, Group}, person::ApubPerson, }, }; -use activitystreams::{ - activity::kind::UpdateType, - base::AnyBase, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{activity::kind::UpdateType, public, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -51,8 +44,6 @@ pub struct UpdateCommunity { #[serde(rename = "type")] kind: UpdateType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -74,7 +65,6 @@ impl UpdateCommunity { cc: vec![community.actor_id()], kind: UpdateType::Update, id: id.clone(), - context: lemmy_context(), unparsed: Default::default(), }; diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 2677ea90..0db590b9 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -14,17 +14,10 @@ use crate::{ verify_activity, verify_is_public, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, }; -use activitystreams::{ - activity::kind::DeleteType, - base::AnyBase, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{activity::kind::DeleteType, public, unparsed::Unparsed}; use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -79,8 +72,6 @@ pub struct Delete { /// deleting their own content. pub(in crate::activities::deletion) summary: Option, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -159,7 +150,6 @@ impl Delete { DeleteType::Delete, &context.settings().get_protocol_and_hostname(), )?, - context: lemmy_context(), unparsed: Default::default(), }) } diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index d881ab42..5cc4dffc 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -15,17 +15,10 @@ use crate::{ verify_activity, verify_is_public, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, }; -use activitystreams::{ - activity::kind::UndoType, - base::AnyBase, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{activity::kind::UndoType, public, unparsed::Unparsed}; use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -52,8 +45,6 @@ pub struct UndoDelete { #[serde(rename = "type")] kind: UndoType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -128,7 +119,6 @@ impl UndoDelete { cc: vec![community.actor_id()], kind: UndoType::Undo, id: id.clone(), - context: lemmy_context(), unparsed: Default::default(), }; diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index 92f53184..28f6c108 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -1,16 +1,14 @@ use crate::{ - activities::{following::follow::FollowCommunity, generate_activity_id, verify_activity}, - context::lemmy_context, + activities::{ + following::follow::FollowCommunity, + generate_activity_id, + send_lemmy_activity, + verify_activity, + }, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, - send_lemmy_activity, -}; -use activitystreams::{ - activity::kind::AcceptType, - base::AnyBase, - primitives::OneOrMany, - unparsed::Unparsed, }; +use activitystreams::{activity::kind::AcceptType, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -32,8 +30,6 @@ pub struct AcceptFollowCommunity { #[serde(rename = "type")] kind: AcceptType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -59,7 +55,6 @@ impl AcceptFollowCommunity { AcceptType::Accept, &context.settings().get_protocol_and_hostname(), )?, - context: lemmy_context(), unparsed: Default::default(), }; let inbox = vec![person.inbox_url()]; diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 4710f70f..57a112e0 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -2,21 +2,15 @@ use crate::{ activities::{ following::accept::AcceptFollowCommunity, generate_activity_id, + send_lemmy_activity, verify_activity, verify_person, verify_person_in_community, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, - send_lemmy_activity, -}; -use activitystreams::{ - activity::kind::FollowType, - base::AnyBase, - primitives::OneOrMany, - unparsed::Unparsed, }; +use activitystreams::{activity::kind::FollowType, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -41,8 +35,6 @@ pub struct FollowCommunity { #[serde(rename = "type")] kind: FollowType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -62,7 +54,6 @@ impl FollowCommunity { FollowType::Follow, &context.settings().get_protocol_and_hostname(), )?, - context: lemmy_context(), unparsed: Default::default(), }) } diff --git a/crates/apub/src/activities/following/undo.rs b/crates/apub/src/activities/following/undo.rs index 7abb6117..a9326a26 100644 --- a/crates/apub/src/activities/following/undo.rs +++ b/crates/apub/src/activities/following/undo.rs @@ -2,20 +2,14 @@ use crate::{ activities::{ following::follow::FollowCommunity, generate_activity_id, + send_lemmy_activity, verify_activity, verify_person, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, - send_lemmy_activity, -}; -use activitystreams::{ - activity::kind::UndoType, - base::AnyBase, - primitives::OneOrMany, - unparsed::Unparsed, }; +use activitystreams::{activity::kind::UndoType, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -40,8 +34,6 @@ pub struct UndoFollowCommunity { #[serde(rename = "type")] kind: UndoType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -62,7 +54,6 @@ impl UndoFollowCommunity { UndoType::Undo, &context.settings().get_protocol_and_hostname(), )?, - context: lemmy_context(), unparsed: Default::default(), }; let inbox = vec![community.shared_inbox_or_inbox_url()]; diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 63f79caa..9509babb 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -1,13 +1,19 @@ use crate::{ check_is_apub_id_valid, + context::WithContext, fetcher::object_id::ObjectId, generate_moderators_url, + insert_activity, objects::{community::ApubCommunity, person::ApubPerson}, }; use activitystreams::public; use anyhow::anyhow; use lemmy_api_common::blocking; -use lemmy_apub_lib::{traits::ActivityFields, verify::verify_domains_match}; +use lemmy_apub_lib::{ + activity_queue::send_activity, + traits::{ActivityFields, ActorType}, + verify::verify_domains_match, +}; use lemmy_db_schema::source::community::Community; use lemmy_db_views_actor::{ community_person_ban_view::CommunityPersonBanView, @@ -15,6 +21,7 @@ use lemmy_db_views_actor::{ }; use lemmy_utils::{settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; +use log::info; use serde::{Deserialize, Serialize}; use strum_macros::ToString; use url::{ParseError, Url}; @@ -146,3 +153,47 @@ where ); Url::parse(&id) } + +async fn send_lemmy_activity( + context: &LemmyContext, + activity: &T, + activity_id: &Url, + actor: &dyn ActorType, + inboxes: Vec, + sensitive: bool, +) -> Result<(), LemmyError> { + if !context.settings().federation.enabled || inboxes.is_empty() { + return Ok(()); + } + let activity = WithContext::new(activity); + + info!("Sending activity {}", activity_id.to_string()); + + // Don't send anything to ourselves + // TODO: this should be a debug assert + let hostname = context.settings().get_hostname_without_port()?; + let inboxes: Vec<&Url> = inboxes + .iter() + .filter(|i| i.domain().expect("valid inbox url") != hostname) + .collect(); + + let serialised_activity = serde_json::to_string(&activity)?; + + insert_activity( + activity_id, + serialised_activity.clone(), + true, + sensitive, + context.pool(), + ) + .await?; + + send_activity( + serialised_activity, + actor, + inboxes, + context.client(), + context.activity_queue(), + ) + .await +} diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index 44498823..b5d9a202 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -12,7 +12,6 @@ use crate::{ verify_person_in_community, CreateOrUpdateType, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{ community::ApubCommunity, @@ -20,7 +19,7 @@ use crate::{ post::{ApubPost, Page}, }, }; -use activitystreams::{base::AnyBase, primitives::OneOrMany, public, unparsed::Unparsed}; +use activitystreams::{public, unparsed::Unparsed}; use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -44,8 +43,6 @@ pub struct CreateOrUpdatePost { #[serde(rename = "type")] kind: CreateOrUpdateType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -69,7 +66,6 @@ impl CreateOrUpdatePost { cc: vec![community.actor_id()], kind, id: id.clone(), - context: lemmy_context(), unparsed: Default::default(), }) } diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs index 22f8edb8..72a38e5e 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -1,14 +1,18 @@ use crate::{ - activities::{generate_activity_id, verify_activity, verify_person, CreateOrUpdateType}, - context::lemmy_context, + activities::{ + generate_activity_id, + send_lemmy_activity, + verify_activity, + verify_person, + CreateOrUpdateType, + }, fetcher::object_id::ObjectId, objects::{ person::ApubPerson, private_message::{ApubPrivateMessage, ChatMessage}, }, - send_lemmy_activity, }; -use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; +use activitystreams::unparsed::Unparsed; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -24,8 +28,6 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct CreateOrUpdatePrivateMessage { - #[serde(rename = "@context")] - pub context: OneOrMany, id: Url, actor: ObjectId, to: [ObjectId; 1], @@ -54,7 +56,6 @@ impl CreateOrUpdatePrivateMessage { &context.settings().get_protocol_and_hostname(), )?; let create_or_update = CreateOrUpdatePrivateMessage { - context: lemmy_context(), id: id.clone(), actor: ObjectId::new(actor.actor_id()), to: [ObjectId::new(recipient.actor_id())], diff --git a/crates/apub/src/activities/private_message/delete.rs b/crates/apub/src/activities/private_message/delete.rs index bcd9542c..0928cba2 100644 --- a/crates/apub/src/activities/private_message/delete.rs +++ b/crates/apub/src/activities/private_message/delete.rs @@ -1,16 +1,9 @@ use crate::{ - activities::{generate_activity_id, verify_activity, verify_person}, - context::lemmy_context, + activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person}, fetcher::object_id::ObjectId, objects::{person::ApubPerson, private_message::ApubPrivateMessage}, - send_lemmy_activity, -}; -use activitystreams::{ - activity::kind::DeleteType, - base::AnyBase, - primitives::OneOrMany, - unparsed::Unparsed, }; +use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -35,8 +28,6 @@ pub struct DeletePrivateMessage { #[serde(rename = "type")] kind: DeleteType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -56,7 +47,6 @@ impl DeletePrivateMessage { DeleteType::Delete, &context.settings().get_protocol_and_hostname(), )?, - context: lemmy_context(), unparsed: Default::default(), }) } diff --git a/crates/apub/src/activities/private_message/undo_delete.rs b/crates/apub/src/activities/private_message/undo_delete.rs index cdabaa9a..4275ea9c 100644 --- a/crates/apub/src/activities/private_message/undo_delete.rs +++ b/crates/apub/src/activities/private_message/undo_delete.rs @@ -2,20 +2,14 @@ use crate::{ activities::{ generate_activity_id, private_message::delete::DeletePrivateMessage, + send_lemmy_activity, verify_activity, verify_person, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{person::ApubPerson, private_message::ApubPrivateMessage}, - send_lemmy_activity, -}; -use activitystreams::{ - activity::kind::UndoType, - base::AnyBase, - primitives::OneOrMany, - unparsed::Unparsed, }; +use activitystreams::{activity::kind::UndoType, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -40,8 +34,6 @@ pub struct UndoDeletePrivateMessage { #[serde(rename = "type")] kind: UndoType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -69,7 +61,6 @@ impl UndoDeletePrivateMessage { object, kind: UndoType::Undo, id: id.clone(), - context: lemmy_context(), unparsed: Default::default(), }; let inbox = vec![recipient.shared_inbox_or_inbox_url()]; diff --git a/crates/apub/src/activities/report.rs b/crates/apub/src/activities/report.rs index 24041b0f..9256920a 100644 --- a/crates/apub/src/activities/report.rs +++ b/crates/apub/src/activities/report.rs @@ -1,17 +1,15 @@ use crate::{ - activities::{generate_activity_id, verify_activity, verify_person_in_community}, - context::lemmy_context, + activities::{ + generate_activity_id, + send_lemmy_activity, + verify_activity, + verify_person_in_community, + }, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, - send_lemmy_activity, PostOrComment, }; -use activitystreams::{ - activity::kind::FlagType, - base::AnyBase, - primitives::OneOrMany, - unparsed::Unparsed, -}; +use activitystreams::{activity::kind::FlagType, unparsed::Unparsed}; use lemmy_api_common::{blocking, comment::CommentReportResponse, post::PostReportResponse}; use lemmy_apub_lib::{ data::Data, @@ -40,8 +38,6 @@ pub struct Report { #[serde(rename = "type")] kind: FlagType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -67,7 +63,6 @@ impl Report { summary: reason, kind, id: id.clone(), - context: lemmy_context(), unparsed: Default::default(), }; send_lemmy_activity( diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index 144cbfc1..cc620661 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -14,18 +14,11 @@ use crate::{ vote::{Vote, VoteType}, }, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, PostOrComment, }; -use activitystreams::{ - activity::kind::UndoType, - base::AnyBase, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{activity::kind::UndoType, public, unparsed::Unparsed}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -49,8 +42,6 @@ pub struct UndoVote { #[serde(rename = "type")] kind: UndoType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -81,7 +72,6 @@ impl UndoVote { cc: vec![community.actor_id()], kind: UndoType::Undo, id: id.clone(), - context: lemmy_context(), unparsed: Default::default(), }; let activity = AnnouncableActivities::UndoVote(undo_vote); diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 5b08390f..3efef7cf 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -10,12 +10,11 @@ use crate::{ verify_person_in_community, voting::{vote_comment, vote_post}, }, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, PostOrComment, }; -use activitystreams::{base::AnyBase, primitives::OneOrMany, public, unparsed::Unparsed}; +use activitystreams::{public, unparsed::Unparsed}; use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -71,8 +70,6 @@ pub struct Vote { #[serde(rename = "type")] pub(in crate::activities::voting) kind: VoteType, id: Url, - #[serde(rename = "@context")] - context: OneOrMany, #[serde(flatten)] unparsed: Unparsed, } @@ -92,7 +89,6 @@ impl Vote { cc: vec![community.actor_id()], kind: kind.clone(), id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?, - context: lemmy_context(), unparsed: Default::default(), }) } diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index dc1d5798..2d190f4e 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -1,17 +1,10 @@ use crate::{ collections::CommunityContext, - context::lemmy_context, fetcher::object_id::ObjectId, generate_moderators_url, objects::person::ApubPerson, }; -use activitystreams::{ - base::AnyBase, - chrono::NaiveDateTime, - collection::kind::OrderedCollectionType, - primitives::OneOrMany, - url::Url, -}; +use activitystreams::{chrono::NaiveDateTime, collection::kind::OrderedCollectionType}; use lemmy_api_common::blocking; use lemmy_apub_lib::{traits::ApubObject, verify::verify_domains_match}; use lemmy_db_schema::{ @@ -22,13 +15,12 @@ use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView; use lemmy_utils::LemmyError; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +use url::Url; #[skip_serializing_none] #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct GroupModerators { - #[serde(rename = "@context")] - context: OneOrMany, r#type: OrderedCollectionType, id: Url, ordered_items: Vec>, @@ -75,7 +67,6 @@ impl ApubObject for ApubCommunityModerators { .map(|m| ObjectId::::new(m.moderator.actor_id.clone().into_inner())) .collect(); Ok(GroupModerators { - context: lemmy_context(), r#type: OrderedCollectionType::OrderedCollection, id: generate_moderators_url(&data.0.actor_id)?.into(), ordered_items, diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index 24465c95..9d870f0e 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -1,17 +1,11 @@ use crate::{ activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType}, collections::CommunityContext, - context::lemmy_context, generate_outbox_url, objects::{person::ApubPerson, post::ApubPost}, }; -use activitystreams::{ - base::AnyBase, - chrono::NaiveDateTime, - collection::kind::OrderedCollectionType, - primitives::OneOrMany, - url::Url, -}; +use activitystreams::collection::kind::OrderedCollectionType; +use chrono::NaiveDateTime; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -25,13 +19,12 @@ use lemmy_db_schema::{ use lemmy_utils::LemmyError; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +use url::Url; #[skip_serializing_none] #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct GroupOutbox { - #[serde(rename = "@context")] - context: OneOrMany, r#type: OrderedCollectionType, id: Url, ordered_items: Vec, @@ -88,7 +81,6 @@ impl ApubObject for ApubCommunityOutbox { } Ok(GroupOutbox { - context: lemmy_context(), r#type: OrderedCollectionType::OrderedCollection, id: generate_outbox_url(&data.0.actor_id)?.into(), ordered_items, diff --git a/crates/apub/src/context.rs b/crates/apub/src/context.rs index bc58052f..cf4b704f 100644 --- a/crates/apub/src/context.rs +++ b/crates/apub/src/context.rs @@ -1,28 +1,51 @@ use activitystreams::{base::AnyBase, context, primitives::OneOrMany}; +use serde::{Deserialize, Serialize}; use serde_json::json; use url::Url; -pub(crate) fn lemmy_context() -> OneOrMany { - let context_ext = AnyBase::from_arbitrary_json(json!( - { - "sc": "http://schema.org#", - "sensitive": "as:sensitive", - "stickied": "as:stickied", - "pt": "https://join-lemmy.org#", - "comments_enabled": { - "type": "sc:Boolean", - "id": "pt:commentsEnabled" - }, - "moderators": "as:moderators", - "matrixUserId": { - "type": "sc:Text", - "id": "as:alsoKnownAs" - }, - })) - .expect("parse context"); - OneOrMany::from(vec![ - AnyBase::from(context()), - context_ext, - AnyBase::from(Url::parse("https://w3id.org/security/v1").expect("parse context")), - ]) +lazy_static! { + static ref CONTEXT: OneOrMany = { + let context_ext = AnyBase::from_arbitrary_json(json!( + { + "sc": "http://schema.org#", + "sensitive": "as:sensitive", + "stickied": "as:stickied", + "pt": "https://join-lemmy.org#", + "comments_enabled": { + "type": "sc:Boolean", + "id": "pt:commentsEnabled" + }, + "moderators": "as:moderators", + "matrixUserId": { + "type": "sc:Text", + "id": "as:alsoKnownAs" + }, + })) + .expect("parse context"); + OneOrMany::from(vec![ + AnyBase::from(context()), + context_ext, + AnyBase::from(Url::parse("https://w3id.org/security/v1").expect("parse context")), + ]) + }; +} + +#[derive(Serialize, Deserialize)] +pub(crate) struct WithContext { + #[serde(rename = "@context")] + context: OneOrMany, + #[serde(flatten)] + inner: T, +} + +impl WithContext { + pub(crate) fn new(inner: T) -> WithContext { + WithContext { + context: CONTEXT.clone(), + inner, + } + } + pub(crate) fn inner(self) -> T { + self.inner + } } diff --git a/crates/apub/src/fetcher/object_id.rs b/crates/apub/src/fetcher/object_id.rs index 66466362..5c5c518f 100644 --- a/crates/apub/src/fetcher/object_id.rs +++ b/crates/apub/src/fetcher/object_id.rs @@ -22,7 +22,6 @@ use url::Url; /// fetch through the search). This should be configurable. static REQUEST_LIMIT: i32 = 25; -// TODO: after moving this file to library, remove lazy_static dependency from apub crate lazy_static! { static ref CLIENT: Client = Client::builder() .user_agent(build_user_agent(&Settings::get())) diff --git a/crates/apub/src/fetcher/post_or_comment.rs b/crates/apub/src/fetcher/post_or_comment.rs index ff0562a6..ac391120 100644 --- a/crates/apub/src/fetcher/post_or_comment.rs +++ b/crates/apub/src/fetcher/post_or_comment.rs @@ -2,7 +2,7 @@ use crate::objects::{ comment::{ApubComment, Note}, post::{ApubPost, Page}, }; -use activitystreams::chrono::NaiveDateTime; +use chrono::NaiveDateTime; use lemmy_apub_lib::traits::ApubObject; use lemmy_db_schema::source::{comment::CommentForm, post::PostForm}; use lemmy_utils::LemmyError; diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index 86cdcbe0..5c2a3e7b 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -7,8 +7,8 @@ use crate::{ post::{ApubPost, Page}, }, }; -use activitystreams::chrono::NaiveDateTime; use anyhow::anyhow; +use chrono::NaiveDateTime; use itertools::Itertools; use lemmy_api_common::blocking; use lemmy_apub_lib::{ diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index f39f7b6f..6c9c632e 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -10,7 +10,7 @@ use crate::{ community_outbox::ApubCommunityOutbox, CommunityContext, }, - context::lemmy_context, + context::WithContext, fetcher::object_id::ObjectId, generate_outbox_url, http::{ @@ -82,9 +82,9 @@ pub async fn community_inbox( ) -> Result { let unparsed = payload_to_string(payload).await?; info!("Received community inbox activity {}", unparsed); - let activity = serde_json::from_str::(&unparsed)?; + let activity = serde_json::from_str::>(&unparsed)?; - receive_group_inbox(activity.clone(), request, &context).await?; + receive_group_inbox(activity.inner(), request, &context).await?; Ok(HttpResponse::Ok().finish()) } @@ -127,9 +127,9 @@ pub(crate) async fn get_apub_community_followers( let mut collection = UnorderedCollection::new(); collection - .set_many_contexts(lemmy_context()) .set_id(community.followers_url.into()) .set_total_items(community_followers.len() as u64); + let collection = WithContext::new(collection); Ok(create_apub_response(&collection)) } diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index fe44d5e3..48956cdf 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -1,5 +1,6 @@ use crate::{ check_is_apub_id_valid, + context::WithContext, fetcher::get_or_fetch_and_upsert_actor, http::{ community::{receive_group_inbox, GroupInboxActivities}, @@ -55,8 +56,8 @@ pub async fn shared_inbox( ) -> Result { let unparsed = payload_to_string(payload).await?; info!("Received shared inbox activity {}", unparsed); - let activity = serde_json::from_str::(&unparsed)?; - match activity { + let activity = serde_json::from_str::>(&unparsed)?; + match activity.inner() { SharedInboxActivities::GroupInboxActivities(g) => { receive_group_inbox(g, request, &context).await } @@ -134,7 +135,7 @@ where { HttpResponse::Ok() .content_type(APUB_JSON_CONTENT_TYPE) - .json(data) + .json(WithContext::new(data)) } fn create_apub_tombstone_response(data: &T) -> HttpResponse @@ -144,7 +145,7 @@ where HttpResponse::Gone() .content_type(APUB_JSON_CONTENT_TYPE) .status(StatusCode::GONE) - .json(data) + .json(WithContext::new(data)) } #[derive(Deserialize)] diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index 88ffa120..2cc18768 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -8,7 +8,7 @@ use crate::{ undo_delete::UndoDeletePrivateMessage, }, }, - context::lemmy_context, + context::WithContext, generate_outbox_url, http::{ create_apub_response, @@ -80,8 +80,8 @@ pub async fn person_inbox( ) -> Result { let unparsed = payload_to_string(payload).await?; info!("Received person inbox activity {}", unparsed); - let activity = serde_json::from_str::(&unparsed)?; - receive_person_inbox(activity, request, &context).await + let activity = serde_json::from_str::>(&unparsed)?; + receive_person_inbox(activity.inner(), request, &context).await } pub(in crate::http) async fn receive_person_inbox( @@ -104,8 +104,8 @@ pub(crate) async fn get_apub_person_outbox( let mut collection = OrderedCollection::new(); collection .set_many_items(Vec::::new()) - .set_many_contexts(lemmy_context()) .set_id(generate_outbox_url(&person.actor_id)?.into()) .set_total_items(0_u64); + let collection = WithContext::new(collection); Ok(create_apub_response(&collection)) } diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index 3bf3ff25..f60e04af 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -12,15 +12,10 @@ extern crate lazy_static; use crate::fetcher::post_or_comment::PostOrComment; use anyhow::{anyhow, Context}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - activity_queue::send_activity, - traits::ActorType, - webfinger::{webfinger_resolve_actor, WebfingerType}, -}; +use lemmy_apub_lib::webfinger::{webfinger_resolve_actor, WebfingerType}; use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, DbPool}; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; -use log::info; use serde::Serialize; use std::net::IpAddr; use url::{ParseError, Url}; @@ -195,46 +190,3 @@ where .await??; Ok(()) } - -pub(crate) async fn send_lemmy_activity( - context: &LemmyContext, - activity: &T, - activity_id: &Url, - actor: &dyn ActorType, - inboxes: Vec, - sensitive: bool, -) -> Result<(), LemmyError> { - if !context.settings().federation.enabled || inboxes.is_empty() { - return Ok(()); - } - - info!("Sending activity {}", activity_id.to_string()); - - // Don't send anything to ourselves - // TODO: this should be a debug assert - let hostname = context.settings().get_hostname_without_port()?; - let inboxes: Vec<&Url> = inboxes - .iter() - .filter(|i| i.domain().expect("valid inbox url") != hostname) - .collect(); - - let serialised_activity = serde_json::to_string(&activity)?; - - insert_activity( - activity_id, - serialised_activity.clone(), - true, - sensitive, - context.pool(), - ) - .await?; - - send_activity( - serialised_activity, - actor, - inboxes, - context.client(), - context.activity_queue(), - ) - .await -} diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 335605a6..5698640d 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -1,6 +1,5 @@ use crate::{ activities::{verify_is_public, verify_person_in_community}, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{ community::ApubCommunity, @@ -11,16 +10,9 @@ use crate::{ }, PostOrComment, }; -use activitystreams::{ - base::AnyBase, - chrono::NaiveDateTime, - object::kind::NoteType, - primitives::OneOrMany, - public, - unparsed::Unparsed, -}; +use activitystreams::{object::kind::NoteType, public, unparsed::Unparsed}; use anyhow::anyhow; -use chrono::{DateTime, FixedOffset}; +use chrono::{DateTime, FixedOffset, NaiveDateTime}; use html2md::parse_html; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -52,11 +44,6 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Note { - /// Necessary to make this optional to make Pleroma Create/Note work. - /// TODO: change this so that context is not defined in the struct itself, but added in activity - /// queue and http handlers - #[serde(rename = "@context")] - context: Option>, r#type: NoteType, id: Url, pub(crate) attributed_to: ObjectId, @@ -206,7 +193,6 @@ impl ApubObject for ApubComment { }; let note = Note { - context: Some(lemmy_context()), r#type: NoteType::Note, id: self.ap_id.to_owned().into_inner(), attributed_to: ObjectId::new(creator.actor_id), diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index a38e759f..c7d1dd3e 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -5,7 +5,6 @@ use crate::{ community_outbox::ApubCommunityOutbox, CommunityContext, }, - context::lemmy_context, fetcher::object_id::ObjectId, generate_moderators_url, generate_outbox_url, @@ -13,13 +12,10 @@ use crate::{ }; use activitystreams::{ actor::{kind::GroupType, Endpoints}, - base::AnyBase, - chrono::NaiveDateTime, object::kind::ImageType, - primitives::OneOrMany, unparsed::Unparsed, }; -use chrono::{DateTime, FixedOffset}; +use chrono::{DateTime, FixedOffset, NaiveDateTime}; use itertools::Itertools; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -50,8 +46,6 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Group { - #[serde(rename = "@context")] - context: OneOrMany, #[serde(rename = "type")] kind: GroupType, pub(crate) id: Url, @@ -181,7 +175,6 @@ impl ApubObject for ApubCommunity { }); let group = Group { - context: lemmy_context(), kind: GroupType::Group, id: self.actor_id(), preferred_username: self.name.clone(), diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index f4ba225b..49ba7eb1 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -1,18 +1,10 @@ use crate::{ check_is_apub_id_valid, - context::lemmy_context, generate_outbox_url, objects::{get_summary_from_string_or_source, ImageObject, Source}, }; -use activitystreams::{ - actor::Endpoints, - base::AnyBase, - chrono::NaiveDateTime, - object::{kind::ImageType, Tombstone}, - primitives::OneOrMany, - unparsed::Unparsed, -}; -use chrono::{DateTime, FixedOffset}; +use activitystreams::{actor::Endpoints, object::kind::ImageType, unparsed::Unparsed}; +use chrono::{DateTime, FixedOffset, NaiveDateTime}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ signatures::PublicKey, @@ -44,8 +36,6 @@ pub enum UserTypes { #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Person { - #[serde(rename = "@context")] - context: OneOrMany, #[serde(rename = "type")] kind: UserTypes, id: Url, @@ -99,7 +89,7 @@ impl From for ApubPerson { impl ApubObject for ApubPerson { type DataType = LemmyContext; type ApubType = Person; - type TombstoneType = Tombstone; + type TombstoneType = (); fn last_refreshed_at(&self) -> Option { Some(self.last_refreshed_at) @@ -146,7 +136,6 @@ impl ApubObject for ApubPerson { }); let person = Person { - context: lemmy_context(), kind, id: self.actor_id.to_owned().into_inner(), preferred_username: self.name.clone(), @@ -170,7 +159,7 @@ impl ApubObject for ApubPerson { Ok(person) } - fn to_tombstone(&self) -> Result { + fn to_tombstone(&self) -> Result<(), LemmyError> { unimplemented!() } diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index f15bb5ba..ee9aa7b8 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -1,6 +1,5 @@ use crate::{ activities::{verify_is_public, verify_person_in_community}, - context::lemmy_context, fetcher::object_id::ObjectId, objects::{ community::ApubCommunity, @@ -11,9 +10,7 @@ use crate::{ }, }; use activitystreams::{ - base::AnyBase, object::kind::{ImageType, PageType}, - primitives::OneOrMany, public, unparsed::Unparsed, }; @@ -49,8 +46,6 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Page { - #[serde(rename = "@context")] - context: OneOrMany, r#type: PageType, id: Url, pub(crate) attributed_to: ObjectId, @@ -196,7 +191,6 @@ impl ApubObject for ApubPost { }); let page = Page { - context: lemmy_context(), r#type: PageType::Page, id: self.ap_id.clone().into(), attributed_to: ObjectId::new(creator.actor_id), diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 56200d1b..e971ebe3 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -1,17 +1,10 @@ use crate::{ - context::lemmy_context, fetcher::object_id::ObjectId, objects::{person::ApubPerson, Source}, }; -use activitystreams::{ - base::AnyBase, - chrono::NaiveDateTime, - object::Tombstone, - primitives::OneOrMany, - unparsed::Unparsed, -}; +use activitystreams::unparsed::Unparsed; use anyhow::anyhow; -use chrono::{DateTime, FixedOffset}; +use chrono::{DateTime, FixedOffset, NaiveDateTime}; use html2md::parse_html; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -37,8 +30,6 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ChatMessage { - #[serde(rename = "@context")] - context: OneOrMany, r#type: ChatMessageType, id: Url, pub(crate) attributed_to: ObjectId, @@ -104,7 +95,7 @@ impl From for ApubPrivateMessage { impl ApubObject for ApubPrivateMessage { type DataType = LemmyContext; type ApubType = ChatMessage; - type TombstoneType = Tombstone; + type TombstoneType = (); fn last_refreshed_at(&self) -> Option { None @@ -137,7 +128,6 @@ impl ApubObject for ApubPrivateMessage { blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; let note = ChatMessage { - context: lemmy_context(), r#type: ChatMessageType::ChatMessage, id: self.ap_id.clone().into(), attributed_to: ObjectId::new(creator.actor_id), @@ -155,7 +145,7 @@ impl ApubObject for ApubPrivateMessage { Ok(note) } - fn to_tombstone(&self) -> Result { + fn to_tombstone(&self) -> Result<(), LemmyError> { unimplemented!() } diff --git a/crates/apub/src/objects/tombstone.rs b/crates/apub/src/objects/tombstone.rs index fc78c968..2746c5ae 100644 --- a/crates/apub/src/objects/tombstone.rs +++ b/crates/apub/src/objects/tombstone.rs @@ -1,10 +1,5 @@ -use crate::context::lemmy_context; -use activitystreams::{ - base::AnyBase, - chrono::{DateTime, FixedOffset, NaiveDateTime}, - object::kind::TombstoneType, - primitives::OneOrMany, -}; +use activitystreams::object::kind::TombstoneType; +use chrono::{DateTime, FixedOffset, NaiveDateTime}; use lemmy_utils::utils::convert_datetime; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -13,8 +8,6 @@ use serde_with::skip_serializing_none; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Tombstone { - #[serde(rename = "@context")] - context: OneOrMany, #[serde(rename = "type")] kind: TombstoneType, former_type: String, @@ -24,7 +17,6 @@ pub struct Tombstone { impl Tombstone { pub fn new(former_type: T, updated_time: NaiveDateTime) -> Tombstone { Tombstone { - context: lemmy_context(), kind: TombstoneType::Tombstone, former_type: former_type.to_string(), deleted: convert_datetime(updated_time), From 358ef99ea27df48f0d0422bc4184f455b4aefc3a Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 28 Oct 2021 17:52:11 +0200 Subject: [PATCH 10/17] Rewrite community followers and user outbox to use our own structs --- .../src/collections/community_followers.rs | 38 +++++++++++++++++++ .../src/collections/community_moderators.rs | 2 - .../apub/src/collections/community_outbox.rs | 4 +- crates/apub/src/collections/mod.rs | 2 + crates/apub/src/collections/user_outbox.rs | 26 +++++++++++++ crates/apub/src/http/community.rs | 21 ++-------- crates/apub/src/http/person.rs | 17 ++------- 7 files changed, 74 insertions(+), 36 deletions(-) create mode 100644 crates/apub/src/collections/community_followers.rs create mode 100644 crates/apub/src/collections/user_outbox.rs diff --git a/crates/apub/src/collections/community_followers.rs b/crates/apub/src/collections/community_followers.rs new file mode 100644 index 00000000..41155b19 --- /dev/null +++ b/crates/apub/src/collections/community_followers.rs @@ -0,0 +1,38 @@ +use crate::generate_followers_url; +use activitystreams::collection::kind::CollectionType; +use lemmy_api_common::blocking; +use lemmy_db_schema::source::community::Community; +use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct CommunityFollowers { + id: Url, + r#type: CollectionType, + total_items: i32, + items: Vec<()>, +} + +impl CommunityFollowers { + pub(crate) async fn new( + community: Community, + context: &LemmyContext, + ) -> Result { + let community_id = community.id; + let community_followers = blocking(context.pool(), move |conn| { + CommunityFollowerView::for_community(conn, community_id) + }) + .await??; + + Ok(CommunityFollowers { + id: generate_followers_url(&community.actor_id)?.into_inner(), + r#type: CollectionType::Collection, + total_items: community_followers.len() as i32, + items: vec![], + }) + } +} diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index 2d190f4e..d91b41c4 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -14,10 +14,8 @@ use lemmy_db_schema::{ use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView; use lemmy_utils::LemmyError; use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; use url::Url; -#[skip_serializing_none] #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct GroupModerators { diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index 9d870f0e..98fb6644 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -18,15 +18,14 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; use url::Url; -#[skip_serializing_none] #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct GroupOutbox { r#type: OrderedCollectionType, id: Url, + total_items: i32, ordered_items: Vec, } @@ -83,6 +82,7 @@ impl ApubObject for ApubCommunityOutbox { Ok(GroupOutbox { r#type: OrderedCollectionType::OrderedCollection, id: generate_outbox_url(&data.0.actor_id)?.into(), + total_items: ordered_items.len() as i32, ordered_items, }) } diff --git a/crates/apub/src/collections/mod.rs b/crates/apub/src/collections/mod.rs index e2a274a0..378e14c0 100644 --- a/crates/apub/src/collections/mod.rs +++ b/crates/apub/src/collections/mod.rs @@ -1,8 +1,10 @@ use crate::objects::community::ApubCommunity; use lemmy_websocket::LemmyContext; +pub(crate) mod community_followers; pub(crate) mod community_moderators; pub(crate) mod community_outbox; +pub(crate) mod user_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/collections/user_outbox.rs b/crates/apub/src/collections/user_outbox.rs new file mode 100644 index 00000000..59c6975e --- /dev/null +++ b/crates/apub/src/collections/user_outbox.rs @@ -0,0 +1,26 @@ +use crate::generate_outbox_url; +use activitystreams::collection::kind::OrderedCollectionType; +use lemmy_db_schema::source::person::Person; +use lemmy_utils::LemmyError; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct UserOutbox { + r#type: OrderedCollectionType, + id: Url, + ordered_items: Vec<()>, + total_items: i32, +} + +impl UserOutbox { + pub(crate) async fn new(user: Person) -> Result { + Ok(UserOutbox { + r#type: OrderedCollectionType::OrderedCollection, + id: generate_outbox_url(&user.actor_id)?.into_inner(), + ordered_items: vec![], + total_items: 0, + }) + } +} diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index 6c9c632e..73f59d03 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -6,6 +6,7 @@ use crate::{ verify_person_in_community, }, collections::{ + community_followers::CommunityFollowers, community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, CommunityContext, @@ -21,10 +22,6 @@ use crate::{ }, objects::community::ApubCommunity, }; -use activitystreams::{ - base::BaseExt, - collection::{CollectionExt, UnorderedCollection}, -}; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -32,7 +29,6 @@ use lemmy_apub_lib::{ verify::verify_domains_match, }; use lemmy_db_schema::source::community::Community; -use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use log::info; @@ -118,19 +114,8 @@ pub(crate) async fn get_apub_community_followers( Community::read_from_name(conn, &info.community_name) }) .await??; - - let community_id = community.id; - let community_followers = blocking(context.pool(), move |conn| { - CommunityFollowerView::for_community(conn, community_id) - }) - .await??; - - let mut collection = UnorderedCollection::new(); - collection - .set_id(community.followers_url.into()) - .set_total_items(community_followers.len() as u64); - let collection = WithContext::new(collection); - Ok(create_apub_response(&collection)) + let followers = CommunityFollowers::new(community, &context).await?; + Ok(create_apub_response(&followers)) } /// Returns the community outbox, which is populated by a maximum of 20 posts (but no other diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index 2cc18768..a2d4fedd 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -8,8 +8,8 @@ use crate::{ undo_delete::UndoDeletePrivateMessage, }, }, + collections::user_outbox::UserOutbox, context::WithContext, - generate_outbox_url, http::{ create_apub_response, create_apub_tombstone_response, @@ -18,10 +18,6 @@ use crate::{ }, objects::person::ApubPerson, }; -use activitystreams::{ - base::BaseExt, - collection::{CollectionExt, OrderedCollection}, -}; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use lemmy_api_common::blocking; use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject}; @@ -30,7 +26,6 @@ use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use log::info; use serde::{Deserialize, Serialize}; -use url::Url; #[derive(Deserialize)] pub struct PersonQuery { @@ -100,12 +95,6 @@ pub(crate) async fn get_apub_person_outbox( Person::find_by_name(conn, &info.user_name) }) .await??; - // TODO: populate the person outbox - let mut collection = OrderedCollection::new(); - collection - .set_many_items(Vec::::new()) - .set_id(generate_outbox_url(&person.actor_id)?.into()) - .set_total_items(0_u64); - let collection = WithContext::new(collection); - Ok(create_apub_response(&collection)) + let outbox = UserOutbox::new(person).await?; + Ok(create_apub_response(&outbox)) } From 5ff044346f1f94a7d37107b4ca081cf1fbd6eff8 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 28 Oct 2021 23:17:59 +0200 Subject: [PATCH 11/17] Move object and collection structs to protocol folder --- .cargo-husky/hooks/pre-commit | 2 +- .../activities/comment/create_or_update.rs | 45 +++--- .../apub/src/activities/community/update.rs | 36 ++--- .../src/activities/post/create_or_update.rs | 35 +++-- .../private_message/create_or_update.rs | 6 +- .../src/collections/community_moderators.rs | 10 +- .../apub/src/collections/community_outbox.rs | 11 +- crates/apub/src/collections/mod.rs | 5 +- crates/apub/src/fetcher/post_or_comment.rs | 14 +- crates/apub/src/fetcher/search.rs | 25 ++-- crates/apub/src/http/community.rs | 26 ++-- crates/apub/src/http/person.rs | 20 +-- crates/apub/src/lib.rs | 1 + crates/apub/src/objects/comment.rs | 136 ++++-------------- crates/apub/src/objects/community.rs | 126 ++++------------ crates/apub/src/objects/mod.rs | 23 +-- crates/apub/src/objects/person.rs | 3 +- crates/apub/src/objects/post.rs | 101 +------------ crates/apub/src/objects/private_message.rs | 66 ++------- .../collections/group_followers.rs} | 0 .../protocol/collections/group_moderators.rs | 12 ++ .../src/protocol/collections/group_outbox.rs | 13 ++ crates/apub/src/protocol/collections/mod.rs | 4 + .../collections/person_outbox.rs} | 0 crates/apub/src/protocol/mod.rs | 23 +++ .../apub/src/protocol/objects/chat_message.rs | 61 ++++++++ crates/apub/src/protocol/objects/group.rs | 95 ++++++++++++ crates/apub/src/protocol/objects/mod.rs | 5 + crates/apub/src/protocol/objects/note.rs | 112 +++++++++++++++ crates/apub/src/protocol/objects/page.rs | 97 +++++++++++++ .../src/{ => protocol}/objects/tombstone.rs | 0 31 files changed, 605 insertions(+), 508 deletions(-) rename crates/apub/src/{collections/community_followers.rs => protocol/collections/group_followers.rs} (100%) create mode 100644 crates/apub/src/protocol/collections/group_moderators.rs create mode 100644 crates/apub/src/protocol/collections/group_outbox.rs create mode 100644 crates/apub/src/protocol/collections/mod.rs rename crates/apub/src/{collections/user_outbox.rs => protocol/collections/person_outbox.rs} (100%) create mode 100644 crates/apub/src/protocol/mod.rs create mode 100644 crates/apub/src/protocol/objects/chat_message.rs create mode 100644 crates/apub/src/protocol/objects/group.rs create mode 100644 crates/apub/src/protocol/objects/mod.rs create mode 100644 crates/apub/src/protocol/objects/note.rs create mode 100644 crates/apub/src/protocol/objects/page.rs rename crates/apub/src/{ => protocol}/objects/tombstone.rs (100%) diff --git a/.cargo-husky/hooks/pre-commit b/.cargo-husky/hooks/pre-commit index 4b50f2a5..1c2858d4 100755 --- a/.cargo-husky/hooks/pre-commit +++ b/.cargo-husky/hooks/pre-commit @@ -1,7 +1,7 @@ #!/bin/bash set -e -cargo +nightly fmt +cargo +nightly fmt -- --check cargo +nightly clippy --workspace --tests --all-targets --all-features -- \ -D warnings -D deprecated -D clippy::perf -D clippy::complexity -D clippy::dbg_macro diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 18661cbf..d53801aa 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -1,3 +1,20 @@ +use activitystreams::{link::Mention, public, unparsed::Unparsed}; +use serde::{Deserialize, Serialize}; +use url::Url; + +use lemmy_api_common::{blocking, check_post_deleted_or_removed}; +use lemmy_apub_lib::{ + data::Data, + traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, + verify::verify_domains_match, +}; +use lemmy_db_schema::{ + source::{community::Community, post::Post}, + traits::Crud, +}; +use lemmy_utils::LemmyError; +use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud}; + use crate::{ activities::{ check_community_deleted_or_removed, @@ -13,27 +30,9 @@ use crate::{ CreateOrUpdateType, }, fetcher::object_id::ObjectId, - objects::{ - comment::{ApubComment, Note}, - community::ApubCommunity, - person::ApubPerson, - }, + objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson}, + protocol::objects::note::Note, }; -use activitystreams::{link::Mention, public, unparsed::Unparsed}; -use lemmy_api_common::{blocking, check_post_deleted_or_removed}; -use lemmy_apub_lib::{ - data::Data, - traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, - verify::verify_domains_match, -}; -use lemmy_db_schema::{ - source::{community::Community, post::Post}, - traits::Crud, -}; -use lemmy_utils::LemmyError; -use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud}; -use serde::{Deserialize, Serialize}; -use url::Url; #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] @@ -153,10 +152,12 @@ impl GetCommunity for CreateOrUpdateComment { #[cfg(test)] mod tests { - use super::*; - use crate::objects::tests::file_to_json_object; use serial_test::serial; + use crate::objects::tests::file_to_json_object; + + use super::*; + #[actix_rt::test] #[serial] async fn test_parse_pleroma_create_comment() { diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index 78fce324..df34ca28 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -1,3 +1,19 @@ +use activitystreams::{activity::kind::UpdateType, public, unparsed::Unparsed}; +use serde::{Deserialize, Serialize}; +use url::Url; + +use lemmy_api_common::blocking; +use lemmy_apub_lib::{ + data::Data, + traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, +}; +use lemmy_db_schema::{ + source::community::{Community, CommunityForm}, + traits::Crud, +}; +use lemmy_utils::LemmyError; +use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; + use crate::{ activities::{ community::{ @@ -11,25 +27,9 @@ use crate::{ verify_person_in_community, }, fetcher::object_id::ObjectId, - objects::{ - community::{ApubCommunity, Group}, - person::ApubPerson, - }, + objects::{community::ApubCommunity, person::ApubPerson}, + protocol::objects::group::Group, }; -use activitystreams::{activity::kind::UpdateType, public, unparsed::Unparsed}; -use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - data::Data, - traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, -}; -use lemmy_db_schema::{ - source::community::{Community, CommunityForm}, - traits::Crud, -}; -use lemmy_utils::LemmyError; -use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; -use serde::{Deserialize, Serialize}; -use url::Url; /// This activity is received from a remote community mod, and updates the description or other /// fields of a local community. diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index b5d9a202..ee1bf19c 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -1,3 +1,18 @@ +use activitystreams::{public, unparsed::Unparsed}; +use anyhow::anyhow; +use serde::{Deserialize, Serialize}; +use url::Url; + +use lemmy_api_common::blocking; +use lemmy_apub_lib::{ + data::Data, + traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, + verify::{verify_domains_match, verify_urls_match}, +}; +use lemmy_db_schema::{source::community::Community, traits::Crud}; +use lemmy_utils::LemmyError; +use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; + use crate::{ activities::{ check_community_deleted_or_removed, @@ -13,25 +28,9 @@ use crate::{ CreateOrUpdateType, }, fetcher::object_id::ObjectId, - objects::{ - community::ApubCommunity, - person::ApubPerson, - post::{ApubPost, Page}, - }, + objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, + protocol::objects::page::Page, }; -use activitystreams::{public, unparsed::Unparsed}; -use anyhow::anyhow; -use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - data::Data, - traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, - verify::{verify_domains_match, verify_urls_match}, -}; -use lemmy_db_schema::{source::community::Community, traits::Crud}; -use lemmy_utils::LemmyError; -use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; -use serde::{Deserialize, Serialize}; -use url::Url; #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs index 72a38e5e..0067607e 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -7,10 +7,8 @@ use crate::{ CreateOrUpdateType, }, fetcher::object_id::ObjectId, - objects::{ - person::ApubPerson, - private_message::{ApubPrivateMessage, ChatMessage}, - }, + objects::{person::ApubPerson, private_message::ApubPrivateMessage}, + protocol::objects::chat_message::ChatMessage, }; use activitystreams::unparsed::Unparsed; use lemmy_api_common::blocking; diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index d91b41c4..1a5a96d3 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -3,6 +3,7 @@ use crate::{ fetcher::object_id::ObjectId, generate_moderators_url, objects::person::ApubPerson, + protocol::collections::group_moderators::GroupModerators, }; use activitystreams::{chrono::NaiveDateTime, collection::kind::OrderedCollectionType}; use lemmy_api_common::blocking; @@ -13,17 +14,8 @@ use lemmy_db_schema::{ }; use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView; use lemmy_utils::LemmyError; -use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct GroupModerators { - r#type: OrderedCollectionType, - id: Url, - ordered_items: Vec>, -} - #[derive(Clone, Debug)] pub(crate) struct ApubCommunityModerators(pub(crate) Vec); diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index 98fb6644..cf5ad8b2 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -3,6 +3,7 @@ use crate::{ collections::CommunityContext, generate_outbox_url, objects::{person::ApubPerson, post::ApubPost}, + protocol::collections::group_outbox::GroupOutbox, }; use activitystreams::collection::kind::OrderedCollectionType; use chrono::NaiveDateTime; @@ -17,18 +18,8 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_utils::LemmyError; -use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct GroupOutbox { - r#type: OrderedCollectionType, - id: Url, - total_items: i32, - ordered_items: Vec, -} - #[derive(Clone, Debug)] pub(crate) struct ApubCommunityOutbox(Vec); diff --git a/crates/apub/src/collections/mod.rs b/crates/apub/src/collections/mod.rs index 378e14c0..a2e77d1b 100644 --- a/crates/apub/src/collections/mod.rs +++ b/crates/apub/src/collections/mod.rs @@ -1,10 +1,9 @@ -use crate::objects::community::ApubCommunity; use lemmy_websocket::LemmyContext; -pub(crate) mod community_followers; +use crate::objects::community::ApubCommunity; + pub(crate) mod community_moderators; pub(crate) mod community_outbox; -pub(crate) mod user_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/post_or_comment.rs b/crates/apub/src/fetcher/post_or_comment.rs index ac391120..c0bc46a8 100644 --- a/crates/apub/src/fetcher/post_or_comment.rs +++ b/crates/apub/src/fetcher/post_or_comment.rs @@ -1,14 +1,16 @@ -use crate::objects::{ - comment::{ApubComment, Note}, - post::{ApubPost, Page}, -}; use chrono::NaiveDateTime; +use serde::Deserialize; +use url::Url; + use lemmy_apub_lib::traits::ApubObject; use lemmy_db_schema::source::{comment::CommentForm, post::PostForm}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::Deserialize; -use url::Url; + +use crate::{ + objects::{comment::ApubComment, post::ApubPost}, + protocol::objects::{note::Note, page::Page}, +}; #[derive(Clone, Debug)] pub enum PostOrComment { diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index 5c2a3e7b..77a23828 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -1,15 +1,9 @@ -use crate::{ - fetcher::object_id::ObjectId, - objects::{ - comment::{ApubComment, Note}, - community::{ApubCommunity, Group}, - person::{ApubPerson, Person}, - post::{ApubPost, Page}, - }, -}; use anyhow::anyhow; use chrono::NaiveDateTime; use itertools::Itertools; +use serde::Deserialize; +use url::Url; + use lemmy_api_common::blocking; use lemmy_apub_lib::{ traits::ApubObject, @@ -21,8 +15,17 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::Deserialize; -use url::Url; + +use crate::{ + fetcher::object_id::ObjectId, + objects::{ + comment::ApubComment, + community::ApubCommunity, + person::{ApubPerson, Person}, + post::ApubPost, + }, + protocol::objects::{group::Group, note::Note, page::Page}, +}; /// Attempt to parse the query as URL, and fetch an ActivityPub object from it. /// diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index 73f59d03..0a7cb664 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -1,3 +1,16 @@ +use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; +use log::info; +use serde::{Deserialize, Serialize}; + +use lemmy_api_common::blocking; +use lemmy_apub_lib::{ + traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, + verify::verify_domains_match, +}; +use lemmy_db_schema::source::community::Community; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; + use crate::{ activities::{ community::announce::{AnnouncableActivities, AnnounceActivity, GetCommunity}, @@ -6,7 +19,6 @@ use crate::{ verify_person_in_community, }, collections::{ - community_followers::CommunityFollowers, community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, CommunityContext, @@ -21,18 +33,8 @@ use crate::{ receive_activity, }, objects::community::ApubCommunity, + protocol::collections::group_followers::CommunityFollowers, }; -use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; -use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, - verify::verify_domains_match, -}; -use lemmy_db_schema::source::community::Community; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use log::info; -use serde::{Deserialize, Serialize}; #[derive(Deserialize)] pub(crate) struct CommunityQuery { diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index a2d4fedd..77bfd694 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -1,3 +1,13 @@ +use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; +use log::info; +use serde::{Deserialize, Serialize}; + +use lemmy_api_common::blocking; +use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject}; +use lemmy_db_schema::source::person::Person; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; + use crate::{ activities::{ community::announce::{AnnouncableActivities, AnnounceActivity}, @@ -8,7 +18,6 @@ use crate::{ undo_delete::UndoDeletePrivateMessage, }, }, - collections::user_outbox::UserOutbox, context::WithContext, http::{ create_apub_response, @@ -17,15 +26,8 @@ use crate::{ receive_activity, }, objects::person::ApubPerson, + protocol::collections::person_outbox::UserOutbox, }; -use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; -use lemmy_api_common::blocking; -use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject}; -use lemmy_db_schema::source::person::Person; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use log::info; -use serde::{Deserialize, Serialize}; #[derive(Deserialize)] pub struct PersonQuery { diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index f60e04af..d188274b 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -5,6 +5,7 @@ pub mod fetcher; pub mod http; pub mod migrations; pub mod objects; +pub(crate) mod protocol; #[macro_use] extern crate lazy_static; diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 5698640d..cd491d87 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -1,27 +1,17 @@ -use crate::{ - activities::{verify_is_public, verify_person_in_community}, - fetcher::object_id::ObjectId, - objects::{ - community::ApubCommunity, - person::ApubPerson, - post::ApubPost, - tombstone::Tombstone, - Source, - }, - PostOrComment, -}; -use activitystreams::{object::kind::NoteType, public, unparsed::Unparsed}; +use std::ops::Deref; + +use activitystreams::{object::kind::NoteType, public}; use anyhow::anyhow; -use chrono::{DateTime, FixedOffset, NaiveDateTime}; +use chrono::NaiveDateTime; use html2md::parse_html; +use url::Url; + use lemmy_api_common::blocking; use lemmy_apub_lib::{ traits::ApubObject, values::{MediaTypeHtml, MediaTypeMarkdown}, - verify::verify_domains_match, }; use lemmy_db_schema::{ - newtypes::CommentId, source::{ comment::{Comment, CommentForm}, community::Community, @@ -35,100 +25,19 @@ use lemmy_utils::{ LemmyError, }; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; -use std::ops::Deref; -use url::Url; -#[skip_serializing_none] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Note { - r#type: NoteType, - id: Url, - pub(crate) attributed_to: ObjectId, - /// Indicates that the object is publicly readable. Unlike [`Post.to`], this one doesn't contain - /// the community ID, as it would be incompatible with Pleroma (and we can get the community from - /// the post in [`in_reply_to`]). - to: Vec, - content: String, - media_type: Option, - source: SourceCompat, - in_reply_to: ObjectId, - published: Option>, - updated: Option>, - #[serde(flatten)] - unparsed: Unparsed, -} - -/// Pleroma puts a raw string in the source, so we have to handle it here for deserialization to work -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -#[serde(untagged)] -enum SourceCompat { - Lemmy(Source), - Pleroma(String), -} - -impl Note { - pub(crate) fn id_unchecked(&self) -> &Url { - &self.id - } - pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> { - verify_domains_match(&self.id, expected_domain)?; - Ok(&self.id) - } - - pub(crate) async fn get_parents( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> 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, request_counter) - .await?, - ); - match parent.deref() { - PostOrComment::Post(p) => { - // Workaround because I cant figure out how to get the post out of the box (and we dont - // want to stackoverflow in a deep comment hierarchy). - let post_id = p.id; - let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; - Ok((post.into(), None)) - } - PostOrComment::Comment(c) => { - let post_id = c.post_id; - let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; - Ok((post.into(), Some(c.id))) - } - } - } - - pub(crate) async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let (post, _parent_comment_id) = self.get_parents(context, request_counter).await?; - let community_id = post.community_id; - let community: ApubCommunity = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await?? - .into(); - - if post.locked { - return Err(anyhow!("Post is locked").into()); - } - verify_domains_match(self.attributed_to.inner(), &self.id)?; - verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; - verify_is_public(&self.to)?; - Ok(()) - } -} +use crate::{ + activities::verify_person_in_community, + fetcher::object_id::ObjectId, + protocol::{ + objects::{ + note::{Note, SourceCompat}, + tombstone::Tombstone, + }, + Source, + }, + PostOrComment, +}; #[derive(Clone, Debug)] pub struct ApubComment(Comment); @@ -277,13 +186,16 @@ impl ApubObject for ApubComment { #[cfg(test)] pub(crate) mod tests { - use super::*; + use assert_json_diff::assert_json_include; + use serial_test::serial; + use crate::objects::{ community::ApubCommunity, tests::{file_to_json_object, init_context}, }; - use assert_json_diff::assert_json_include; - use serial_test::serial; + + use super::*; + use crate::objects::{person::ApubPerson, post::ApubPost}; pub(crate) async fn prepare_comment_test( url: &Url, diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index c7d1dd3e..0a4c06a4 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -1,117 +1,40 @@ -use crate::{ - check_is_apub_id_valid, - collections::{ - community_moderators::ApubCommunityModerators, - community_outbox::ApubCommunityOutbox, - CommunityContext, - }, - fetcher::object_id::ObjectId, - generate_moderators_url, - generate_outbox_url, - objects::{get_summary_from_string_or_source, tombstone::Tombstone, ImageObject, Source}, -}; +use std::ops::Deref; + use activitystreams::{ actor::{kind::GroupType, Endpoints}, object::kind::ImageType, - unparsed::Unparsed, }; -use chrono::{DateTime, FixedOffset, NaiveDateTime}; +use chrono::NaiveDateTime; use itertools::Itertools; +use log::debug; +use url::Url; + use lemmy_api_common::blocking; use lemmy_apub_lib::{ - signatures::PublicKey, traits::{ActorType, ApubObject}, values::MediaTypeMarkdown, - verify::verify_domains_match, -}; -use lemmy_db_schema::{ - naive_now, - source::community::{Community, CommunityForm}, - DbPool, }; +use lemmy_db_schema::{source::community::Community, DbPool}; use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; use lemmy_utils::{ settings::structs::Settings, - utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html}, + utils::{convert_datetime, markdown_to_html}, LemmyError, }; use lemmy_websocket::LemmyContext; -use log::debug; -use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; -use std::ops::Deref; -use url::Url; -#[skip_serializing_none] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Group { - #[serde(rename = "type")] - kind: GroupType, - pub(crate) id: Url, - /// username, set at account creation and can never be changed - preferred_username: String, - /// title (can be changed at any time) - name: String, - summary: Option, - source: Option, - icon: Option, - /// banner - image: Option, - // lemmy extension - sensitive: Option, - // lemmy extension - pub(crate) moderators: Option>, - inbox: Url, - pub(crate) outbox: ObjectId, - followers: Url, - endpoints: Endpoints, - public_key: PublicKey, - published: Option>, - updated: Option>, - #[serde(flatten)] - unparsed: Unparsed, -} - -impl Group { - pub(crate) async fn from_apub_to_form( - group: &Group, - expected_domain: &Url, - settings: &Settings, - ) -> Result { - verify_domains_match(expected_domain, &group.id)?; - let name = group.preferred_username.clone(); - let title = group.name.clone(); - let description = get_summary_from_string_or_source(&group.summary, &group.source); - let shared_inbox = group.endpoints.shared_inbox.clone().map(|s| s.into()); - - let slur_regex = &settings.slur_regex(); - check_slurs(&name, slur_regex)?; - check_slurs(&title, slur_regex)?; - check_slurs_opt(&description, slur_regex)?; - - Ok(CommunityForm { - name, - title, - description, - removed: None, - published: group.published.map(|u| u.naive_local()), - updated: group.updated.map(|u| u.naive_local()), - deleted: None, - nsfw: Some(group.sensitive.unwrap_or(false)), - actor_id: Some(group.id.clone().into()), - local: Some(false), - private_key: None, - public_key: Some(group.public_key.public_key_pem.clone()), - last_refreshed_at: Some(naive_now()), - icon: Some(group.icon.clone().map(|i| i.url.into())), - banner: Some(group.image.clone().map(|i| i.url.into())), - followers_url: Some(group.followers.clone().into()), - inbox_url: Some(group.inbox.clone().into()), - shared_inbox_url: Some(shared_inbox), - }) - } -} +use crate::{ + check_is_apub_id_valid, + collections::{community_moderators::ApubCommunityModerators, CommunityContext}, + fetcher::object_id::ObjectId, + generate_moderators_url, + generate_outbox_url, + protocol::{ + objects::{group::Group, tombstone::Tombstone}, + ImageObject, + Source, + }, +}; #[derive(Clone, Debug)] pub struct ApubCommunity(Community); @@ -300,12 +223,15 @@ impl ApubCommunity { #[cfg(test)] mod tests { - use super::*; - use crate::objects::tests::{file_to_json_object, init_context}; use assert_json_diff::assert_json_include; - use lemmy_db_schema::traits::Crud; use serial_test::serial; + use lemmy_db_schema::traits::Crud; + + use crate::objects::tests::{file_to_json_object, init_context}; + + use super::*; + #[actix_rt::test] #[serial] async fn test_parse_lemmy_community() { diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs index d0cb1341..b577dabe 100644 --- a/crates/apub/src/objects/mod.rs +++ b/crates/apub/src/objects/mod.rs @@ -1,32 +1,13 @@ -use activitystreams::object::kind::ImageType; +use crate::protocol::Source; use html2md::parse_html; -use lemmy_apub_lib::values::MediaTypeMarkdown; -use serde::{Deserialize, Serialize}; -use url::Url; pub mod comment; pub mod community; pub mod person; pub mod post; pub mod private_message; -pub mod tombstone; -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Source { - content: String, - media_type: MediaTypeMarkdown, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ImageObject { - #[serde(rename = "type")] - kind: ImageType, - url: Url, -} - -fn get_summary_from_string_or_source( +pub(crate) fn get_summary_from_string_or_source( raw: &Option, source: &Option, ) -> Option { diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index 49ba7eb1..1d914e32 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -1,7 +1,8 @@ use crate::{ check_is_apub_id_valid, generate_outbox_url, - objects::{get_summary_from_string_or_source, ImageObject, Source}, + objects::get_summary_from_string_or_source, + protocol::{ImageObject, Source}, }; use activitystreams::{actor::Endpoints, object::kind::ImageType, unparsed::Unparsed}; use chrono::{DateTime, FixedOffset, NaiveDateTime}; diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index ee9aa7b8..3ade8de0 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -1,10 +1,8 @@ use crate::{ - activities::{verify_is_public, verify_person_in_community}, + activities::verify_person_in_community, fetcher::object_id::ObjectId, - objects::{ - community::ApubCommunity, - person::ApubPerson, - tombstone::Tombstone, + protocol::{ + objects::{page::Page, tombstone::Tombstone}, ImageObject, Source, }, @@ -12,15 +10,12 @@ use crate::{ use activitystreams::{ object::kind::{ImageType, PageType}, public, - unparsed::Unparsed, }; -use anyhow::anyhow; -use chrono::{DateTime, FixedOffset, NaiveDateTime}; +use chrono::NaiveDateTime; use lemmy_api_common::blocking; use lemmy_apub_lib::{ traits::ApubObject, values::{MediaTypeHtml, MediaTypeMarkdown}, - verify::verify_domains_match, }; use lemmy_db_schema::{ self, @@ -33,97 +28,13 @@ use lemmy_db_schema::{ }; use lemmy_utils::{ request::fetch_site_data, - utils::{check_slurs, convert_datetime, markdown_to_html, remove_slurs}, + utils::{convert_datetime, markdown_to_html, remove_slurs}, LemmyError, }; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; use std::ops::Deref; use url::Url; -#[skip_serializing_none] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Page { - r#type: PageType, - id: Url, - pub(crate) attributed_to: ObjectId, - to: Vec, - name: String, - content: Option, - media_type: Option, - source: Option, - url: Option, - image: Option, - pub(crate) comments_enabled: Option, - sensitive: Option, - pub(crate) stickied: Option, - published: Option>, - updated: Option>, - #[serde(flatten)] - unparsed: Unparsed, -} - -impl Page { - pub(crate) fn id_unchecked(&self) -> &Url { - &self.id - } - pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> { - verify_domains_match(&self.id, expected_domain)?; - Ok(&self.id) - } - - /// Only mods can change the post's stickied/locked status. So if either of these is changed from - /// 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; - - let is_mod_action = if let Ok(old_post) = old_post { - self.stickied != Some(old_post.stickied) || self.comments_enabled != Some(!old_post.locked) - } else { - false - }; - Ok(is_mod_action) - } - - pub(crate) async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - let community = self.extract_community(context, request_counter).await?; - - check_slurs(&self.name, &context.settings().slur_regex())?; - verify_domains_match(self.attributed_to.inner(), &self.id.clone())?; - verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; - verify_is_public(&self.to.clone())?; - Ok(()) - } - - pub(crate) async fn extract_community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - let mut to_iter = self.to.iter(); - loop { - if let Some(cid) = to_iter.next() { - let cid = ObjectId::new(cid.clone()); - if let Ok(c) = cid.dereference(context, request_counter).await { - break Ok(c); - } - } else { - return Err(anyhow!("No community found in cc").into()); - } - } - } -} - #[derive(Clone, Debug)] pub struct ApubPost(Post); @@ -283,6 +194,8 @@ mod tests { use super::*; use crate::objects::{ community::ApubCommunity, + person::ApubPerson, + post::ApubPost, tests::{file_to_json_object, init_context}, }; use assert_json_diff::assert_json_include; diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index e971ebe3..ee0aec95 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -1,16 +1,16 @@ use crate::{ fetcher::object_id::ObjectId, - objects::{person::ApubPerson, Source}, + protocol::{ + objects::chat_message::{ChatMessage, ChatMessageType}, + Source, + }, }; -use activitystreams::unparsed::Unparsed; -use anyhow::anyhow; -use chrono::{DateTime, FixedOffset, NaiveDateTime}; +use chrono::NaiveDateTime; use html2md::parse_html; use lemmy_api_common::blocking; use lemmy_apub_lib::{ traits::ApubObject, values::{MediaTypeHtml, MediaTypeMarkdown}, - verify::verify_domains_match, }; use lemmy_db_schema::{ source::{ @@ -21,60 +21,9 @@ use lemmy_db_schema::{ }; use lemmy_utils::{utils::convert_datetime, LemmyError}; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; use std::ops::Deref; use url::Url; -#[skip_serializing_none] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ChatMessage { - r#type: ChatMessageType, - id: Url, - pub(crate) attributed_to: ObjectId, - to: [ObjectId; 1], - content: String, - media_type: Option, - source: Option, - published: Option>, - updated: Option>, - #[serde(flatten)] - unparsed: Unparsed, -} - -/// https://docs.pleroma.social/backend/development/ap_extensions/#chatmessages -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum ChatMessageType { - ChatMessage, -} - -impl ChatMessage { - pub(crate) fn id_unchecked(&self) -> &Url { - &self.id - } - pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> { - verify_domains_match(&self.id, expected_domain)?; - Ok(&self.id) - } - - pub(crate) async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_domains_match(self.attributed_to.inner(), &self.id)?; - let person = self - .attributed_to - .dereference(context, request_counter) - .await?; - if person.banned { - return Err(anyhow!("Person is banned from site").into()); - } - Ok(()) - } -} - #[derive(Clone, Debug)] pub struct ApubPrivateMessage(PrivateMessage); @@ -189,7 +138,10 @@ impl ApubObject for ApubPrivateMessage { #[cfg(test)] mod tests { use super::*; - use crate::objects::tests::{file_to_json_object, init_context}; + use crate::objects::{ + person::ApubPerson, + tests::{file_to_json_object, init_context}, + }; use assert_json_diff::assert_json_include; use serial_test::serial; diff --git a/crates/apub/src/collections/community_followers.rs b/crates/apub/src/protocol/collections/group_followers.rs similarity index 100% rename from crates/apub/src/collections/community_followers.rs rename to crates/apub/src/protocol/collections/group_followers.rs diff --git a/crates/apub/src/protocol/collections/group_moderators.rs b/crates/apub/src/protocol/collections/group_moderators.rs new file mode 100644 index 00000000..d37751a1 --- /dev/null +++ b/crates/apub/src/protocol/collections/group_moderators.rs @@ -0,0 +1,12 @@ +use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson}; +use activitystreams::collection::kind::OrderedCollectionType; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GroupModerators { + pub(crate) r#type: OrderedCollectionType, + pub(crate) id: Url, + pub(crate) ordered_items: Vec>, +} diff --git a/crates/apub/src/protocol/collections/group_outbox.rs b/crates/apub/src/protocol/collections/group_outbox.rs new file mode 100644 index 00000000..26da4b6f --- /dev/null +++ b/crates/apub/src/protocol/collections/group_outbox.rs @@ -0,0 +1,13 @@ +use crate::activities::post::create_or_update::CreateOrUpdatePost; +use activitystreams::collection::kind::OrderedCollectionType; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GroupOutbox { + pub(crate) r#type: OrderedCollectionType, + pub(crate) id: Url, + pub(crate) total_items: i32, + pub(crate) ordered_items: Vec, +} diff --git a/crates/apub/src/protocol/collections/mod.rs b/crates/apub/src/protocol/collections/mod.rs new file mode 100644 index 00000000..646abbeb --- /dev/null +++ b/crates/apub/src/protocol/collections/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod group_followers; +pub(crate) mod group_moderators; +pub(crate) mod group_outbox; +pub(crate) mod person_outbox; diff --git a/crates/apub/src/collections/user_outbox.rs b/crates/apub/src/protocol/collections/person_outbox.rs similarity index 100% rename from crates/apub/src/collections/user_outbox.rs rename to crates/apub/src/protocol/collections/person_outbox.rs diff --git a/crates/apub/src/protocol/mod.rs b/crates/apub/src/protocol/mod.rs new file mode 100644 index 00000000..f4ad9e23 --- /dev/null +++ b/crates/apub/src/protocol/mod.rs @@ -0,0 +1,23 @@ +use activitystreams::object::kind::ImageType; +use serde::{Deserialize, Serialize}; +use url::Url; + +use lemmy_apub_lib::values::MediaTypeMarkdown; + +pub(crate) mod collections; +pub(crate) mod objects; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Source { + pub(crate) content: String, + pub(crate) media_type: MediaTypeMarkdown, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ImageObject { + #[serde(rename = "type")] + pub(crate) kind: ImageType, + pub(crate) url: Url, +} diff --git a/crates/apub/src/protocol/objects/chat_message.rs b/crates/apub/src/protocol/objects/chat_message.rs new file mode 100644 index 00000000..038af4ed --- /dev/null +++ b/crates/apub/src/protocol/objects/chat_message.rs @@ -0,0 +1,61 @@ +use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson, protocol::Source}; +use activitystreams::{ + chrono::{DateTime, FixedOffset}, + unparsed::Unparsed, +}; +use anyhow::anyhow; +use lemmy_apub_lib::{values::MediaTypeHtml, verify::verify_domains_match}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; +use url::Url; + +#[skip_serializing_none] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ChatMessage { + pub(crate) r#type: ChatMessageType, + pub(crate) id: Url, + pub(crate) attributed_to: ObjectId, + pub(crate) to: [ObjectId; 1], + pub(crate) content: String, + pub(crate) media_type: Option, + pub(crate) source: Option, + pub(crate) published: Option>, + pub(crate) updated: Option>, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} + +/// https://docs.pleroma.social/backend/development/ap_extensions/#chatmessages +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum ChatMessageType { + ChatMessage, +} + +impl ChatMessage { + pub(crate) fn id_unchecked(&self) -> &Url { + &self.id + } + pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> { + verify_domains_match(&self.id, expected_domain)?; + Ok(&self.id) + } + + pub(crate) async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_domains_match(self.attributed_to.inner(), &self.id)?; + let person = self + .attributed_to + .dereference(context, request_counter) + .await?; + if person.banned { + return Err(anyhow!("Person is banned from site").into()); + } + Ok(()) + } +} diff --git a/crates/apub/src/protocol/objects/group.rs b/crates/apub/src/protocol/objects/group.rs new file mode 100644 index 00000000..94587890 --- /dev/null +++ b/crates/apub/src/protocol/objects/group.rs @@ -0,0 +1,95 @@ +use crate::{ + collections::{ + community_moderators::ApubCommunityModerators, + community_outbox::ApubCommunityOutbox, + }, + fetcher::object_id::ObjectId, + objects::get_summary_from_string_or_source, + protocol::{ImageObject, Source}, +}; +use activitystreams::{ + actor::{kind::GroupType, Endpoints}, + unparsed::Unparsed, +}; +use chrono::{DateTime, FixedOffset}; +use lemmy_apub_lib::{signatures::PublicKey, verify::verify_domains_match}; +use lemmy_db_schema::{naive_now, source::community::CommunityForm}; +use lemmy_utils::{ + settings::structs::Settings, + utils::{check_slurs, check_slurs_opt}, + LemmyError, +}; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; +use url::Url; + +#[skip_serializing_none] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Group { + #[serde(rename = "type")] + pub(crate) kind: GroupType, + pub(crate) id: Url, + /// username, set at account creation and can never be changed + pub(crate) preferred_username: String, + /// title (can be changed at any time) + pub(crate) name: String, + pub(crate) summary: Option, + pub(crate) source: Option, + pub(crate) icon: Option, + /// banner + pub(crate) image: Option, + // lemmy extension + pub(crate) sensitive: Option, + // lemmy extension + pub(crate) moderators: Option>, + pub(crate) inbox: Url, + pub(crate) outbox: ObjectId, + pub(crate) followers: Url, + pub(crate) endpoints: Endpoints, + pub(crate) public_key: PublicKey, + pub(crate) published: Option>, + pub(crate) updated: Option>, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} + +impl Group { + pub(crate) async fn from_apub_to_form( + group: &Group, + expected_domain: &Url, + settings: &Settings, + ) -> Result { + verify_domains_match(expected_domain, &group.id)?; + let name = group.preferred_username.clone(); + let title = group.name.clone(); + let description = get_summary_from_string_or_source(&group.summary, &group.source); + let shared_inbox = group.endpoints.shared_inbox.clone().map(|s| s.into()); + + let slur_regex = &settings.slur_regex(); + check_slurs(&name, slur_regex)?; + check_slurs(&title, slur_regex)?; + check_slurs_opt(&description, slur_regex)?; + + Ok(CommunityForm { + name, + title, + description, + removed: None, + published: group.published.map(|u| u.naive_local()), + updated: group.updated.map(|u| u.naive_local()), + deleted: None, + nsfw: Some(group.sensitive.unwrap_or(false)), + actor_id: Some(group.id.clone().into()), + local: Some(false), + private_key: None, + public_key: Some(group.public_key.public_key_pem.clone()), + last_refreshed_at: Some(naive_now()), + icon: Some(group.icon.clone().map(|i| i.url.into())), + banner: Some(group.image.clone().map(|i| i.url.into())), + followers_url: Some(group.followers.clone().into()), + inbox_url: Some(group.inbox.clone().into()), + shared_inbox_url: Some(shared_inbox), + }) + } +} diff --git a/crates/apub/src/protocol/objects/mod.rs b/crates/apub/src/protocol/objects/mod.rs new file mode 100644 index 00000000..3e133831 --- /dev/null +++ b/crates/apub/src/protocol/objects/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod chat_message; +pub(crate) mod group; +pub(crate) mod note; +pub(crate) mod page; +pub(crate) mod tombstone; diff --git a/crates/apub/src/protocol/objects/note.rs b/crates/apub/src/protocol/objects/note.rs new file mode 100644 index 00000000..bdc4da66 --- /dev/null +++ b/crates/apub/src/protocol/objects/note.rs @@ -0,0 +1,112 @@ +use crate::{ + activities::{verify_is_public, verify_person_in_community}, + fetcher::{object_id::ObjectId, post_or_comment::PostOrComment}, + objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, + protocol::Source, +}; +use activitystreams::{object::kind::NoteType, unparsed::Unparsed}; +use anyhow::anyhow; +use chrono::{DateTime, FixedOffset}; +use lemmy_api_common::blocking; +use lemmy_apub_lib::{values::MediaTypeHtml, verify::verify_domains_match}; +use lemmy_db_schema::{ + newtypes::CommentId, + source::{community::Community, post::Post}, + traits::Crud, +}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; +use std::ops::Deref; +use url::Url; + +#[skip_serializing_none] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Note { + pub(crate) r#type: NoteType, + pub(crate) id: Url, + pub(crate) attributed_to: ObjectId, + /// Indicates that the object is publicly readable. Unlike [`Post.to`], this one doesn't contain + /// the community ID, as it would be incompatible with Pleroma (and we can get the community from + /// the post in [`in_reply_to`]). + pub(crate) to: Vec, + pub(crate) content: String, + pub(crate) media_type: Option, + pub(crate) source: SourceCompat, + pub(crate) in_reply_to: ObjectId, + pub(crate) published: Option>, + pub(crate) updated: Option>, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} + +/// Pleroma puts a raw string in the source, so we have to handle it here for deserialization to work +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +#[serde(untagged)] +pub(crate) enum SourceCompat { + Lemmy(Source), + Pleroma(String), +} + +impl Note { + pub(crate) fn id_unchecked(&self) -> &Url { + &self.id + } + pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> { + verify_domains_match(&self.id, expected_domain)?; + Ok(&self.id) + } + + pub(crate) async fn get_parents( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> 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, request_counter) + .await?, + ); + match parent.deref() { + PostOrComment::Post(p) => { + // Workaround because I cant figure out how to get the post out of the box (and we dont + // want to stackoverflow in a deep comment hierarchy). + let post_id = p.id; + let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; + Ok((post.into(), None)) + } + PostOrComment::Comment(c) => { + let post_id = c.post_id; + let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; + Ok((post.into(), Some(c.id))) + } + } + } + + pub(crate) async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + let (post, _parent_comment_id) = self.get_parents(context, request_counter).await?; + let community_id = post.community_id; + let community: ApubCommunity = blocking(context.pool(), move |conn| { + Community::read(conn, community_id) + }) + .await?? + .into(); + + if post.locked { + return Err(anyhow!("Post is locked").into()); + } + verify_domains_match(self.attributed_to.inner(), &self.id)?; + verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; + verify_is_public(&self.to)?; + Ok(()) + } +} diff --git a/crates/apub/src/protocol/objects/page.rs b/crates/apub/src/protocol/objects/page.rs new file mode 100644 index 00000000..7887f19c --- /dev/null +++ b/crates/apub/src/protocol/objects/page.rs @@ -0,0 +1,97 @@ +use crate::{ + activities::{verify_is_public, verify_person_in_community}, + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, + protocol::{ImageObject, Source}, +}; +use activitystreams::{object::kind::PageType, unparsed::Unparsed}; +use anyhow::anyhow; +use chrono::{DateTime, FixedOffset}; +use lemmy_apub_lib::{values::MediaTypeHtml, verify::verify_domains_match}; +use lemmy_utils::{utils::check_slurs, LemmyError}; +use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; +use url::Url; + +#[skip_serializing_none] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Page { + pub(crate) r#type: PageType, + pub(crate) id: Url, + pub(crate) attributed_to: ObjectId, + pub(crate) to: Vec, + pub(crate) name: String, + pub(crate) content: Option, + pub(crate) media_type: Option, + pub(crate) source: Option, + pub(crate) url: Option, + pub(crate) image: Option, + pub(crate) comments_enabled: Option, + pub(crate) sensitive: Option, + pub(crate) stickied: Option, + pub(crate) published: Option>, + pub(crate) updated: Option>, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} + +impl Page { + pub(crate) fn id_unchecked(&self) -> &Url { + &self.id + } + pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> { + verify_domains_match(&self.id, expected_domain)?; + Ok(&self.id) + } + + /// Only mods can change the post's stickied/locked status. So if either of these is changed from + /// 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; + + let is_mod_action = if let Ok(old_post) = old_post { + self.stickied != Some(old_post.stickied) || self.comments_enabled != Some(!old_post.locked) + } else { + false + }; + Ok(is_mod_action) + } + + pub(crate) async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + let community = self.extract_community(context, request_counter).await?; + + check_slurs(&self.name, &context.settings().slur_regex())?; + verify_domains_match(self.attributed_to.inner(), &self.id.clone())?; + verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; + verify_is_public(&self.to.clone())?; + Ok(()) + } + + pub(crate) async fn extract_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let mut to_iter = self.to.iter(); + loop { + if let Some(cid) = to_iter.next() { + let cid = ObjectId::new(cid.clone()); + if let Ok(c) = cid.dereference(context, request_counter).await { + break Ok(c); + } + } else { + return Err(anyhow!("No community found in cc").into()); + } + } + } +} diff --git a/crates/apub/src/objects/tombstone.rs b/crates/apub/src/protocol/objects/tombstone.rs similarity index 100% rename from crates/apub/src/objects/tombstone.rs rename to crates/apub/src/protocol/objects/tombstone.rs From 03d8ac75efa9badfb476a54d026c5511e028b2b6 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Fri, 29 Oct 2021 12:32:42 +0200 Subject: [PATCH 12/17] Move activity structs to protocol folder --- crates/api/src/comment.rs | 11 +- crates/api/src/comment_report.rs | 6 +- crates/api/src/community.rs | 6 +- crates/api/src/post.rs | 8 +- crates/api/src/post_report.rs | 6 +- crates/api_crud/src/comment/create.rs | 12 +- crates/api_crud/src/comment/update.rs | 8 +- crates/api_crud/src/community/update.rs | 2 +- crates/api_crud/src/post/create.rs | 16 +-- crates/api_crud/src/post/update.rs | 9 +- crates/api_crud/src/private_message/create.rs | 4 +- crates/api_crud/src/private_message/delete.rs | 2 +- crates/api_crud/src/private_message/update.rs | 2 +- .../activities/comment/create_or_update.rs | 31 +---- .../apub/src/activities/community/add_mod.rs | 29 +---- .../apub/src/activities/community/announce.rs | 76 +----------- .../src/activities/community/block_user.rs | 28 +---- crates/apub/src/activities/community/mod.rs | 20 ++-- .../src/activities/community/remove_mod.rs | 29 +---- .../src/activities/{ => community}/report.rs | 43 +++---- .../activities/community/undo_block_user.rs | 31 ++--- .../apub/src/activities/community/update.rs | 55 +++------ crates/apub/src/activities/deletion/delete.rs | 71 ++++------- crates/apub/src/activities/deletion/mod.rs | 19 ++- .../src/activities/deletion/undo_delete.rs | 55 ++++----- .../apub/src/activities/following/accept.rs | 26 +--- .../apub/src/activities/following/follow.rs | 21 +--- crates/apub/src/activities/following/mod.rs | 2 +- .../following/{undo.rs => undo_follow.rs} | 26 +--- crates/apub/src/activities/mod.rs | 10 +- .../src/activities/post/create_or_update.rs | 27 +---- .../private_message/create_or_update.rs | 31 +---- .../src/activities/private_message/delete.rs | 20 +--- .../activities/private_message/undo_delete.rs | 29 +---- crates/apub/src/activities/voting/mod.rs | 9 +- .../apub/src/activities/voting/undo_vote.rs | 59 ++++----- crates/apub/src/activities/voting/vote.rs | 83 ++++--------- crates/apub/src/activity_lists.rs | 113 ++++++++++++++++++ .../apub/src/collections/community_outbox.rs | 20 ++-- crates/apub/src/http/community.rs | 47 +++----- crates/apub/src/http/mod.rs | 16 +-- crates/apub/src/http/person.rs | 41 ++----- crates/apub/src/lib.rs | 3 +- .../protocol/activities/community/add_mod.rs | 20 ++++ .../protocol/activities/community/announce.rs | 23 ++++ .../activities/community/block_user.rs | 23 ++++ .../src/protocol/activities/community/mod.rs | 7 ++ .../activities/community/remove_mod.rs | 20 ++++ .../protocol/activities/community/report.rs | 22 ++++ .../activities/community/undo_block_user.rs | 23 ++++ .../protocol/activities/community/update.rs | 26 ++++ .../activities/create_or_update/comment.rs | 25 ++++ .../activities/create_or_update/mod.rs | 2 + .../activities/create_or_update/post.rs | 23 ++++ .../protocol/activities/deletion/delete.rs | 24 ++++ .../src/protocol/activities/deletion/mod.rs | 2 + .../activities/deletion/undo_delete.rs | 25 ++++ .../protocol/activities/following/accept.rs | 22 ++++ .../protocol/activities/following/follow.rs | 21 ++++ .../src/protocol/activities/following/mod.rs | 3 + .../activities/following/undo_follow.rs | 22 ++++ crates/apub/src/protocol/activities/mod.rs | 15 +++ .../private_message/create_or_update.rs | 22 ++++ .../activities/private_message/delete.rs | 21 ++++ .../activities/private_message/mod.rs | 3 + .../activities/private_message/undo_delete.rs | 22 ++++ .../src/protocol/activities/voting/mod.rs | 2 + .../protocol/activities/voting/undo_vote.rs | 25 ++++ .../src/protocol/activities/voting/vote.rs | 53 ++++++++ .../src/protocol/collections/group_outbox.rs | 2 +- crates/apub/src/protocol/mod.rs | 1 + 71 files changed, 910 insertions(+), 751 deletions(-) rename crates/apub/src/activities/{ => community}/report.rs (88%) rename crates/apub/src/activities/following/{undo.rs => undo_follow.rs} (79%) create mode 100644 crates/apub/src/activity_lists.rs create mode 100644 crates/apub/src/protocol/activities/community/add_mod.rs create mode 100644 crates/apub/src/protocol/activities/community/announce.rs create mode 100644 crates/apub/src/protocol/activities/community/block_user.rs create mode 100644 crates/apub/src/protocol/activities/community/mod.rs create mode 100644 crates/apub/src/protocol/activities/community/remove_mod.rs create mode 100644 crates/apub/src/protocol/activities/community/report.rs create mode 100644 crates/apub/src/protocol/activities/community/undo_block_user.rs create mode 100644 crates/apub/src/protocol/activities/community/update.rs create mode 100644 crates/apub/src/protocol/activities/create_or_update/comment.rs create mode 100644 crates/apub/src/protocol/activities/create_or_update/mod.rs create mode 100644 crates/apub/src/protocol/activities/create_or_update/post.rs create mode 100644 crates/apub/src/protocol/activities/deletion/delete.rs create mode 100644 crates/apub/src/protocol/activities/deletion/mod.rs create mode 100644 crates/apub/src/protocol/activities/deletion/undo_delete.rs create mode 100644 crates/apub/src/protocol/activities/following/accept.rs create mode 100644 crates/apub/src/protocol/activities/following/follow.rs create mode 100644 crates/apub/src/protocol/activities/following/mod.rs create mode 100644 crates/apub/src/protocol/activities/following/undo_follow.rs create mode 100644 crates/apub/src/protocol/activities/mod.rs create mode 100644 crates/apub/src/protocol/activities/private_message/create_or_update.rs create mode 100644 crates/apub/src/protocol/activities/private_message/delete.rs create mode 100644 crates/apub/src/protocol/activities/private_message/mod.rs create mode 100644 crates/apub/src/protocol/activities/private_message/undo_delete.rs create mode 100644 crates/apub/src/protocol/activities/voting/mod.rs create mode 100644 crates/apub/src/protocol/activities/voting/undo_vote.rs create mode 100644 crates/apub/src/protocol/activities/voting/vote.rs diff --git a/crates/api/src/comment.rs b/crates/api/src/comment.rs index 62b01b33..e6eef2cb 100644 --- a/crates/api/src/comment.rs +++ b/crates/api/src/comment.rs @@ -1,5 +1,7 @@ -use crate::Perform; +use std::convert::TryInto; + use actix_web::web::Data; + use lemmy_api_common::{ blocking, check_community_ban, @@ -9,11 +11,11 @@ use lemmy_api_common::{ get_local_user_view_from_jwt, }; use lemmy_apub::{ - activities::voting::{ + fetcher::post_or_comment::PostOrComment, + protocol::activities::voting::{ undo_vote::UndoVote, vote::{Vote, VoteType}, }, - fetcher::post_or_comment::PostOrComment, }; use lemmy_db_schema::{ newtypes::LocalUserId, @@ -23,7 +25,8 @@ use lemmy_db_schema::{ use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView}; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation}; -use std::convert::TryInto; + +use crate::Perform; #[async_trait::async_trait(?Send)] impl Perform for MarkCommentAsRead { diff --git a/crates/api/src/comment_report.rs b/crates/api/src/comment_report.rs index a7299b7a..82ecc1f4 100644 --- a/crates/api/src/comment_report.rs +++ b/crates/api/src/comment_report.rs @@ -1,5 +1,5 @@ -use crate::Perform; use actix_web::web::Data; + use lemmy_api_common::{ blocking, check_community_ban, @@ -7,7 +7,7 @@ use lemmy_api_common::{ get_local_user_view_from_jwt, is_mod_or_admin, }; -use lemmy_apub::{activities::report::Report, fetcher::object_id::ObjectId}; +use lemmy_apub::{fetcher::object_id::ObjectId, protocol::activities::community::report::Report}; use lemmy_db_schema::{source::comment_report::*, traits::Reportable}; use lemmy_db_views::{ comment_report_view::{CommentReportQueryBuilder, CommentReportView}, @@ -16,6 +16,8 @@ use lemmy_db_views::{ use lemmy_utils::{ApiError, ConnectionId, LemmyError}; use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation}; +use crate::Perform; + /// Creates a comment report and notifies the moderators of the community #[async_trait::async_trait(?Send)] impl Perform for CreateCommentReport { diff --git a/crates/api/src/community.rs b/crates/api/src/community.rs index ac39751c..b47a4ec6 100644 --- a/crates/api/src/community.rs +++ b/crates/api/src/community.rs @@ -10,16 +10,16 @@ use lemmy_api_common::{ is_mod_or_admin, }; use lemmy_apub::{ - activities::{ + objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::{ community::{ add_mod::AddMod, block_user::BlockUserFromCommunity, remove_mod::RemoveMod, undo_block_user::UndoBlockUserFromCommunity, }, - following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity}, + following::{follow::FollowCommunity as FollowCommunityApub, undo_follow::UndoFollowCommunity}, }, - objects::{community::ApubCommunity, person::ApubPerson}, }; use lemmy_db_schema::{ source::{ diff --git a/crates/api/src/post.rs b/crates/api/src/post.rs index 488c8f59..3564f135 100644 --- a/crates/api/src/post.rs +++ b/crates/api/src/post.rs @@ -12,16 +12,16 @@ use lemmy_api_common::{ post::*, }; use lemmy_apub::{ - activities::{ - post::create_or_update::CreateOrUpdatePost, + fetcher::post_or_comment::PostOrComment, + objects::post::ApubPost, + protocol::activities::{ + create_or_update::post::CreateOrUpdatePost, voting::{ undo_vote::UndoVote, vote::{Vote, VoteType}, }, CreateOrUpdateType, }, - fetcher::post_or_comment::PostOrComment, - objects::post::ApubPost, }; use lemmy_db_schema::{ source::{moderator::*, post::*}, diff --git a/crates/api/src/post_report.rs b/crates/api/src/post_report.rs index 3e610bff..98b2f1c1 100644 --- a/crates/api/src/post_report.rs +++ b/crates/api/src/post_report.rs @@ -1,5 +1,5 @@ -use crate::Perform; use actix_web::web::Data; + use lemmy_api_common::{ blocking, check_community_ban, @@ -13,7 +13,7 @@ use lemmy_api_common::{ ResolvePostReport, }, }; -use lemmy_apub::{activities::report::Report, fetcher::object_id::ObjectId}; +use lemmy_apub::{fetcher::object_id::ObjectId, protocol::activities::community::report::Report}; use lemmy_db_schema::{ source::post_report::{PostReport, PostReportForm}, traits::Reportable, @@ -25,6 +25,8 @@ use lemmy_db_views::{ use lemmy_utils::{ApiError, ConnectionId, LemmyError}; use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation}; +use crate::Perform; + /// Creates a post report and notifies the moderators of the community #[async_trait::async_trait(?Send)] impl Perform for CreatePostReport { diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index 6c90e94e..ab093ea1 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -1,5 +1,5 @@ -use crate::PerformCrud; use actix_web::web::Data; + use lemmy_api_common::{ blocking, check_community_ban, @@ -11,13 +11,13 @@ use lemmy_api_common::{ get_post, }; use lemmy_apub::{ - activities::{ - comment::create_or_update::CreateOrUpdateComment, + fetcher::post_or_comment::PostOrComment, + generate_local_apub_endpoint, + protocol::activities::{ + create_or_update::comment::CreateOrUpdateComment, voting::vote::{Vote, VoteType}, CreateOrUpdateType, }, - fetcher::post_or_comment::PostOrComment, - generate_local_apub_endpoint, EndpointType, }; use lemmy_db_schema::{ @@ -40,6 +40,8 @@ use lemmy_websocket::{ UserOperationCrud, }; +use crate::PerformCrud; + #[async_trait::async_trait(?Send)] impl PerformCrud for CreateComment { type Response = CommentResponse; diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs index 9a164fc3..70f15dfb 100644 --- a/crates/api_crud/src/comment/update.rs +++ b/crates/api_crud/src/comment/update.rs @@ -1,5 +1,5 @@ -use crate::PerformCrud; use actix_web::web::Data; + use lemmy_api_common::{ blocking, check_community_ban, @@ -8,8 +8,8 @@ use lemmy_api_common::{ comment::*, get_local_user_view_from_jwt, }; -use lemmy_apub::activities::{ - comment::create_or_update::CreateOrUpdateComment, +use lemmy_apub::protocol::activities::{ + create_or_update::comment::CreateOrUpdateComment, CreateOrUpdateType, }; use lemmy_db_schema::source::comment::Comment; @@ -26,6 +26,8 @@ use lemmy_websocket::{ UserOperationCrud, }; +use crate::PerformCrud; + #[async_trait::async_trait(?Send)] impl PerformCrud for EditComment { type Response = CommentResponse; diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs index 97722a98..4764b025 100644 --- a/crates/api_crud/src/community/update.rs +++ b/crates/api_crud/src/community/update.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ community::{CommunityResponse, EditCommunity}, get_local_user_view_from_jwt, }; -use lemmy_apub::activities::community::update::UpdateCommunity; +use lemmy_apub::protocol::activities::community::update::UpdateCommunity; use lemmy_db_schema::{ diesel_option_overwrite_to_url, naive_now, diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index a6641034..99b67d2e 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -1,5 +1,7 @@ -use crate::PerformCrud; use actix_web::web::Data; +use log::warn; +use webmention::{Webmention, WebmentionError}; + use lemmy_api_common::{ blocking, check_community_ban, @@ -10,13 +12,13 @@ use lemmy_api_common::{ post::*, }; use lemmy_apub::{ - activities::{ - post::create_or_update::CreateOrUpdatePost, + fetcher::post_or_comment::PostOrComment, + generate_local_apub_endpoint, + protocol::activities::{ + create_or_update::post::CreateOrUpdatePost, voting::vote::{Vote, VoteType}, CreateOrUpdateType, }, - fetcher::post_or_comment::PostOrComment, - generate_local_apub_endpoint, EndpointType, }; use lemmy_db_schema::{ @@ -31,8 +33,8 @@ use lemmy_utils::{ LemmyError, }; use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; -use log::warn; -use webmention::{Webmention, WebmentionError}; + +use crate::PerformCrud; #[async_trait::async_trait(?Send)] impl PerformCrud for CreatePost { diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index 96e4400a..0a982d68 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -1,5 +1,5 @@ -use crate::PerformCrud; use actix_web::web::Data; + use lemmy_api_common::{ blocking, check_community_ban, @@ -7,7 +7,10 @@ use lemmy_api_common::{ get_local_user_view_from_jwt, post::*, }; -use lemmy_apub::activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType}; +use lemmy_apub::protocol::activities::{ + create_or_update::post::CreateOrUpdatePost, + CreateOrUpdateType, +}; use lemmy_db_schema::{ naive_now, source::post::{Post, PostForm}, @@ -22,6 +25,8 @@ use lemmy_utils::{ }; use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; +use crate::PerformCrud; + #[async_trait::async_trait(?Send)] impl PerformCrud for EditPost { type Response = PostResponse; diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index c7bca4e0..705d781e 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -7,11 +7,11 @@ use lemmy_api_common::{ person::{CreatePrivateMessage, PrivateMessageResponse}, }; use lemmy_apub::{ - activities::{ + generate_local_apub_endpoint, + protocol::activities::{ private_message::create_or_update::CreateOrUpdatePrivateMessage, CreateOrUpdateType, }, - generate_local_apub_endpoint, EndpointType, }; use lemmy_db_schema::{ diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs index f369f82b..06bc22ed 100644 --- a/crates/api_crud/src/private_message/delete.rs +++ b/crates/api_crud/src/private_message/delete.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ get_local_user_view_from_jwt, person::{DeletePrivateMessage, PrivateMessageResponse}, }; -use lemmy_apub::activities::private_message::{ +use lemmy_apub::protocol::activities::private_message::{ delete::DeletePrivateMessage as DeletePrivateMessageApub, undo_delete::UndoDeletePrivateMessage, }; diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs index d72e3b13..8114556c 100644 --- a/crates/api_crud/src/private_message/update.rs +++ b/crates/api_crud/src/private_message/update.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ get_local_user_view_from_jwt, person::{EditPrivateMessage, PrivateMessageResponse}, }; -use lemmy_apub::activities::{ +use lemmy_apub::protocol::activities::{ private_message::create_or_update::CreateOrUpdatePrivateMessage, CreateOrUpdateType, }; diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index d53801aa..335e1713 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -1,11 +1,9 @@ -use activitystreams::{link::Mention, public, unparsed::Unparsed}; -use serde::{Deserialize, Serialize}; -use url::Url; +use activitystreams::public; use lemmy_api_common::{blocking, check_post_deleted_or_removed}; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, + traits::{ActivityHandler, ActorType, ApubObject}, verify::verify_domains_match, }; use lemmy_db_schema::{ @@ -19,37 +17,18 @@ use crate::{ activities::{ check_community_deleted_or_removed, comment::{collect_non_local_mentions, get_notif_recipients}, - community::{ - announce::{AnnouncableActivities, GetCommunity}, - send_to_community, - }, + community::{announce::GetCommunity, send_to_community}, generate_activity_id, verify_activity, verify_is_public, verify_person_in_community, - CreateOrUpdateType, }, + activity_lists::AnnouncableActivities, fetcher::object_id::ObjectId, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson}, - protocol::objects::note::Note, + protocol::activities::{create_or_update::comment::CreateOrUpdateComment, CreateOrUpdateType}, }; -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct CreateOrUpdateComment { - actor: ObjectId, - to: Vec, - object: Note, - cc: Vec, - #[serde(default)] - tag: Vec, - #[serde(rename = "type")] - kind: CreateOrUpdateType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} - impl CreateOrUpdateComment { pub async fn send( comment: &ApubComment, diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index b65fa48e..b18e50ad 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -1,10 +1,6 @@ use crate::{ activities::{ - community::{ - announce::{AnnouncableActivities, GetCommunity}, - get_community_from_moderators_url, - send_to_community, - }, + community::{announce::GetCommunity, get_community_from_moderators_url, send_to_community}, generate_activity_id, verify_activity, verify_add_remove_moderator_target, @@ -12,15 +8,17 @@ use crate::{ verify_mod_action, verify_person_in_community, }, + activity_lists::AnnouncableActivities, fetcher::object_id::ObjectId, generate_moderators_url, objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::community::add_mod::AddMod, }; -use activitystreams::{activity::kind::AddType, public, unparsed::Unparsed}; +use activitystreams::{activity::kind::AddType, public}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, + traits::{ActivityHandler, ActorType}, }; use lemmy_db_schema::{ source::community::{CommunityModerator, CommunityModeratorForm}, @@ -28,23 +26,6 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct AddMod { - actor: ObjectId, - to: Vec, - object: ObjectId, - target: Url, - cc: Vec, - #[serde(rename = "type")] - kind: AddType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} impl AddMod { pub async fn send( diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 007fcbb3..62e7c56f 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -1,55 +1,27 @@ use crate::{ activities::{ - comment::create_or_update::CreateOrUpdateComment, - community::{ - add_mod::AddMod, - block_user::BlockUserFromCommunity, - list_community_follower_inboxes, - remove_mod::RemoveMod, - undo_block_user::UndoBlockUserFromCommunity, - update::UpdateCommunity, - }, - deletion::{delete::Delete, undo_delete::UndoDelete}, + community::list_community_follower_inboxes, generate_activity_id, - post::create_or_update::CreateOrUpdatePost, send_lemmy_activity, verify_activity, verify_is_public, - voting::{undo_vote::UndoVote, vote::Vote}, }, + activity_lists::AnnouncableActivities, fetcher::object_id::ObjectId, http::is_activity_already_known, insert_activity, objects::community::ApubCommunity, + protocol::activities::community::announce::AnnounceActivity, }; -use activitystreams::{activity::kind::AnnounceType, public, unparsed::Unparsed}; +use activitystreams::{activity::kind::AnnounceType, public}; use lemmy_apub_lib::{ data::Data, traits::{ActivityFields, ActivityHandler, ActorType}, - verify::verify_urls_match, }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] -#[serde(untagged)] -#[activity_handler(LemmyContext)] -pub enum AnnouncableActivities { - CreateOrUpdateComment(CreateOrUpdateComment), - CreateOrUpdatePost(Box), - Vote(Vote), - UndoVote(UndoVote), - Delete(Delete), - UndoDelete(UndoDelete), - UpdateCommunity(Box), - BlockUserFromCommunity(BlockUserFromCommunity), - UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), - AddMod(AddMod), - RemoveMod(RemoveMod), -} - #[async_trait::async_trait(?Send)] pub(crate) trait GetCommunity { async fn get_community( @@ -59,46 +31,6 @@ pub(crate) trait GetCommunity { ) -> Result; } -#[async_trait::async_trait(?Send)] -impl GetCommunity for AnnouncableActivities { - async fn get_community( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result { - use AnnouncableActivities::*; - let community = match self { - CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?, - CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?, - Vote(a) => a.get_community(context, request_counter).await?, - UndoVote(a) => a.get_community(context, request_counter).await?, - Delete(a) => a.get_community(context, request_counter).await?, - UndoDelete(a) => a.get_community(context, request_counter).await?, - UpdateCommunity(a) => a.get_community(context, request_counter).await?, - BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, - UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, - AddMod(a) => a.get_community(context, request_counter).await?, - RemoveMod(a) => a.get_community(context, request_counter).await?, - }; - verify_urls_match(self.actor(), &community.actor_id())?; - Ok(community) - } -} - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct AnnounceActivity { - actor: ObjectId, - to: Vec, - object: AnnouncableActivities, - cc: Vec, - #[serde(rename = "type")] - kind: AnnounceType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} - impl AnnounceActivity { pub async fn send( object: AnnouncableActivities, diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs index 0a716228..dfe6c4c9 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/community/block_user.rs @@ -1,23 +1,22 @@ use crate::{ activities::{ - community::{ - announce::{AnnouncableActivities, GetCommunity}, - send_to_community, - }, + community::{announce::GetCommunity, send_to_community}, generate_activity_id, verify_activity, verify_is_public, verify_mod_action, verify_person_in_community, }, + activity_lists::AnnouncableActivities, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::community::block_user::BlockUserFromCommunity, }; -use activitystreams::{activity::kind::BlockType, public, unparsed::Unparsed}; +use activitystreams::{activity::kind::BlockType, public}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, + traits::{ActivityHandler, ActorType}, }; use lemmy_db_schema::{ source::community::{ @@ -30,23 +29,6 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct BlockUserFromCommunity { - actor: ObjectId, - to: Vec, - pub(in crate::activities::community) object: ObjectId, - cc: Vec, - target: ObjectId, - #[serde(rename = "type")] - kind: BlockType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} impl BlockUserFromCommunity { pub(in crate::activities::community) fn new( diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 1f51c033..ebc6e7f9 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -1,23 +1,25 @@ +use itertools::Itertools; +use url::Url; + +use lemmy_apub_lib::traits::ActorType; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; + use crate::{ - activities::{ - community::announce::{AnnouncableActivities, AnnounceActivity}, - send_lemmy_activity, - }, + activities::send_lemmy_activity, + activity_lists::AnnouncableActivities, check_is_apub_id_valid, fetcher::object_id::ObjectId, insert_activity, objects::community::ApubCommunity, + protocol::activities::community::announce::AnnounceActivity, }; -use itertools::Itertools; -use lemmy_apub_lib::traits::ActorType; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use url::Url; pub mod add_mod; pub mod announce; pub mod block_user; pub mod remove_mod; +pub mod report; pub mod undo_block_user; pub mod update; diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index f0c0b90e..02ff3c06 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -1,10 +1,6 @@ use crate::{ activities::{ - community::{ - announce::{AnnouncableActivities, GetCommunity}, - get_community_from_moderators_url, - send_to_community, - }, + community::{announce::GetCommunity, get_community_from_moderators_url, send_to_community}, generate_activity_id, verify_activity, verify_add_remove_moderator_target, @@ -12,15 +8,17 @@ use crate::{ verify_mod_action, verify_person_in_community, }, + activity_lists::AnnouncableActivities, fetcher::object_id::ObjectId, generate_moderators_url, objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::community::remove_mod::RemoveMod, }; -use activitystreams::{activity::kind::RemoveType, public, unparsed::Unparsed}; +use activitystreams::{activity::kind::RemoveType, public}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, + traits::{ActivityHandler, ActorType}, }; use lemmy_db_schema::{ source::community::{CommunityModerator, CommunityModeratorForm}, @@ -28,23 +26,6 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct RemoveMod { - actor: ObjectId, - to: Vec, - pub(in crate::activities) object: ObjectId, - cc: Vec, - #[serde(rename = "type")] - kind: RemoveType, - pub(in crate::activities) target: Url, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} impl RemoveMod { pub async fn send( diff --git a/crates/apub/src/activities/report.rs b/crates/apub/src/activities/community/report.rs similarity index 88% rename from crates/apub/src/activities/report.rs rename to crates/apub/src/activities/community/report.rs index 9256920a..1e7cc5fa 100644 --- a/crates/apub/src/activities/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -1,19 +1,9 @@ -use crate::{ - activities::{ - generate_activity_id, - send_lemmy_activity, - verify_activity, - verify_person_in_community, - }, - fetcher::object_id::ObjectId, - objects::{community::ApubCommunity, person::ApubPerson}, - PostOrComment, -}; -use activitystreams::{activity::kind::FlagType, unparsed::Unparsed}; +use activitystreams::activity::kind::FlagType; + use lemmy_api_common::{blocking, comment::CommentReportResponse, post::PostReportResponse}; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, + traits::{ActivityHandler, ActorType}, }; use lemmy_db_schema::{ source::{ @@ -25,22 +15,19 @@ use lemmy_db_schema::{ use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView}; use lemmy_utils::LemmyError; use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation}; -use serde::{Deserialize, Serialize}; -use url::Url; -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct Report { - actor: ObjectId, - to: [ObjectId; 1], - object: ObjectId, - summary: String, - #[serde(rename = "type")] - kind: FlagType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} +use crate::{ + activities::{ + generate_activity_id, + send_lemmy_activity, + verify_activity, + verify_person_in_community, + }, + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::community::report::Report, + PostOrComment, +}; impl Report { pub async fn send( diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs index ad220c96..2bda9444 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/community/undo_block_user.rs @@ -1,24 +1,25 @@ use crate::{ activities::{ - community::{ - announce::{AnnouncableActivities, GetCommunity}, - block_user::BlockUserFromCommunity, - send_to_community, - }, + community::{announce::GetCommunity, send_to_community}, generate_activity_id, verify_activity, verify_is_public, verify_mod_action, verify_person_in_community, }, + activity_lists::AnnouncableActivities, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::community::{ + block_user::BlockUserFromCommunity, + undo_block_user::UndoBlockUserFromCommunity, + }, }; -use activitystreams::{activity::kind::UndoType, public, unparsed::Unparsed}; +use activitystreams::{activity::kind::UndoType, public}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, + traits::{ActivityHandler, ActorType}, }; use lemmy_db_schema::{ source::community::{CommunityPersonBan, CommunityPersonBanForm}, @@ -26,22 +27,6 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct UndoBlockUserFromCommunity { - actor: ObjectId, - to: Vec, - object: BlockUserFromCommunity, - cc: Vec, - #[serde(rename = "type")] - kind: UndoType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} impl UndoBlockUserFromCommunity { pub async fn send( diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index df34ca28..28de0db0 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -1,11 +1,22 @@ -use activitystreams::{activity::kind::UpdateType, public, unparsed::Unparsed}; -use serde::{Deserialize, Serialize}; -use url::Url; - +use crate::{ + activities::{ + community::{announce::GetCommunity, send_to_community}, + generate_activity_id, + verify_activity, + verify_is_public, + verify_mod_action, + verify_person_in_community, + }, + activity_lists::AnnouncableActivities, + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson}, + protocol::{activities::community::update::UpdateCommunity, objects::group::Group}, +}; +use activitystreams::{activity::kind::UpdateType, public}; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, + traits::{ActivityHandler, ActorType, ApubObject}, }; use lemmy_db_schema::{ source::community::{Community, CommunityForm}, @@ -14,40 +25,6 @@ use lemmy_db_schema::{ use lemmy_utils::LemmyError; use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; -use crate::{ - activities::{ - community::{ - announce::{AnnouncableActivities, GetCommunity}, - send_to_community, - }, - generate_activity_id, - verify_activity, - verify_is_public, - verify_mod_action, - verify_person_in_community, - }, - fetcher::object_id::ObjectId, - objects::{community::ApubCommunity, person::ApubPerson}, - protocol::objects::group::Group, -}; - -/// This activity is received from a remote community mod, and updates the description or other -/// fields of a local community. -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct UpdateCommunity { - actor: ObjectId, - to: Vec, - // TODO: would be nice to use a separate struct here, which only contains the fields updated here - object: Group, - cc: Vec, - #[serde(rename = "type")] - kind: UpdateType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} - impl UpdateCommunity { pub async fn send( community: &ApubCommunity, diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 0db590b9..672fca52 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -1,28 +1,11 @@ -use crate::{ - activities::{ - community::{ - announce::{AnnouncableActivities, GetCommunity}, - send_to_community, - }, - deletion::{ - receive_delete_action, - verify_delete_activity, - DeletableObjects, - WebsocketMessages, - }, - generate_activity_id, - verify_activity, - verify_is_public, - }, - fetcher::object_id::ObjectId, - objects::{community::ApubCommunity, person::ApubPerson}, -}; -use activitystreams::{activity::kind::DeleteType, public, unparsed::Unparsed}; +use activitystreams::{activity::kind::DeleteType, public}; use anyhow::anyhow; +use url::Url; + use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, + traits::{ActivityHandler, ActorType}, }; use lemmy_db_schema::{ source::{ @@ -46,35 +29,25 @@ use lemmy_websocket::{ LemmyContext, UserOperationCrud, }; -use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; -use url::Url; -/// This is very confusing, because there are four distinct cases to handle: -/// - user deletes their post -/// - user deletes their comment -/// - remote community mod deletes local community -/// - remote community deletes itself (triggered by a mod) -/// -/// TODO: we should probably change how community deletions work to simplify this. Probably by -/// wrapping it in an announce just like other activities, instead of having the community send it. -#[skip_serializing_none] -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct Delete { - actor: ObjectId, - to: Vec, - pub(in crate::activities::deletion) object: Url, - pub(in crate::activities::deletion) cc: Vec, - #[serde(rename = "type")] - kind: DeleteType, - /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user - /// deleting their own content. - pub(in crate::activities::deletion) summary: Option, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} +use crate::{ + activities::{ + community::{announce::GetCommunity, send_to_community}, + deletion::{ + receive_delete_action, + verify_delete_activity, + DeletableObjects, + WebsocketMessages, + }, + generate_activity_id, + verify_activity, + verify_is_public, + }, + activity_lists::AnnouncableActivities, + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::deletion::delete::Delete, +}; #[async_trait::async_trait(?Send)] impl ActivityHandler for Delete { diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index 1af02245..b9c11291 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -1,12 +1,5 @@ -use crate::{ - activities::{ - deletion::{delete::Delete, undo_delete::UndoDelete}, - verify_mod_action, - verify_person_in_community, - }, - fetcher::object_id::ObjectId, - objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, -}; +use url::Url; + use lemmy_api_common::blocking; use lemmy_apub_lib::{ traits::{ActivityFields, ActorType, ApubObject}, @@ -19,7 +12,13 @@ use lemmy_websocket::{ LemmyContext, UserOperationCrud, }; -use url::Url; + +use crate::{ + activities::{verify_mod_action, verify_person_in_community}, + fetcher::object_id::ObjectId, + objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, + protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete}, +}; pub mod delete; pub mod undo_delete; diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index 5cc4dffc..c1689119 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -1,11 +1,24 @@ +use activitystreams::{activity::kind::UndoType, public}; +use anyhow::anyhow; +use url::Url; + +use lemmy_api_common::blocking; +use lemmy_apub_lib::{ + data::Data, + traits::{ActivityHandler, ActorType}, +}; +use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post}; +use lemmy_utils::LemmyError; +use lemmy_websocket::{ + send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message}, + LemmyContext, + UserOperationCrud, +}; + use crate::{ activities::{ - community::{ - announce::{AnnouncableActivities, GetCommunity}, - send_to_community, - }, + community::{announce::GetCommunity, send_to_community}, deletion::{ - delete::Delete, receive_delete_action, verify_delete_activity, DeletableObjects, @@ -15,39 +28,11 @@ use crate::{ verify_activity, verify_is_public, }, + activity_lists::AnnouncableActivities, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete}, }; -use activitystreams::{activity::kind::UndoType, public, unparsed::Unparsed}; -use anyhow::anyhow; -use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, -}; -use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post}; -use lemmy_utils::LemmyError; -use lemmy_websocket::{ - send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message}, - LemmyContext, - UserOperationCrud, -}; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct UndoDelete { - actor: ObjectId, - to: Vec, - object: Delete, - cc: Vec, - #[serde(rename = "type")] - kind: UndoType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} #[async_trait::async_trait(?Send)] impl ActivityHandler for UndoDelete { diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index 28f6c108..984d622a 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -1,14 +1,9 @@ use crate::{ - activities::{ - following::follow::FollowCommunity, - generate_activity_id, - send_lemmy_activity, - verify_activity, - }, + activities::{generate_activity_id, send_lemmy_activity, verify_activity}, fetcher::object_id::ObjectId, - objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity}, }; -use activitystreams::{activity::kind::AcceptType, unparsed::Unparsed}; +use activitystreams::activity::kind::AcceptType; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -18,21 +13,6 @@ use lemmy_apub_lib::{ use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct AcceptFollowCommunity { - actor: ObjectId, - to: [ObjectId; 1], - object: FollowCommunity, - #[serde(rename = "type")] - kind: AcceptType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} impl AcceptFollowCommunity { pub async fn send( diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 57a112e0..e048907f 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -1,6 +1,5 @@ use crate::{ activities::{ - following::accept::AcceptFollowCommunity, generate_activity_id, send_lemmy_activity, verify_activity, @@ -9,12 +8,13 @@ use crate::{ }, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity}, }; -use activitystreams::{activity::kind::FollowType, unparsed::Unparsed}; +use activitystreams::activity::kind::FollowType; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, + traits::{ActivityHandler, ActorType}, verify::verify_urls_match, }; use lemmy_db_schema::{ @@ -23,21 +23,6 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct FollowCommunity { - pub(in crate::activities::following) actor: ObjectId, - pub(in crate::activities::following) to: [ObjectId; 1], - pub(in crate::activities::following) object: ObjectId, - #[serde(rename = "type")] - kind: FollowType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} impl FollowCommunity { pub(in crate::activities::following) fn new( diff --git a/crates/apub/src/activities/following/mod.rs b/crates/apub/src/activities/following/mod.rs index 050c3691..60bdd5f7 100644 --- a/crates/apub/src/activities/following/mod.rs +++ b/crates/apub/src/activities/following/mod.rs @@ -1,3 +1,3 @@ pub mod accept; pub mod follow; -pub mod undo; +pub mod undo_follow; diff --git a/crates/apub/src/activities/following/undo.rs b/crates/apub/src/activities/following/undo_follow.rs similarity index 79% rename from crates/apub/src/activities/following/undo.rs rename to crates/apub/src/activities/following/undo_follow.rs index a9326a26..c3fd78b5 100644 --- a/crates/apub/src/activities/following/undo.rs +++ b/crates/apub/src/activities/following/undo_follow.rs @@ -1,15 +1,10 @@ use crate::{ - activities::{ - following::follow::FollowCommunity, - generate_activity_id, - send_lemmy_activity, - verify_activity, - verify_person, - }, + activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person}, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity}, }; -use activitystreams::{activity::kind::UndoType, unparsed::Unparsed}; +use activitystreams::activity::kind::UndoType; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -22,21 +17,6 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct UndoFollowCommunity { - actor: ObjectId, - to: [ObjectId; 1], - object: FollowCommunity, - #[serde(rename = "type")] - kind: UndoType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} impl UndoFollowCommunity { pub async fn send( diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 9509babb..bc6cfb51 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -22,8 +22,7 @@ use lemmy_db_views_actor::{ use lemmy_utils::{settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; use log::info; -use serde::{Deserialize, Serialize}; -use strum_macros::ToString; +use serde::Serialize; use url::{ParseError, Url}; use uuid::Uuid; @@ -33,15 +32,8 @@ pub mod deletion; pub mod following; pub mod post; pub mod private_message; -pub mod report; pub mod voting; -#[derive(Clone, Debug, ToString, Deserialize, Serialize)] -pub enum CreateOrUpdateType { - Create, - Update, -} - /// Checks that the specified Url actually identifies a Person (by fetching it), and that the person /// doesn't have a site ban. async fn verify_person( diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index ee1bf19c..41590493 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -1,7 +1,5 @@ -use activitystreams::{public, unparsed::Unparsed}; +use activitystreams::public; use anyhow::anyhow; -use serde::{Deserialize, Serialize}; -use url::Url; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -16,36 +14,19 @@ use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCru use crate::{ activities::{ check_community_deleted_or_removed, - community::{ - announce::{AnnouncableActivities, GetCommunity}, - send_to_community, - }, + community::{announce::GetCommunity, send_to_community}, generate_activity_id, verify_activity, verify_is_public, verify_mod_action, verify_person_in_community, - CreateOrUpdateType, }, + activity_lists::AnnouncableActivities, fetcher::object_id::ObjectId, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, - protocol::objects::page::Page, + protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType}, }; -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct CreateOrUpdatePost { - actor: ObjectId, - to: Vec, - object: Page, - cc: Vec, - #[serde(rename = "type")] - kind: CreateOrUpdateType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} - impl CreateOrUpdatePost { pub(crate) async fn new( post: &ApubPost, diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs index 0067607e..cfd7c8bc 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -1,40 +1,21 @@ use crate::{ - activities::{ - generate_activity_id, - send_lemmy_activity, - verify_activity, - verify_person, - CreateOrUpdateType, - }, + activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person}, fetcher::object_id::ObjectId, objects::{person::ApubPerson, private_message::ApubPrivateMessage}, - protocol::objects::chat_message::ChatMessage, + protocol::activities::{ + private_message::create_or_update::CreateOrUpdatePrivateMessage, + CreateOrUpdateType, + }, }; -use activitystreams::unparsed::Unparsed; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, + traits::{ActivityHandler, ActorType, ApubObject}, verify::verify_domains_match, }; use lemmy_db_schema::{source::person::Person, traits::Crud}; use lemmy_utils::LemmyError; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct CreateOrUpdatePrivateMessage { - id: Url, - actor: ObjectId, - to: [ObjectId; 1], - object: ChatMessage, - #[serde(rename = "type")] - kind: CreateOrUpdateType, - #[serde(flatten)] - pub unparsed: Unparsed, -} impl CreateOrUpdatePrivateMessage { pub async fn send( diff --git a/crates/apub/src/activities/private_message/delete.rs b/crates/apub/src/activities/private_message/delete.rs index 0928cba2..da3b6472 100644 --- a/crates/apub/src/activities/private_message/delete.rs +++ b/crates/apub/src/activities/private_message/delete.rs @@ -2,12 +2,13 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person}, fetcher::object_id::ObjectId, objects::{person::ApubPerson, private_message::ApubPrivateMessage}, + protocol::activities::private_message::delete::DeletePrivateMessage, }; -use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed}; +use activitystreams::activity::kind::DeleteType; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, + traits::{ActivityHandler, ActorType}, verify::verify_domains_match, }; use lemmy_db_schema::{ @@ -16,21 +17,6 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct DeletePrivateMessage { - actor: ObjectId, - to: [ObjectId; 1], - pub(in crate::activities::private_message) object: ObjectId, - #[serde(rename = "type")] - kind: DeleteType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} impl DeletePrivateMessage { pub(in crate::activities::private_message) fn new( diff --git a/crates/apub/src/activities/private_message/undo_delete.rs b/crates/apub/src/activities/private_message/undo_delete.rs index 4275ea9c..bba9e0f2 100644 --- a/crates/apub/src/activities/private_message/undo_delete.rs +++ b/crates/apub/src/activities/private_message/undo_delete.rs @@ -1,15 +1,13 @@ use crate::{ - activities::{ - generate_activity_id, - private_message::delete::DeletePrivateMessage, - send_lemmy_activity, - verify_activity, - verify_person, - }, + activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person}, fetcher::object_id::ObjectId, objects::{person::ApubPerson, private_message::ApubPrivateMessage}, + protocol::activities::private_message::{ + delete::DeletePrivateMessage, + undo_delete::UndoDeletePrivateMessage, + }, }; -use activitystreams::{activity::kind::UndoType, unparsed::Unparsed}; +use activitystreams::activity::kind::UndoType; use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -22,21 +20,6 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; -use serde::{Deserialize, Serialize}; -use url::Url; - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct UndoDeletePrivateMessage { - actor: ObjectId, - to: [ObjectId; 1], - object: DeletePrivateMessage, - #[serde(rename = "type")] - kind: UndoType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} impl UndoDeletePrivateMessage { pub async fn send( diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs index 829553d3..0a2a8fd1 100644 --- a/crates/apub/src/activities/voting/mod.rs +++ b/crates/apub/src/activities/voting/mod.rs @@ -1,7 +1,3 @@ -use crate::{ - activities::voting::vote::VoteType, - objects::{comment::ApubComment, person::ApubPerson, post::ApubPost}, -}; use lemmy_api_common::blocking; use lemmy_db_schema::{ source::{ @@ -17,6 +13,11 @@ use lemmy_websocket::{ UserOperation, }; +use crate::{ + objects::{comment::ApubComment, person::ApubPerson, post::ApubPost}, + protocol::activities::voting::vote::VoteType, +}; + pub mod undo_vote; pub mod vote; diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index cc620661..e95d2517 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -1,24 +1,7 @@ -use crate::{ - activities::{ - community::{ - announce::{AnnouncableActivities, GetCommunity}, - send_to_community, - }, - generate_activity_id, - verify_activity, - verify_is_public, - verify_person_in_community, - voting::{ - undo_vote_comment, - undo_vote_post, - vote::{Vote, VoteType}, - }, - }, - fetcher::object_id::ObjectId, - objects::{community::ApubCommunity, person::ApubPerson}, - PostOrComment, -}; -use activitystreams::{activity::kind::UndoType, public, unparsed::Unparsed}; +use std::ops::Deref; + +use activitystreams::{activity::kind::UndoType, public}; + use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -28,23 +11,25 @@ use lemmy_apub_lib::{ use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use std::ops::Deref; -use url::Url; -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct UndoVote { - actor: ObjectId, - to: Vec, - object: Vote, - cc: Vec, - #[serde(rename = "type")] - kind: UndoType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} +use crate::{ + activities::{ + community::{announce::GetCommunity, send_to_community}, + generate_activity_id, + verify_activity, + verify_is_public, + verify_person_in_community, + voting::{undo_vote_comment, undo_vote_post}, + }, + activity_lists::AnnouncableActivities, + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::voting::{ + undo_vote::UndoVote, + vote::{Vote, VoteType}, + }, + PostOrComment, +}; impl UndoVote { pub async fn send( diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 3efef7cf..01df4b93 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -1,25 +1,11 @@ -use crate::{ - activities::{ - community::{ - announce::{AnnouncableActivities, GetCommunity}, - send_to_community, - }, - generate_activity_id, - verify_activity, - verify_is_public, - verify_person_in_community, - voting::{vote_comment, vote_post}, - }, - fetcher::object_id::ObjectId, - objects::{community::ApubCommunity, person::ApubPerson}, - PostOrComment, -}; -use activitystreams::{public, unparsed::Unparsed}; -use anyhow::anyhow; +use std::ops::Deref; + +use activitystreams::public; + use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, - traits::{ActivityFields, ActivityHandler, ActorType}, + traits::{ActivityHandler, ActorType}, }; use lemmy_db_schema::{ newtypes::CommunityId, @@ -28,51 +14,22 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, ops::Deref}; -use strum_macros::ToString; -use url::Url; -#[derive(Clone, Debug, ToString, Deserialize, Serialize)] -pub enum VoteType { - Like, - Dislike, -} - -impl TryFrom for VoteType { - type Error = LemmyError; - - fn try_from(value: i16) -> Result { - match value { - 1 => Ok(VoteType::Like), - -1 => Ok(VoteType::Dislike), - _ => Err(anyhow!("invalid vote value").into()), - } - } -} - -impl From<&VoteType> for i16 { - fn from(value: &VoteType) -> i16 { - match value { - VoteType::Like => 1, - VoteType::Dislike => -1, - } - } -} - -#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] -#[serde(rename_all = "camelCase")] -pub struct Vote { - actor: ObjectId, - to: Vec, - pub(in crate::activities::voting) object: ObjectId, - cc: Vec, - #[serde(rename = "type")] - pub(in crate::activities::voting) kind: VoteType, - id: Url, - #[serde(flatten)] - unparsed: Unparsed, -} +use crate::{ + activities::{ + community::{announce::GetCommunity, send_to_community}, + generate_activity_id, + verify_activity, + verify_is_public, + verify_person_in_community, + voting::{vote_comment, vote_post}, + }, + activity_lists::AnnouncableActivities, + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::voting::vote::{Vote, VoteType}, + PostOrComment, +}; impl Vote { pub(in crate::activities::voting) fn new( diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs new file mode 100644 index 00000000..9fd1a9dc --- /dev/null +++ b/crates/apub/src/activity_lists.rs @@ -0,0 +1,113 @@ +use serde::{Deserialize, Serialize}; + +use lemmy_apub_lib::{ + traits::{ActivityFields, ActivityHandler, ActorType}, + verify::verify_urls_match, +}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; + +use crate::{ + activities::community::announce::GetCommunity, + objects::community::ApubCommunity, + protocol::activities::{ + community::{ + add_mod::AddMod, + announce::AnnounceActivity, + block_user::BlockUserFromCommunity, + remove_mod::RemoveMod, + report::Report, + undo_block_user::UndoBlockUserFromCommunity, + update::UpdateCommunity, + }, + create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost}, + deletion::{delete::Delete, undo_delete::UndoDelete}, + following::{ + accept::AcceptFollowCommunity, + follow::FollowCommunity, + undo_follow::UndoFollowCommunity, + }, + private_message::{ + create_or_update::CreateOrUpdatePrivateMessage, + delete::DeletePrivateMessage, + undo_delete::UndoDeletePrivateMessage, + }, + voting::{undo_vote::UndoVote, vote::Vote}, + }, +}; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] +#[serde(untagged)] +#[activity_handler(LemmyContext)] +pub enum SharedInboxActivities { + GroupInboxActivities(GroupInboxActivities), + // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably + // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt. + PersonInboxActivities(PersonInboxActivities), +} + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] +#[serde(untagged)] +#[activity_handler(LemmyContext)] +pub enum GroupInboxActivities { + FollowCommunity(FollowCommunity), + UndoFollowCommunity(UndoFollowCommunity), + AnnouncableActivities(AnnouncableActivities), + Report(Report), +} + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] +#[serde(untagged)] +#[activity_handler(LemmyContext)] +pub enum PersonInboxActivities { + AcceptFollowCommunity(AcceptFollowCommunity), + /// Some activities can also be sent from user to user, eg a comment with mentions + AnnouncableActivities(AnnouncableActivities), + CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage), + DeletePrivateMessage(DeletePrivateMessage), + UndoDeletePrivateMessage(UndoDeletePrivateMessage), + AnnounceActivity(Box), +} + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] +#[serde(untagged)] +#[activity_handler(LemmyContext)] +pub enum AnnouncableActivities { + CreateOrUpdateComment(CreateOrUpdateComment), + CreateOrUpdatePost(Box), + Vote(Vote), + UndoVote(UndoVote), + Delete(Delete), + UndoDelete(UndoDelete), + UpdateCommunity(Box), + BlockUserFromCommunity(BlockUserFromCommunity), + UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), + AddMod(AddMod), + RemoveMod(RemoveMod), +} + +#[async_trait::async_trait(?Send)] +impl GetCommunity for AnnouncableActivities { + async fn get_community( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + use AnnouncableActivities::*; + let community = match self { + CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?, + CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?, + Vote(a) => a.get_community(context, request_counter).await?, + UndoVote(a) => a.get_community(context, request_counter).await?, + Delete(a) => a.get_community(context, request_counter).await?, + UndoDelete(a) => a.get_community(context, request_counter).await?, + UpdateCommunity(a) => a.get_community(context, request_counter).await?, + BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, + UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, + AddMod(a) => a.get_community(context, request_counter).await?, + RemoveMod(a) => a.get_community(context, request_counter).await?, + }; + verify_urls_match(self.actor(), &community.actor_id())?; + Ok(community) + } +} diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index cf5ad8b2..451c3fa9 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -1,12 +1,7 @@ -use crate::{ - activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType}, - collections::CommunityContext, - generate_outbox_url, - objects::{person::ApubPerson, post::ApubPost}, - protocol::collections::group_outbox::GroupOutbox, -}; use activitystreams::collection::kind::OrderedCollectionType; use chrono::NaiveDateTime; +use url::Url; + use lemmy_api_common::blocking; use lemmy_apub_lib::{ data::Data, @@ -18,7 +13,16 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_utils::LemmyError; -use url::Url; + +use crate::{ + collections::CommunityContext, + generate_outbox_url, + objects::{person::ApubPerson, post::ApubPost}, + protocol::{ + activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType}, + collections::group_outbox::GroupOutbox, + }, +}; #[derive(Clone, Debug)] pub(crate) struct ApubCommunityOutbox(Vec); diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index 0a7cb664..ffc76d3f 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -1,23 +1,6 @@ -use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; -use log::info; -use serde::{Deserialize, Serialize}; - -use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - traits::{ActivityFields, ActivityHandler, ActorType, ApubObject}, - verify::verify_domains_match, -}; -use lemmy_db_schema::source::community::Community; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; - use crate::{ - activities::{ - community::announce::{AnnouncableActivities, AnnounceActivity, GetCommunity}, - following::{follow::FollowCommunity, undo::UndoFollowCommunity}, - report::Report, - verify_person_in_community, - }, + activities::{community::announce::GetCommunity, verify_person_in_community}, + activity_lists::GroupInboxActivities, collections::{ community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, @@ -33,8 +16,22 @@ use crate::{ receive_activity, }, objects::community::ApubCommunity, - protocol::collections::group_followers::CommunityFollowers, + protocol::{ + activities::community::announce::AnnounceActivity, + collections::group_followers::CommunityFollowers, + }, }; +use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; +use lemmy_api_common::blocking; +use lemmy_apub_lib::{ + traits::{ActivityFields, ActorType, ApubObject}, + verify::verify_domains_match, +}; +use lemmy_db_schema::source::community::Community; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use log::info; +use serde::Deserialize; #[derive(Deserialize)] pub(crate) struct CommunityQuery { @@ -61,16 +58,6 @@ pub(crate) async fn get_apub_community_http( } } -#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] -#[serde(untagged)] -#[activity_handler(LemmyContext)] -pub enum GroupInboxActivities { - FollowCommunity(FollowCommunity), - UndoFollowCommunity(UndoFollowCommunity), - AnnouncableActivities(AnnouncableActivities), - Report(Report), -} - /// Handler for all incoming receive to community inboxes. pub async fn community_inbox( request: HttpRequest, diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index 48956cdf..9c61c274 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -1,11 +1,9 @@ use crate::{ + activity_lists::SharedInboxActivities, check_is_apub_id_valid, context::WithContext, fetcher::get_or_fetch_and_upsert_actor, - http::{ - community::{receive_group_inbox, GroupInboxActivities}, - person::{receive_person_inbox, PersonInboxActivities}, - }, + http::{community::receive_group_inbox, person::receive_person_inbox}, insert_activity, }; use actix_web::{ @@ -39,16 +37,6 @@ mod person; mod post; pub mod routes; -#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] -#[serde(untagged)] -#[activity_handler(LemmyContext)] -pub enum SharedInboxActivities { - GroupInboxActivities(GroupInboxActivities), - // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably - // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt. - PersonInboxActivities(PersonInboxActivities), -} - pub async fn shared_inbox( request: HttpRequest, payload: Payload, diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index 77bfd694..3f18bcc5 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -1,23 +1,5 @@ -use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; -use log::info; -use serde::{Deserialize, Serialize}; - -use lemmy_api_common::blocking; -use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject}; -use lemmy_db_schema::source::person::Person; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; - use crate::{ - activities::{ - community::announce::{AnnouncableActivities, AnnounceActivity}, - following::accept::AcceptFollowCommunity, - private_message::{ - create_or_update::CreateOrUpdatePrivateMessage, - delete::DeletePrivateMessage, - undo_delete::UndoDeletePrivateMessage, - }, - }, + activity_lists::PersonInboxActivities, context::WithContext, http::{ create_apub_response, @@ -28,6 +10,14 @@ use crate::{ objects::person::ApubPerson, protocol::collections::person_outbox::UserOutbox, }; +use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; +use lemmy_api_common::blocking; +use lemmy_apub_lib::traits::ApubObject; +use lemmy_db_schema::source::person::Person; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use log::info; +use serde::Deserialize; #[derive(Deserialize)] pub struct PersonQuery { @@ -56,19 +46,6 @@ pub(crate) async fn get_apub_person_http( } } -#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] -#[serde(untagged)] -#[activity_handler(LemmyContext)] -pub enum PersonInboxActivities { - AcceptFollowCommunity(AcceptFollowCommunity), - /// Some activities can also be sent from user to user, eg a comment with mentions - AnnouncableActivities(AnnouncableActivities), - CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage), - DeletePrivateMessage(DeletePrivateMessage), - UndoDeletePrivateMessage(UndoDeletePrivateMessage), - AnnounceActivity(Box), -} - pub async fn person_inbox( request: HttpRequest, payload: Payload, diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index d188274b..f38a9f86 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -1,11 +1,12 @@ pub mod activities; +pub(crate) mod activity_lists; pub(crate) mod collections; mod context; pub mod fetcher; pub mod http; pub mod migrations; pub mod objects; -pub(crate) mod protocol; +pub mod protocol; #[macro_use] extern crate lazy_static; diff --git a/crates/apub/src/protocol/activities/community/add_mod.rs b/crates/apub/src/protocol/activities/community/add_mod.rs new file mode 100644 index 00000000..74ec4645 --- /dev/null +++ b/crates/apub/src/protocol/activities/community/add_mod.rs @@ -0,0 +1,20 @@ +use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson}; +use activitystreams::{activity::kind::AddType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct AddMod { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: ObjectId, + pub(crate) target: Url, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: AddType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/community/announce.rs b/crates/apub/src/protocol/activities/community/announce.rs new file mode 100644 index 00000000..2f4e9bd2 --- /dev/null +++ b/crates/apub/src/protocol/activities/community/announce.rs @@ -0,0 +1,23 @@ +use crate::{ + activity_lists::AnnouncableActivities, + fetcher::object_id::ObjectId, + objects::community::ApubCommunity, +}; +use activitystreams::{activity::kind::AnnounceType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct AnnounceActivity { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: AnnouncableActivities, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: AnnounceType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/community/block_user.rs b/crates/apub/src/protocol/activities/community/block_user.rs new file mode 100644 index 00000000..4ede06ae --- /dev/null +++ b/crates/apub/src/protocol/activities/community/block_user.rs @@ -0,0 +1,23 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson}, +}; +use activitystreams::{activity::kind::BlockType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct BlockUserFromCommunity { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: ObjectId, + pub(crate) cc: Vec, + pub(crate) target: ObjectId, + #[serde(rename = "type")] + pub(crate) kind: BlockType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/community/mod.rs b/crates/apub/src/protocol/activities/community/mod.rs new file mode 100644 index 00000000..62f8329b --- /dev/null +++ b/crates/apub/src/protocol/activities/community/mod.rs @@ -0,0 +1,7 @@ +pub mod add_mod; +pub mod announce; +pub mod block_user; +pub mod remove_mod; +pub mod report; +pub mod undo_block_user; +pub mod update; diff --git a/crates/apub/src/protocol/activities/community/remove_mod.rs b/crates/apub/src/protocol/activities/community/remove_mod.rs new file mode 100644 index 00000000..db30ddbe --- /dev/null +++ b/crates/apub/src/protocol/activities/community/remove_mod.rs @@ -0,0 +1,20 @@ +use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson}; +use activitystreams::{activity::kind::RemoveType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct RemoveMod { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: ObjectId, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: RemoveType, + pub(crate) target: Url, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/community/report.rs b/crates/apub/src/protocol/activities/community/report.rs new file mode 100644 index 00000000..5efdd792 --- /dev/null +++ b/crates/apub/src/protocol/activities/community/report.rs @@ -0,0 +1,22 @@ +use crate::{ + fetcher::{object_id::ObjectId, post_or_comment::PostOrComment}, + objects::{community::ApubCommunity, person::ApubPerson}, +}; +use activitystreams::{activity::kind::FlagType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct Report { + pub(crate) actor: ObjectId, + pub(crate) to: [ObjectId; 1], + pub(crate) object: ObjectId, + pub(crate) summary: String, + #[serde(rename = "type")] + pub(crate) kind: FlagType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/community/undo_block_user.rs b/crates/apub/src/protocol/activities/community/undo_block_user.rs new file mode 100644 index 00000000..0e89f87e --- /dev/null +++ b/crates/apub/src/protocol/activities/community/undo_block_user.rs @@ -0,0 +1,23 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::person::ApubPerson, + protocol::activities::community::block_user::BlockUserFromCommunity, +}; +use activitystreams::{activity::kind::UndoType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct UndoBlockUserFromCommunity { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: BlockUserFromCommunity, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: UndoType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/community/update.rs b/crates/apub/src/protocol/activities/community/update.rs new file mode 100644 index 00000000..4ba1ed84 --- /dev/null +++ b/crates/apub/src/protocol/activities/community/update.rs @@ -0,0 +1,26 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::person::ApubPerson, + protocol::objects::group::Group, +}; +use activitystreams::{activity::kind::UpdateType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +/// This activity is received from a remote community mod, and updates the description or other +/// fields of a local community. +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct UpdateCommunity { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + // TODO: would be nice to use a separate struct here, which only contains the fields updated here + pub(crate) object: Group, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: UpdateType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/create_or_update/comment.rs b/crates/apub/src/protocol/activities/create_or_update/comment.rs new file mode 100644 index 00000000..ede7417b --- /dev/null +++ b/crates/apub/src/protocol/activities/create_or_update/comment.rs @@ -0,0 +1,25 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::person::ApubPerson, + protocol::{activities::CreateOrUpdateType, objects::note::Note}, +}; +use activitystreams::{link::Mention, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct CreateOrUpdateComment { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: Note, + pub(crate) cc: Vec, + #[serde(default)] + pub(crate) tag: Vec, + #[serde(rename = "type")] + pub(crate) kind: CreateOrUpdateType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/create_or_update/mod.rs b/crates/apub/src/protocol/activities/create_or_update/mod.rs new file mode 100644 index 00000000..38203b5e --- /dev/null +++ b/crates/apub/src/protocol/activities/create_or_update/mod.rs @@ -0,0 +1,2 @@ +pub mod comment; +pub mod post; diff --git a/crates/apub/src/protocol/activities/create_or_update/post.rs b/crates/apub/src/protocol/activities/create_or_update/post.rs new file mode 100644 index 00000000..03b283e3 --- /dev/null +++ b/crates/apub/src/protocol/activities/create_or_update/post.rs @@ -0,0 +1,23 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::person::ApubPerson, + protocol::{activities::CreateOrUpdateType, objects::page::Page}, +}; +use activitystreams::unparsed::Unparsed; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct CreateOrUpdatePost { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: Page, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: CreateOrUpdateType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/deletion/delete.rs b/crates/apub/src/protocol/activities/deletion/delete.rs new file mode 100644 index 00000000..f8e81b47 --- /dev/null +++ b/crates/apub/src/protocol/activities/deletion/delete.rs @@ -0,0 +1,24 @@ +use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson}; +use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; +use url::Url; + +#[skip_serializing_none] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct Delete { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: Url, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: DeleteType, + /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user + /// deleting their own content. + pub(crate) summary: Option, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/deletion/mod.rs b/crates/apub/src/protocol/activities/deletion/mod.rs new file mode 100644 index 00000000..b440edd6 --- /dev/null +++ b/crates/apub/src/protocol/activities/deletion/mod.rs @@ -0,0 +1,2 @@ +pub mod delete; +pub mod undo_delete; diff --git a/crates/apub/src/protocol/activities/deletion/undo_delete.rs b/crates/apub/src/protocol/activities/deletion/undo_delete.rs new file mode 100644 index 00000000..d962820b --- /dev/null +++ b/crates/apub/src/protocol/activities/deletion/undo_delete.rs @@ -0,0 +1,25 @@ +use activitystreams::{activity::kind::UndoType, unparsed::Unparsed}; +use serde::{Deserialize, Serialize}; +use url::Url; + +use lemmy_apub_lib::traits::ActivityFields; + +use crate::{ + fetcher::object_id::ObjectId, + objects::person::ApubPerson, + protocol::activities::deletion::delete::Delete, +}; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct UndoDelete { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: Delete, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: UndoType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/following/accept.rs b/crates/apub/src/protocol/activities/following/accept.rs new file mode 100644 index 00000000..502a908c --- /dev/null +++ b/crates/apub/src/protocol/activities/following/accept.rs @@ -0,0 +1,22 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::following::follow::FollowCommunity, +}; +use activitystreams::{activity::kind::AcceptType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct AcceptFollowCommunity { + pub(crate) actor: ObjectId, + pub(crate) to: [ObjectId; 1], + pub(crate) object: FollowCommunity, + #[serde(rename = "type")] + pub(crate) kind: AcceptType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/following/follow.rs b/crates/apub/src/protocol/activities/following/follow.rs new file mode 100644 index 00000000..9dfec216 --- /dev/null +++ b/crates/apub/src/protocol/activities/following/follow.rs @@ -0,0 +1,21 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson}, +}; +use activitystreams::{activity::kind::FollowType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct FollowCommunity { + pub(crate) actor: ObjectId, + pub(crate) to: [ObjectId; 1], + pub(crate) object: ObjectId, + #[serde(rename = "type")] + pub(crate) kind: FollowType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/following/mod.rs b/crates/apub/src/protocol/activities/following/mod.rs new file mode 100644 index 00000000..5dc6a3f8 --- /dev/null +++ b/crates/apub/src/protocol/activities/following/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod accept; +pub mod follow; +pub mod undo_follow; diff --git a/crates/apub/src/protocol/activities/following/undo_follow.rs b/crates/apub/src/protocol/activities/following/undo_follow.rs new file mode 100644 index 00000000..be6a7ab8 --- /dev/null +++ b/crates/apub/src/protocol/activities/following/undo_follow.rs @@ -0,0 +1,22 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::{community::ApubCommunity, person::ApubPerson}, + protocol::activities::following::follow::FollowCommunity, +}; +use activitystreams::{activity::kind::UndoType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct UndoFollowCommunity { + pub(crate) actor: ObjectId, + pub(crate) to: [ObjectId; 1], + pub(crate) object: FollowCommunity, + #[serde(rename = "type")] + pub(crate) kind: UndoType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/mod.rs b/crates/apub/src/protocol/activities/mod.rs new file mode 100644 index 00000000..23575c27 --- /dev/null +++ b/crates/apub/src/protocol/activities/mod.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; +use strum_macros::ToString; + +pub mod community; +pub mod create_or_update; +pub mod deletion; +pub mod following; +pub mod private_message; +pub mod voting; + +#[derive(Clone, Debug, ToString, Deserialize, Serialize)] +pub enum CreateOrUpdateType { + Create, + Update, +} diff --git a/crates/apub/src/protocol/activities/private_message/create_or_update.rs b/crates/apub/src/protocol/activities/private_message/create_or_update.rs new file mode 100644 index 00000000..7632ef9f --- /dev/null +++ b/crates/apub/src/protocol/activities/private_message/create_or_update.rs @@ -0,0 +1,22 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::person::ApubPerson, + protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage}, +}; +use activitystreams::unparsed::Unparsed; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct CreateOrUpdatePrivateMessage { + pub(crate) id: Url, + pub(crate) actor: ObjectId, + pub(crate) to: [ObjectId; 1], + pub(crate) object: ChatMessage, + #[serde(rename = "type")] + pub(crate) kind: CreateOrUpdateType, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/private_message/delete.rs b/crates/apub/src/protocol/activities/private_message/delete.rs new file mode 100644 index 00000000..499d7d1d --- /dev/null +++ b/crates/apub/src/protocol/activities/private_message/delete.rs @@ -0,0 +1,21 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::{person::ApubPerson, private_message::ApubPrivateMessage}, +}; +use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct DeletePrivateMessage { + pub(crate) actor: ObjectId, + pub(crate) to: [ObjectId; 1], + pub(crate) object: ObjectId, + #[serde(rename = "type")] + pub(crate) kind: DeleteType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/private_message/mod.rs b/crates/apub/src/protocol/activities/private_message/mod.rs new file mode 100644 index 00000000..4bcda7c7 --- /dev/null +++ b/crates/apub/src/protocol/activities/private_message/mod.rs @@ -0,0 +1,3 @@ +pub mod create_or_update; +pub mod delete; +pub mod undo_delete; diff --git a/crates/apub/src/protocol/activities/private_message/undo_delete.rs b/crates/apub/src/protocol/activities/private_message/undo_delete.rs new file mode 100644 index 00000000..699f6eec --- /dev/null +++ b/crates/apub/src/protocol/activities/private_message/undo_delete.rs @@ -0,0 +1,22 @@ +use crate::{ + fetcher::object_id::ObjectId, + objects::person::ApubPerson, + protocol::activities::private_message::delete::DeletePrivateMessage, +}; +use activitystreams::{activity::kind::UndoType, unparsed::Unparsed}; +use lemmy_apub_lib::traits::ActivityFields; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct UndoDeletePrivateMessage { + pub(crate) actor: ObjectId, + pub(crate) to: [ObjectId; 1], + pub(crate) object: DeletePrivateMessage, + #[serde(rename = "type")] + pub(crate) kind: UndoType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/voting/mod.rs b/crates/apub/src/protocol/activities/voting/mod.rs new file mode 100644 index 00000000..4583c231 --- /dev/null +++ b/crates/apub/src/protocol/activities/voting/mod.rs @@ -0,0 +1,2 @@ +pub mod undo_vote; +pub mod vote; diff --git a/crates/apub/src/protocol/activities/voting/undo_vote.rs b/crates/apub/src/protocol/activities/voting/undo_vote.rs new file mode 100644 index 00000000..0d3e6636 --- /dev/null +++ b/crates/apub/src/protocol/activities/voting/undo_vote.rs @@ -0,0 +1,25 @@ +use activitystreams::{activity::kind::UndoType, unparsed::Unparsed}; +use serde::{Deserialize, Serialize}; +use url::Url; + +use lemmy_apub_lib::traits::ActivityFields; + +use crate::{ + fetcher::object_id::ObjectId, + objects::person::ApubPerson, + protocol::activities::voting::vote::Vote, +}; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct UndoVote { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: Vote, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: UndoType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/apub/src/protocol/activities/voting/vote.rs b/crates/apub/src/protocol/activities/voting/vote.rs new file mode 100644 index 00000000..fdc87a3b --- /dev/null +++ b/crates/apub/src/protocol/activities/voting/vote.rs @@ -0,0 +1,53 @@ +use crate::{ + fetcher::{object_id::ObjectId, post_or_comment::PostOrComment}, + objects::person::ApubPerson, +}; +use activitystreams::unparsed::Unparsed; +use anyhow::anyhow; +use lemmy_apub_lib::traits::ActivityFields; +use lemmy_utils::LemmyError; +use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; +use strum_macros::ToString; +use url::Url; + +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] +#[serde(rename_all = "camelCase")] +pub struct Vote { + pub(crate) actor: ObjectId, + pub(crate) to: Vec, + pub(crate) object: ObjectId, + pub(crate) cc: Vec, + #[serde(rename = "type")] + pub(crate) kind: VoteType, + pub(crate) id: Url, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} + +#[derive(Clone, Debug, ToString, Deserialize, Serialize)] +pub enum VoteType { + Like, + Dislike, +} + +impl TryFrom for VoteType { + type Error = LemmyError; + + fn try_from(value: i16) -> Result { + match value { + 1 => Ok(VoteType::Like), + -1 => Ok(VoteType::Dislike), + _ => Err(anyhow!("invalid vote value").into()), + } + } +} + +impl From<&VoteType> for i16 { + fn from(value: &VoteType) -> i16 { + match value { + VoteType::Like => 1, + VoteType::Dislike => -1, + } + } +} diff --git a/crates/apub/src/protocol/collections/group_outbox.rs b/crates/apub/src/protocol/collections/group_outbox.rs index 26da4b6f..3b295003 100644 --- a/crates/apub/src/protocol/collections/group_outbox.rs +++ b/crates/apub/src/protocol/collections/group_outbox.rs @@ -1,4 +1,4 @@ -use crate::activities::post::create_or_update::CreateOrUpdatePost; +use crate::protocol::activities::create_or_update::post::CreateOrUpdatePost; use activitystreams::collection::kind::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 f4ad9e23..f587dba7 100644 --- a/crates/apub/src/protocol/mod.rs +++ b/crates/apub/src/protocol/mod.rs @@ -4,6 +4,7 @@ use url::Url; use lemmy_apub_lib::values::MediaTypeMarkdown; +pub mod activities; pub(crate) mod collections; pub(crate) mod objects; From aaaf03977970103363c80e5534c79363dcab0821 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Fri, 29 Oct 2021 16:54:19 +0200 Subject: [PATCH 13/17] Move apub test files into tree structure --- .../objects/chat_message.json} | 0 .../objects/group.json} | 0 .../objects/note.json} | 0 .../objects/page.json} | 0 .../objects/person.json} | 0 .../activities/create-note.json} | 0 .../objects/chat_message.json} | 0 .../objects/note.json} | 0 .../objects/person.json} | 0 .../activities/comment/create_or_update.rs | 8 +++---- crates/apub/src/objects/comment.rs | 23 +++++++++---------- crates/apub/src/objects/community.rs | 13 ++++------- crates/apub/src/objects/person.rs | 4 ++-- crates/apub/src/objects/post.rs | 6 ++--- crates/apub/src/objects/private_message.rs | 8 +++---- 15 files changed, 28 insertions(+), 34 deletions(-) rename crates/apub/assets/{lemmy-private-message.json => lemmy/objects/chat_message.json} (100%) rename crates/apub/assets/{lemmy-community.json => lemmy/objects/group.json} (100%) rename crates/apub/assets/{lemmy-comment.json => lemmy/objects/note.json} (100%) rename crates/apub/assets/{lemmy-post.json => lemmy/objects/page.json} (100%) rename crates/apub/assets/{lemmy-person.json => lemmy/objects/person.json} (100%) rename crates/apub/assets/{pleroma-create-comment.json => pleroma/activities/create-note.json} (100%) rename crates/apub/assets/{pleroma-private-message.json => pleroma/objects/chat_message.json} (100%) rename crates/apub/assets/{pleroma-comment.json => pleroma/objects/note.json} (100%) rename crates/apub/assets/{pleroma-person.json => pleroma/objects/person.json} (100%) diff --git a/crates/apub/assets/lemmy-private-message.json b/crates/apub/assets/lemmy/objects/chat_message.json similarity index 100% rename from crates/apub/assets/lemmy-private-message.json rename to crates/apub/assets/lemmy/objects/chat_message.json diff --git a/crates/apub/assets/lemmy-community.json b/crates/apub/assets/lemmy/objects/group.json similarity index 100% rename from crates/apub/assets/lemmy-community.json rename to crates/apub/assets/lemmy/objects/group.json diff --git a/crates/apub/assets/lemmy-comment.json b/crates/apub/assets/lemmy/objects/note.json similarity index 100% rename from crates/apub/assets/lemmy-comment.json rename to crates/apub/assets/lemmy/objects/note.json diff --git a/crates/apub/assets/lemmy-post.json b/crates/apub/assets/lemmy/objects/page.json similarity index 100% rename from crates/apub/assets/lemmy-post.json rename to crates/apub/assets/lemmy/objects/page.json diff --git a/crates/apub/assets/lemmy-person.json b/crates/apub/assets/lemmy/objects/person.json similarity index 100% rename from crates/apub/assets/lemmy-person.json rename to crates/apub/assets/lemmy/objects/person.json diff --git a/crates/apub/assets/pleroma-create-comment.json b/crates/apub/assets/pleroma/activities/create-note.json similarity index 100% rename from crates/apub/assets/pleroma-create-comment.json rename to crates/apub/assets/pleroma/activities/create-note.json diff --git a/crates/apub/assets/pleroma-private-message.json b/crates/apub/assets/pleroma/objects/chat_message.json similarity index 100% rename from crates/apub/assets/pleroma-private-message.json rename to crates/apub/assets/pleroma/objects/chat_message.json diff --git a/crates/apub/assets/pleroma-comment.json b/crates/apub/assets/pleroma/objects/note.json similarity index 100% rename from crates/apub/assets/pleroma-comment.json rename to crates/apub/assets/pleroma/objects/note.json diff --git a/crates/apub/assets/pleroma-person.json b/crates/apub/assets/pleroma/objects/person.json similarity index 100% rename from crates/apub/assets/pleroma-person.json rename to crates/apub/assets/pleroma/objects/person.json diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 335e1713..79259180 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -131,15 +131,13 @@ impl GetCommunity for CreateOrUpdateComment { #[cfg(test)] mod tests { - use serial_test::serial; - - use crate::objects::tests::file_to_json_object; - use super::*; + use crate::objects::tests::file_to_json_object; + use serial_test::serial; #[actix_rt::test] #[serial] async fn test_parse_pleroma_create_comment() { - let _: CreateOrUpdateComment = file_to_json_object("assets/pleroma-create-comment.json"); + file_to_json_object::("assets/pleroma/activities/create-note.json"); } } diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index cd491d87..736d1453 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -186,30 +186,29 @@ impl ApubObject for ApubComment { #[cfg(test)] pub(crate) mod tests { - use assert_json_diff::assert_json_include; - use serial_test::serial; - + use super::*; use crate::objects::{ community::ApubCommunity, + person::ApubPerson, + post::ApubPost, tests::{file_to_json_object, init_context}, }; - - use super::*; - use crate::objects::{person::ApubPerson, post::ApubPost}; + use assert_json_diff::assert_json_include; + use serial_test::serial; pub(crate) async fn prepare_comment_test( url: &Url, context: &LemmyContext, ) -> (ApubPerson, ApubCommunity, ApubPost) { - let person_json = file_to_json_object("assets/lemmy-person.json"); + let person_json = file_to_json_object("assets/lemmy/objects/person.json"); let person = ApubPerson::from_apub(&person_json, context, url, &mut 0) .await .unwrap(); - let community_json = file_to_json_object("assets/lemmy-community.json"); + let community_json = file_to_json_object("assets/lemmy/objects/group.json"); let community = ApubCommunity::from_apub(&community_json, context, url, &mut 0) .await .unwrap(); - let post_json = file_to_json_object("assets/lemmy-post.json"); + let post_json = file_to_json_object("assets/lemmy/objects/page.json"); let post = ApubPost::from_apub(&post_json, context, url, &mut 0) .await .unwrap(); @@ -229,7 +228,7 @@ pub(crate) mod tests { let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap(); let data = prepare_comment_test(&url, &context).await; - let json = file_to_json_object("assets/lemmy-comment.json"); + let json = file_to_json_object("assets/lemmy/objects/note.json"); let mut request_counter = 0; let comment = ApubComment::from_apub(&json, &context, &url, &mut request_counter) .await @@ -257,11 +256,11 @@ pub(crate) mod tests { let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2") .unwrap(); - let person_json = file_to_json_object("assets/pleroma-person.json"); + let person_json = file_to_json_object("assets/pleroma/objects/person.json"); ApubPerson::from_apub(&person_json, &context, &pleroma_url, &mut 0) .await .unwrap(); - let json = file_to_json_object("assets/pleroma-comment.json"); + let json = file_to_json_object("assets/pleroma/objects/note.json"); let mut request_counter = 0; let comment = ApubComment::from_apub(&json, &context, &pleroma_url, &mut request_counter) .await diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index 0a4c06a4..e1a37e8b 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -223,20 +223,17 @@ impl ApubCommunity { #[cfg(test)] mod tests { - use assert_json_diff::assert_json_include; - use serial_test::serial; - - use lemmy_db_schema::traits::Crud; - - use crate::objects::tests::{file_to_json_object, init_context}; - use super::*; + use crate::objects::tests::{file_to_json_object, init_context}; + use assert_json_diff::assert_json_include; + use lemmy_db_schema::traits::Crud; + use serial_test::serial; #[actix_rt::test] #[serial] async fn test_parse_lemmy_community() { let context = init_context(); - let mut json: Group = file_to_json_object("assets/lemmy-community.json"); + let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json"); let json_orig = json.clone(); // change these links so they dont fetch over the network json.moderators = Some(ObjectId::new( diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index 1d914e32..9e487ed3 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -256,7 +256,7 @@ mod tests { #[serial] async fn test_parse_lemmy_person() { let context = init_context(); - let json = file_to_json_object("assets/lemmy-person.json"); + let json = file_to_json_object("assets/lemmy/objects/person.json"); let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap(); let mut request_counter = 0; let person = ApubPerson::from_apub(&json, &context, &url, &mut request_counter) @@ -280,7 +280,7 @@ mod tests { #[serial] async fn test_parse_pleroma_person() { let context = init_context(); - let json = file_to_json_object("assets/pleroma-person.json"); + let json = file_to_json_object("assets/pleroma/objects/person.json"); let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap(); let mut request_counter = 0; let person = ApubPerson::from_apub(&json, &context, &url, &mut request_counter) diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 3ade8de0..e5a83297 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -206,15 +206,15 @@ mod tests { async fn test_parse_lemmy_post() { let context = init_context(); let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap(); - let community_json = file_to_json_object("assets/lemmy-community.json"); + let community_json = file_to_json_object("assets/lemmy/objects/group.json"); let community = ApubCommunity::from_apub(&community_json, &context, &url, &mut 0) .await .unwrap(); - let person_json = file_to_json_object("assets/lemmy-person.json"); + let person_json = file_to_json_object("assets/lemmy/objects/person.json"); let person = ApubPerson::from_apub(&person_json, &context, &url, &mut 0) .await .unwrap(); - let json = file_to_json_object("assets/lemmy-post.json"); + let json = file_to_json_object("assets/lemmy/objects/page.json"); let mut request_counter = 0; let post = ApubPost::from_apub(&json, &context, &url, &mut request_counter) .await diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index ee0aec95..678435ee 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -146,11 +146,11 @@ mod tests { use serial_test::serial; async fn prepare_comment_test(url: &Url, context: &LemmyContext) -> (ApubPerson, ApubPerson) { - let lemmy_person = file_to_json_object("assets/lemmy-person.json"); + let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json"); let person1 = ApubPerson::from_apub(&lemmy_person, context, url, &mut 0) .await .unwrap(); - let pleroma_person = file_to_json_object("assets/pleroma-person.json"); + let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json"); let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap(); let person2 = ApubPerson::from_apub(&pleroma_person, context, &pleroma_url, &mut 0) .await @@ -169,7 +169,7 @@ mod tests { let context = init_context(); let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap(); let data = prepare_comment_test(&url, &context).await; - let json = file_to_json_object("assets/lemmy-private-message.json"); + let json = file_to_json_object("assets/lemmy/objects/chat_message.json"); let mut request_counter = 0; let pm = ApubPrivateMessage::from_apub(&json, &context, &url, &mut request_counter) .await @@ -193,7 +193,7 @@ mod tests { let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap(); 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-private-message.json"); + let json = file_to_json_object("assets/pleroma/objects/chat_message.json"); let mut request_counter = 0; let pm = ApubPrivateMessage::from_apub(&json, &context, &pleroma_url, &mut request_counter) .await From 0bde2d595e793c0b6eb31b592036f0111321cebd Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Mon, 1 Nov 2021 14:05:20 +0100 Subject: [PATCH 14/17] Add tests for parsing activities and collections --- .../lemmy/activities/community/add_mod.json | 13 ++ .../community/announce_create_page.json | 37 ++++ .../activities/community/block_user.json | 13 ++ .../activities/community/remove_mod.json | 13 ++ .../activities/community/report_page.json | 10 + .../activities/community/undo_block_user.json | 24 ++ .../community/update_community.json | 37 ++++ .../create_or_update/create_note.json | 29 +++ .../create_or_update/create_page.json | 32 +++ .../create_or_update/update_page.json | 33 +++ .../activities/deletion/delete_page.json | 12 + .../activities/deletion/remove_note.json | 13 ++ .../activities/deletion/undo_delete_page.json | 23 ++ .../activities/deletion/undo_remove_note.json | 24 ++ .../lemmy/activities/following/accept.json | 17 ++ .../lemmy/activities/following/follow.json | 9 + .../activities/following/undo_follow.json | 17 ++ .../activities/private_message/create.json | 23 ++ .../activities/private_message/delete.json | 9 + .../private_message/undo_delete.json | 17 ++ .../lemmy/activities/voting/dislike_page.json | 12 + .../lemmy/activities/voting/like_note.json | 12 + .../activities/voting/undo_dislike_page.json | 23 ++ .../activities/voting/undo_like_note.json | 23 ++ .../lemmy/collections/group_followers.json | 6 + .../lemmy/collections/group_moderators.json | 7 + .../lemmy/collections/group_outbox.json | 209 ++++++++++++++++++ .../lemmy/collections/person_outbox.json | 6 + crates/apub/assets/lemmy/objects/page.json | 5 + crates/apub/assets/lemmy/objects/person.json | 1 + .../{create-note.json => create_note.json} | 0 .../activities/comment/create_or_update.rs | 13 -- crates/apub/src/activity_lists.rs | 14 +- .../src/collections/community_moderators.rs | 71 ++++++ crates/apub/src/fetcher/search.rs | 9 +- crates/apub/src/http/community.rs | 10 +- crates/apub/src/http/person.rs | 4 +- crates/apub/src/objects/comment.rs | 16 +- crates/apub/src/objects/community.rs | 31 ++- crates/apub/src/objects/person.rs | 85 ++----- crates/apub/src/objects/post.rs | 21 +- .../src/protocol/activities/community/mod.rs | 41 ++++ .../activities/create_or_update/mod.rs | 32 +++ .../src/protocol/activities/deletion/mod.rs | 19 ++ .../src/protocol/activities/following/mod.rs | 23 ++ .../activities/private_message/mod.rs | 27 +++ .../src/protocol/activities/voting/mod.rs | 19 ++ .../protocol/collections/group_followers.rs | 8 +- crates/apub/src/protocol/collections/mod.rs | 24 ++ .../src/protocol/collections/person_outbox.rs | 8 +- crates/apub/src/protocol/mod.rs | 17 ++ crates/apub/src/protocol/objects/mod.rs | 20 ++ crates/apub/src/protocol/objects/person.rs | 41 ++++ crates/db_schema/src/impls/activity.rs | 23 +- docker/federation/docker-compose.yml | 4 +- 55 files changed, 1112 insertions(+), 177 deletions(-) create mode 100644 crates/apub/assets/lemmy/activities/community/add_mod.json create mode 100644 crates/apub/assets/lemmy/activities/community/announce_create_page.json create mode 100644 crates/apub/assets/lemmy/activities/community/block_user.json create mode 100644 crates/apub/assets/lemmy/activities/community/remove_mod.json create mode 100644 crates/apub/assets/lemmy/activities/community/report_page.json create mode 100644 crates/apub/assets/lemmy/activities/community/undo_block_user.json create mode 100644 crates/apub/assets/lemmy/activities/community/update_community.json create mode 100644 crates/apub/assets/lemmy/activities/create_or_update/create_note.json create mode 100644 crates/apub/assets/lemmy/activities/create_or_update/create_page.json create mode 100644 crates/apub/assets/lemmy/activities/create_or_update/update_page.json create mode 100644 crates/apub/assets/lemmy/activities/deletion/delete_page.json create mode 100644 crates/apub/assets/lemmy/activities/deletion/remove_note.json create mode 100644 crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json create mode 100644 crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json create mode 100644 crates/apub/assets/lemmy/activities/following/accept.json create mode 100644 crates/apub/assets/lemmy/activities/following/follow.json create mode 100644 crates/apub/assets/lemmy/activities/following/undo_follow.json create mode 100644 crates/apub/assets/lemmy/activities/private_message/create.json create mode 100644 crates/apub/assets/lemmy/activities/private_message/delete.json create mode 100644 crates/apub/assets/lemmy/activities/private_message/undo_delete.json create mode 100644 crates/apub/assets/lemmy/activities/voting/dislike_page.json create mode 100644 crates/apub/assets/lemmy/activities/voting/like_note.json create mode 100644 crates/apub/assets/lemmy/activities/voting/undo_dislike_page.json create mode 100644 crates/apub/assets/lemmy/activities/voting/undo_like_note.json create mode 100644 crates/apub/assets/lemmy/collections/group_followers.json create mode 100644 crates/apub/assets/lemmy/collections/group_moderators.json create mode 100644 crates/apub/assets/lemmy/collections/group_outbox.json create mode 100644 crates/apub/assets/lemmy/collections/person_outbox.json rename crates/apub/assets/pleroma/activities/{create-note.json => create_note.json} (100%) create mode 100644 crates/apub/src/protocol/objects/person.rs diff --git a/crates/apub/assets/lemmy/activities/community/add_mod.json b/crates/apub/assets/lemmy/activities/community/add_mod.json new file mode 100644 index 00000000..d0eedd8b --- /dev/null +++ b/crates/apub/assets/lemmy/activities/community/add_mod.json @@ -0,0 +1,13 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/u/lemmy_alpha", + "target": "http://enterprise.lemmy.ml/c/main/moderators", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Add", + "id": "http://enterprise.lemmy.ml/activities/add/ec069147-77c3-447f-88c8-0ef1df10403f" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/community/announce_create_page.json b/crates/apub/assets/lemmy/activities/community/announce_create_page.json new file mode 100644 index 00000000..6b58cf1c --- /dev/null +++ b/crates/apub/assets/lemmy/activities/community/announce_create_page.json @@ -0,0 +1,37 @@ +{ + "actor": "http://enterprise.lemmy.ml/c/main", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "http://enterprise.lemmy.ml/post/7", + "attributedTo": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "http://enterprise.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "post 4", + "mediaType": "text/html", + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-11-01T12:11:22.871846+00:00" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Create", + "id": "http://enterprise.lemmy.ml/activities/create/2807c9ec-3ad8-4859-a9e0-28b59b6e499f" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main/followers" + ], + "type": "Announce", + "id": "http://enterprise.lemmy.ml/activities/announce/8030b171-803a-4108-94b1-342688f375cf" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/community/block_user.json b/crates/apub/assets/lemmy/activities/community/block_user.json new file mode 100644 index 00000000..9ca00816 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/community/block_user.json @@ -0,0 +1,13 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/u/lemmy_alpha", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "target": "http://enterprise.lemmy.ml/c/main", + "type": "Block", + "id": "http://enterprise.lemmy.ml/activities/block/5d42fffb-0903-4625-86d4-0b39bb344fc2" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/community/remove_mod.json b/crates/apub/assets/lemmy/activities/community/remove_mod.json new file mode 100644 index 00000000..2932fec3 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/community/remove_mod.json @@ -0,0 +1,13 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/u/lemmy_alpha", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Remove", + "target": "http://enterprise.lemmy.ml/c/main/moderators", + "id": "http://enterprise.lemmy.ml/activities/remove/aab114f8-cfbd-4935-a5b7-e1a64603650d" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/community/report_page.json b/crates/apub/assets/lemmy/activities/community/report_page.json new file mode 100644 index 00000000..bd1691b5 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/community/report_page.json @@ -0,0 +1,10 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "http://enterprise.lemmy.ml/c/main" + ], + "object": "http://enterprise.lemmy.ml/post/7", + "summary": "report this post", + "type": "Flag", + "id": "http://ds9.lemmy.ml/activities/flag/98b0933f-5e45-4a95-a15f-e0dc86361ba4" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/community/undo_block_user.json b/crates/apub/assets/lemmy/activities/community/undo_block_user.json new file mode 100644 index 00000000..5810b4f4 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/community/undo_block_user.json @@ -0,0 +1,24 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/u/lemmy_alpha", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "target": "http://enterprise.lemmy.ml/c/main", + "type": "Block", + "id": "http://enterprise.lemmy.ml/activities/block/726f43ab-bd0e-4ab3-89c8-627e976f553c" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Undo", + "id": "http://enterprise.lemmy.ml/activities/undo/06a20ffb-3e32-42fb-8f4c-674b36d7c557" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/community/update_community.json b/crates/apub/assets/lemmy/activities/community/update_community.json new file mode 100644 index 00000000..275d6d2b --- /dev/null +++ b/crates/apub/assets/lemmy/activities/community/update_community.json @@ -0,0 +1,37 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Group", + "id": "http://enterprise.lemmy.ml/c/main", + "preferredUsername": "main", + "name": "The Updated Community", + "summary": "

updated 2

\n", + "source": { + "content": "updated 2", + "mediaType": "text/markdown" + }, + "sensitive": false, + "moderators": "http://enterprise.lemmy.ml/c/main/moderators", + "inbox": "http://enterprise.lemmy.ml/c/main/inbox", + "outbox": "http://enterprise.lemmy.ml/c/main/outbox", + "followers": "http://enterprise.lemmy.ml/c/main/followers", + "endpoints": { + "sharedInbox": "http://enterprise.lemmy.ml/inbox" + }, + "publicKey": { + "id": "http://enterprise.lemmy.ml/c/main#main-key", + "owner": "http://enterprise.lemmy.ml/c/main", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA16Xh06V1l2yy0WAIMUTV\nnvZIuAuKDxzDQUNT+n8gmcVuvBu7tkpbPTQ3DjGB3bQfGC2ekew/yldwOXyZ7ry1\npbJSYSrCBJrAlPLs/ao3OPTqmcl3vnSWti/hqopEV+Um2t7fwpkCjVrnzVKRSlys\nihnrth64ZiwAqq2llpaXzWc1SR2URZYSdnry/4d9UNrZVkumIeg1gk9KbCAo4j/O\njsv/aBjpZcTeLmtMZf6fcrvGre9duJdx6e2Tg/YNcnSnARosqev/UwVTzzGNVWXg\n9rItaa0a0aea4se4Bn6QXvOBbcq3+OYZMR6a34hh5BTeNG8WbpwmVahS0WFUsv9G\nswIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "published": "2021-10-29T15:05:51.476984+00:00", + "updated": "2021-11-01T12:23:50.151874+00:00" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Update", + "id": "http://ds9.lemmy.ml/activities/update/d3717cf5-096d-473f-9530-5d52f9d51f5f" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/create_or_update/create_note.json b/crates/apub/assets/lemmy/activities/create_or_update/create_note.json new file mode 100644 index 00000000..4360ce92 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/create_or_update/create_note.json @@ -0,0 +1,29 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Note", + "id": "http://ds9.lemmy.ml/comment/1", + "attributedTo": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "content": "hello", + "mediaType": "text/html", + "source": { + "content": "hello", + "mediaType": "text/markdown" + }, + "inReplyTo": "http://ds9.lemmy.ml/post/1", + "published": "2021-11-01T11:45:49.794920+00:00" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main", + "http://ds9.lemmy.ml/u/lemmy_alpha" + ], + "tag": [], + "type": "Create", + "id": "http://ds9.lemmy.ml/activities/create/1e77d67c-44ac-45ed-bf2a-460e21f60236" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/create_or_update/create_page.json b/crates/apub/assets/lemmy/activities/create_or_update/create_page.json new file mode 100644 index 00000000..b223120b --- /dev/null +++ b/crates/apub/assets/lemmy/activities/create_or_update/create_page.json @@ -0,0 +1,32 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "http://ds9.lemmy.ml/post/1", + "attributedTo": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "http://enterprise.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "test post", + "content": "

test body

\n", + "mediaType": "text/html", + "source": { + "content": "test body", + "mediaType": "text/markdown" + }, + "url": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg", + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-10-29T15:10:51.557399+00:00" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Create", + "id": "http://ds9.lemmy.ml/activities/create/eee6a57a-622f-464d-b560-73ae1fcd3ddf" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/create_or_update/update_page.json b/crates/apub/assets/lemmy/activities/create_or_update/update_page.json new file mode 100644 index 00000000..beadfa0d --- /dev/null +++ b/crates/apub/assets/lemmy/activities/create_or_update/update_page.json @@ -0,0 +1,33 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "http://ds9.lemmy.ml/post/1", + "attributedTo": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "http://enterprise.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "test post 1", + "content": "

test body

\n", + "mediaType": "text/html", + "source": { + "content": "test body", + "mediaType": "text/markdown" + }, + "url": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg", + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-10-29T15:10:51.557399+00:00", + "updated": "2021-10-29T15:11:35.976374+00:00" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Update", + "id": "http://ds9.lemmy.ml/activities/update/ab360117-e165-4de4-b7fc-906b62c98631" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/deletion/delete_page.json b/crates/apub/assets/lemmy/activities/deletion/delete_page.json new file mode 100644 index 00000000..8dd26a10 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/deletion/delete_page.json @@ -0,0 +1,12 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/post/1", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Delete", + "id": "http://ds9.lemmy.ml/activities/delete/f2abee48-c7bb-41d5-9e27-8775ff32db12" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/deletion/remove_note.json b/crates/apub/assets/lemmy/activities/deletion/remove_note.json new file mode 100644 index 00000000..8ea35404 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/deletion/remove_note.json @@ -0,0 +1,13 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/comment/1", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Delete", + "summary": "bad comment", + "id": "http://enterprise.lemmy.ml/activities/delete/42ca1a79-f99e-4518-a2ca-ba2df221eb5e" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json b/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json new file mode 100644 index 00000000..9f824fa1 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json @@ -0,0 +1,23 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/post/1", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Delete", + "id": "http://ds9.lemmy.ml/activities/delete/b13cca96-7737-41e1-9769-8fbf972b3509" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Undo", + "id": "http://ds9.lemmy.ml/activities/undo/5e939cfb-b8a1-4de8-950f-9d684e9162b9" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json b/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json new file mode 100644 index 00000000..413cf16b --- /dev/null +++ b/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json @@ -0,0 +1,24 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/comment/1", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Delete", + "summary": "bad comment", + "id": "http://enterprise.lemmy.ml/activities/delete/2598435c-87a3-49cd-81f3-a44b03b7af9d" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Undo", + "id": "http://enterprise.lemmy.ml/activities/undo/a850cf21-3866-4b3a-b80b-56aa00997fee" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/following/accept.json b/crates/apub/assets/lemmy/activities/following/accept.json new file mode 100644 index 00000000..d5bcfaf4 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/following/accept.json @@ -0,0 +1,17 @@ +{ + "actor": "http://enterprise.lemmy.ml/c/main", + "to": [ + "http://ds9.lemmy.ml/u/lemmy_alpha" + ], + "object": { + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "http://enterprise.lemmy.ml/c/main" + ], + "object": "http://enterprise.lemmy.ml/c/main", + "type": "Follow", + "id": "http://ds9.lemmy.ml/activities/follow/6abcd50b-b8ca-4952-86b0-a6dd8cc12866" + }, + "type": "Accept", + "id": "http://enterprise.lemmy.ml/activities/accept/75f080cc-3d45-4654-8186-8f3bb853fa27" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/following/follow.json b/crates/apub/assets/lemmy/activities/following/follow.json new file mode 100644 index 00000000..50cc77dc --- /dev/null +++ b/crates/apub/assets/lemmy/activities/following/follow.json @@ -0,0 +1,9 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "http://enterprise.lemmy.ml/c/main" + ], + "object": "http://enterprise.lemmy.ml/c/main", + "type": "Follow", + "id": "http://ds9.lemmy.ml/activities/follow/6abcd50b-b8ca-4952-86b0-a6dd8cc12866" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/following/undo_follow.json b/crates/apub/assets/lemmy/activities/following/undo_follow.json new file mode 100644 index 00000000..d27bec0a --- /dev/null +++ b/crates/apub/assets/lemmy/activities/following/undo_follow.json @@ -0,0 +1,17 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "http://enterprise.lemmy.ml/c/main" + ], + "object": { + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "http://enterprise.lemmy.ml/c/main" + ], + "object": "http://enterprise.lemmy.ml/c/main", + "type": "Follow", + "id": "http://ds9.lemmy.ml/activities/follow/dc2f1bc5-f3a0-4daa-a46b-428cbfbd023c" + }, + "type": "Undo", + "id": "http://ds9.lemmy.ml/activities/undo/dd83c482-8ebd-4b6c-9008-c8373bd1a86a" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/private_message/create.json b/crates/apub/assets/lemmy/activities/private_message/create.json new file mode 100644 index 00000000..de080a8f --- /dev/null +++ b/crates/apub/assets/lemmy/activities/private_message/create.json @@ -0,0 +1,23 @@ +{ + "id": "http://enterprise.lemmy.ml/activities/create/987d05fa-f637-46d7-85be-13d112bc269f", + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "http://ds9.lemmy.ml/u/lemmy_alpha" + ], + "object": { + "type": "ChatMessage", + "id": "http://enterprise.lemmy.ml/private_message/1", + "attributedTo": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "http://ds9.lemmy.ml/u/lemmy_alpha" + ], + "content": "hello", + "mediaType": "text/html", + "source": { + "content": "hello", + "mediaType": "text/markdown" + }, + "published": "2021-10-29T15:31:56.058289+00:00" + }, + "type": "Create" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/private_message/delete.json b/crates/apub/assets/lemmy/activities/private_message/delete.json new file mode 100644 index 00000000..f5f85ac3 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/private_message/delete.json @@ -0,0 +1,9 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "http://enterprise.lemmy.ml/u/lemmy_beta" + ], + "object": "http://enterprise.lemmy.ml/private_message/1", + "type": "Delete", + "id": "http://enterprise.lemmy.ml/activities/delete/041d9858-5eef-4ad9-84ae-7455b4d87ed9" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/private_message/undo_delete.json b/crates/apub/assets/lemmy/activities/private_message/undo_delete.json new file mode 100644 index 00000000..d9b4ed75 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/private_message/undo_delete.json @@ -0,0 +1,17 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "http://ds9.lemmy.ml/u/lemmy_alpha" + ], + "object": { + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "http://enterprise.lemmy.ml/u/lemmy_beta" + ], + "object": "http://enterprise.lemmy.ml/private_message/1", + "type": "Delete", + "id": "http://enterprise.lemmy.ml/activities/delete/616c41be-04ed-4bd4-b865-30712186b122" + }, + "type": "Undo", + "id": "http://enterprise.lemmy.ml/activities/undo/35e5b337-014c-4bbe-8d63-6fac96f51409" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/voting/dislike_page.json b/crates/apub/assets/lemmy/activities/voting/dislike_page.json new file mode 100644 index 00000000..822a9d35 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/voting/dislike_page.json @@ -0,0 +1,12 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/post/1", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Dislike", + "id": "http://enterprise.lemmy.ml/activities/dislike/64d40d40-a829-43a5-8247-1fb595b3ca1c" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/voting/like_note.json b/crates/apub/assets/lemmy/activities/voting/like_note.json new file mode 100644 index 00000000..35e96906 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/voting/like_note.json @@ -0,0 +1,12 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/comment/1", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Like", + "id": "http://ds9.lemmy.ml/activities/like/fd61d070-7382-46a9-b2b7-6bb253732877" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/voting/undo_dislike_page.json b/crates/apub/assets/lemmy/activities/voting/undo_dislike_page.json new file mode 100644 index 00000000..4123ebab --- /dev/null +++ b/crates/apub/assets/lemmy/activities/voting/undo_dislike_page.json @@ -0,0 +1,23 @@ +{ + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/post/1", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Like", + "id": "http://enterprise.lemmy.ml/activities/like/2227ab2c-79e2-4fca-a1d2-1d67dacf2457" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Undo", + "id": "http://enterprise.lemmy.ml/activities/undo/6cc6fb71-39fe-49ea-9506-f0423b101e98" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/activities/voting/undo_like_note.json b/crates/apub/assets/lemmy/activities/voting/undo_like_note.json new file mode 100644 index 00000000..84a6efe5 --- /dev/null +++ b/crates/apub/assets/lemmy/activities/voting/undo_like_note.json @@ -0,0 +1,23 @@ +{ + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": "http://ds9.lemmy.ml/comment/1", + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Like", + "id": "http://ds9.lemmy.ml/activities/like/efcf7ae2-dfcc-4ff4-9ce4-6adf251ff004" + }, + "cc": [ + "http://enterprise.lemmy.ml/c/main" + ], + "type": "Undo", + "id": "http://ds9.lemmy.ml/activities/undo/3518565c-24a7-4d9e-8e0a-f7a2f45ac618" +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/collections/group_followers.json b/crates/apub/assets/lemmy/collections/group_followers.json new file mode 100644 index 00000000..a2b03ab1 --- /dev/null +++ b/crates/apub/assets/lemmy/collections/group_followers.json @@ -0,0 +1,6 @@ +{ + "id": "http://enterprise.lemmy.ml/c/main/followers", + "type": "Collection", + "totalItems": 3, + "items": [] +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/collections/group_moderators.json b/crates/apub/assets/lemmy/collections/group_moderators.json new file mode 100644 index 00000000..eccd0dad --- /dev/null +++ b/crates/apub/assets/lemmy/collections/group_moderators.json @@ -0,0 +1,7 @@ +{ + "type": "OrderedCollection", + "id": "https://enterprise.lemmy.ml/c/tenforward/moderators", + "orderedItems": [ + "https://enterprise.lemmy.ml/u/picard" + ] +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/collections/group_outbox.json b/crates/apub/assets/lemmy/collections/group_outbox.json new file mode 100644 index 00000000..cf68742f --- /dev/null +++ b/crates/apub/assets/lemmy/collections/group_outbox.json @@ -0,0 +1,209 @@ +{ + "type": "OrderedCollection", + "id": "https://ds9.lemmy.ml/c/main/outbox", + "totalItems": 7, + "orderedItems": [ + { + "actor": "https://ds9.lemmy.ml/u/dess_ds9", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "https://ds9.lemmy.ml/post/1685", + "attributedTo": "https://ds9.lemmy.ml/u/dess_ds9", + "to": [ + "https://ds9.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "Test post", + "mediaType": "text/html", + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-09-30T16:37:58.425718+00:00", + "updated": "2021-09-30T16:39:50.934055+00:00" + }, + "cc": [ + "https://ds9.lemmy.ml/c/main" + ], + "type": "Create", + "id": "https://ds9.lemmy.ml/activities/create/157bc329-05cb-4dc3-ad9e-5110fde3f3aa" + }, + { + "actor": "https://ds9.lemmy.ml/u/nutomic", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "https://ds9.lemmy.ml/post/1665", + "attributedTo": "https://ds9.lemmy.ml/u/nutomic", + "to": [ + "https://ds9.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "another webmention test", + "mediaType": "text/html", + "url": "https://webmention.rocks/test/1", + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-09-17T13:22:15.026912+00:00" + }, + "cc": [ + "https://ds9.lemmy.ml/c/main" + ], + "type": "Create", + "id": "https://ds9.lemmy.ml/activities/create/c54e4509-16ac-42bf-b3b4-0bf8516f8152" + }, + { + "actor": "https://ds9.lemmy.ml/u/nutomic", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "https://ds9.lemmy.ml/post/1664", + "attributedTo": "https://ds9.lemmy.ml/u/nutomic", + "to": [ + "https://ds9.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "another test", + "mediaType": "text/html", + "url": "https://webmention.rocks/test/1", + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-09-17T13:13:21.675891+00:00" + }, + "cc": [ + "https://ds9.lemmy.ml/c/main" + ], + "type": "Create", + "id": "https://ds9.lemmy.ml/activities/create/25f7d2cb-11d5-4c9c-aa3c-85fbff9f9e0c" + }, + { + "actor": "https://ds9.lemmy.ml/u/nutomic", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "https://ds9.lemmy.ml/post/1663", + "attributedTo": "https://ds9.lemmy.ml/u/nutomic", + "to": [ + "https://ds9.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "Webmention test from Lemmy", + "mediaType": "text/html", + "url": "https://webmention.rocks/test/1", + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-09-17T13:00:15.392844+00:00" + }, + "cc": [ + "https://ds9.lemmy.ml/c/main" + ], + "type": "Create", + "id": "https://ds9.lemmy.ml/activities/create/cfbd12b8-2e11-42b6-a609-b482decbaf11" + }, + { + "actor": "https://ds9.lemmy.ml/u/dess_tester_3", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "https://ds9.lemmy.ml/post/1644", + "attributedTo": "https://ds9.lemmy.ml/u/dess_tester_3", + "to": [ + "https://ds9.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "The best wireless earbuds you can buy right now | Engadget", + "mediaType": "text/html", + "url": "https://www.engadget.com/best-wireless-earbuds-120058222.html", + "image": { + "type": "Image", + "url": "https://ds9.lemmy.ml/pictrs/image/0WWsYOuwAE.jpg" + }, + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-08-26T01:22:06.428368+00:00" + }, + "cc": [ + "https://ds9.lemmy.ml/c/main" + ], + "type": "Create", + "id": "https://ds9.lemmy.ml/activities/create/76c94408-944a-4a2f-a88b-d10f12b472b0" + }, + { + "actor": "https://ds9.lemmy.ml/u/dess_ds9", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "https://ds9.lemmy.ml/post/1643", + "attributedTo": "https://ds9.lemmy.ml/u/dess_ds9", + "to": [ + "https://ds9.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "First Look: Cadillac’s luxury EV debut seems like a winner | Engadges", + "content": "

test

\n", + "mediaType": "text/html", + "source": { + "content": "test", + "mediaType": "text/markdown" + }, + "url": "https://www.engadget.com/cadillac-lyriq-luxury-ev-first-look-video-171543752.html", + "image": { + "type": "Image", + "url": "https://ds9.lemmy.ml/pictrs/image/gnmtvgXP31.jpg" + }, + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-08-23T23:43:06.560543+00:00", + "updated": "2021-08-23T23:52:51.832606+00:00" + }, + "cc": [ + "https://ds9.lemmy.ml/c/main" + ], + "type": "Create", + "id": "https://ds9.lemmy.ml/activities/create/b1f95918-f593-4951-91cf-2c3340cd9509" + }, + { + "actor": "https://ds9.lemmy.ml/u/dess_ds9_2", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "object": { + "type": "Page", + "id": "https://ds9.lemmy.ml/post/1642", + "attributedTo": "https://ds9.lemmy.ml/u/dess_ds9_2", + "to": [ + "https://ds9.lemmy.ml/c/main", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name": "A test post from DS9", + "mediaType": "text/html", + "commentsEnabled": true, + "sensitive": false, + "stickied": false, + "published": "2021-08-06T14:10:47.493075+00:00" + }, + "cc": [ + "https://ds9.lemmy.ml/c/main" + ], + "type": "Create", + "id": "https://ds9.lemmy.ml/activities/create/6359b2e7-badb-4241-b5ee-b093078361bd" + } + ] +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/collections/person_outbox.json b/crates/apub/assets/lemmy/collections/person_outbox.json new file mode 100644 index 00000000..8da56102 --- /dev/null +++ b/crates/apub/assets/lemmy/collections/person_outbox.json @@ -0,0 +1,6 @@ +{ + "type": "OrderedCollection", + "id": "http://ds9.lemmy.ml/u/lemmy_alpha/outbox", + "orderedItems": [], + "totalItems": 0 +} \ No newline at end of file diff --git a/crates/apub/assets/lemmy/objects/page.json b/crates/apub/assets/lemmy/objects/page.json index 2c39acce..36cac596 100644 --- a/crates/apub/assets/lemmy/objects/page.json +++ b/crates/apub/assets/lemmy/objects/page.json @@ -13,6 +13,11 @@ "content": "This is a post in the /c/tenforward community", "mediaType": "text/markdown" }, + "url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png", + "image": { + "type": "Image", + "url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png" + }, "sensitive": false, "commentsEnabled": true, "stickied": true, diff --git a/crates/apub/assets/lemmy/objects/person.json b/crates/apub/assets/lemmy/objects/person.json index 769f2f3e..e21fa4d2 100644 --- a/crates/apub/assets/lemmy/objects/person.json +++ b/crates/apub/assets/lemmy/objects/person.json @@ -16,6 +16,7 @@ "type": "Image", "url": "https://enterprise.lemmy.ml/pictrs/image/XenaYI5hTn.png" }, + "matrix_user_id": "@picard:matrix.org", "inbox": "https://enterprise.lemmy.ml/u/picard/inbox", "outbox": "https://enterprise.lemmy.ml/u/picard/outbox", "endpoints": { diff --git a/crates/apub/assets/pleroma/activities/create-note.json b/crates/apub/assets/pleroma/activities/create_note.json similarity index 100% rename from crates/apub/assets/pleroma/activities/create-note.json rename to crates/apub/assets/pleroma/activities/create_note.json diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 79259180..a9c1a9ee 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -128,16 +128,3 @@ impl GetCommunity for CreateOrUpdateComment { Ok(community.into()) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::objects::tests::file_to_json_object; - use serial_test::serial; - - #[actix_rt::test] - #[serial] - async fn test_parse_pleroma_create_comment() { - file_to_json_object::("assets/pleroma/activities/create-note.json"); - } -} diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs index 9fd1a9dc..1197af85 100644 --- a/crates/apub/src/activity_lists.rs +++ b/crates/apub/src/activity_lists.rs @@ -1,12 +1,3 @@ -use serde::{Deserialize, Serialize}; - -use lemmy_apub_lib::{ - traits::{ActivityFields, ActivityHandler, ActorType}, - verify::verify_urls_match, -}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; - use crate::{ activities::community::announce::GetCommunity, objects::community::ApubCommunity, @@ -35,6 +26,10 @@ use crate::{ voting::{undo_vote::UndoVote, vote::Vote}, }, }; +use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] #[serde(untagged)] @@ -107,7 +102,6 @@ impl GetCommunity for AnnouncableActivities { AddMod(a) => a.get_community(context, request_counter).await?, RemoveMod(a) => a.get_community(context, request_counter).await?, }; - verify_urls_match(self.actor(), &community.actor_id())?; Ok(community) } } diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index 1a5a96d3..d97affe2 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -120,3 +120,74 @@ impl ApubObject for ApubCommunityModerators { Ok(ApubCommunityModerators { 0: vec![] }) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::objects::{ + community::tests::parse_lemmy_community, + person::tests::parse_lemmy_person, + tests::{file_to_json_object, init_context}, + }; + use lemmy_db_schema::{ + source::{ + community::Community, + person::{Person, PersonForm}, + }, + traits::Crud, + }; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_community_moderators() { + let context = init_context(); + let community = parse_lemmy_community(&context).await; + let community_id = community.id; + + let old_mod = PersonForm { + name: "holly".into(), + ..PersonForm::default() + }; + let old_mod = Person::create(&context.pool().get().unwrap(), &old_mod).unwrap(); + let community_moderator_form = CommunityModeratorForm { + community_id: community.id, + person_id: old_mod.id, + }; + + CommunityModerator::join(&context.pool().get().unwrap(), &community_moderator_form).unwrap(); + + let new_mod = parse_lemmy_person(&context).await; + + let json: GroupModerators = + file_to_json_object("assets/lemmy/collections/group_moderators.json"); + let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap(); + let mut request_counter = 0; + let community_context = CommunityContext { + 0: community, + 1: context, + }; + ApubCommunityModerators::from_apub(&json, &community_context, &url, &mut request_counter) + .await + .unwrap(); + assert_eq!(request_counter, 0); + + let current_moderators = blocking(community_context.1.pool(), move |conn| { + CommunityModeratorView::for_community(conn, community_id) + }) + .await + .unwrap() + .unwrap(); + + assert_eq!(current_moderators.len(), 1); + assert_eq!(current_moderators[0].moderator.id, new_mod.id); + + Person::delete(&*community_context.1.pool().get().unwrap(), old_mod.id).unwrap(); + Person::delete(&*community_context.1.pool().get().unwrap(), new_mod.id).unwrap(); + Community::delete( + &*community_context.1.pool().get().unwrap(), + community_context.0.id, + ) + .unwrap(); + } +} diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index 77a23828..51699eb0 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -18,13 +18,8 @@ use lemmy_websocket::LemmyContext; use crate::{ fetcher::object_id::ObjectId, - objects::{ - comment::ApubComment, - community::ApubCommunity, - person::{ApubPerson, Person}, - post::ApubPost, - }, - protocol::objects::{group::Group, note::Note, page::Page}, + objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, + protocol::objects::{group::Group, note::Note, page::Page, person::Person}, }; /// Attempt to parse the query as URL, and fetch an ActivityPub object from it. diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index ffc76d3f..4110d2b2 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -18,15 +18,12 @@ use crate::{ objects::community::ApubCommunity, protocol::{ activities::community::announce::AnnounceActivity, - collections::group_followers::CommunityFollowers, + collections::group_followers::GroupFollowers, }, }; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - traits::{ActivityFields, ActorType, ApubObject}, - verify::verify_domains_match, -}; +use lemmy_apub_lib::traits::{ActivityFields, ApubObject}; use lemmy_db_schema::source::community::Community; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; @@ -84,7 +81,6 @@ pub(in crate::http) async fn receive_group_inbox( if let GroupInboxActivities::AnnouncableActivities(announcable) = activity { let community = announcable.get_community(context, &mut 0).await?; let actor_id = ObjectId::new(announcable.actor().clone()); - verify_domains_match(&community.actor_id(), announcable.id_unchecked())?; verify_person_in_community(&actor_id, &community, context, &mut 0).await?; if community.local { AnnounceActivity::send(announcable, &community, vec![], context).await?; @@ -103,7 +99,7 @@ pub(crate) async fn get_apub_community_followers( Community::read_from_name(conn, &info.community_name) }) .await??; - let followers = CommunityFollowers::new(community, &context).await?; + let followers = GroupFollowers::new(community, &context).await?; Ok(create_apub_response(&followers)) } diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index 3f18bcc5..a5ea4ad1 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -8,7 +8,7 @@ use crate::{ receive_activity, }, objects::person::ApubPerson, - protocol::collections::person_outbox::UserOutbox, + protocol::collections::person_outbox::PersonOutbox, }; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use lemmy_api_common::blocking; @@ -74,6 +74,6 @@ pub(crate) async fn get_apub_person_outbox( Person::find_by_name(conn, &info.user_name) }) .await??; - let outbox = UserOutbox::new(person).await?; + let outbox = PersonOutbox::new(person).await?; Ok(create_apub_response(&outbox)) } diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 736d1453..b5f72ca3 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -188,26 +188,20 @@ impl ApubObject for ApubComment { pub(crate) mod tests { use super::*; use crate::objects::{ - community::ApubCommunity, - person::ApubPerson, + community::{tests::parse_lemmy_community, ApubCommunity}, + person::{tests::parse_lemmy_person, ApubPerson}, post::ApubPost, tests::{file_to_json_object, init_context}, }; use assert_json_diff::assert_json_include; use serial_test::serial; - pub(crate) async fn prepare_comment_test( + async fn prepare_comment_test( url: &Url, context: &LemmyContext, ) -> (ApubPerson, ApubCommunity, ApubPost) { - let person_json = file_to_json_object("assets/lemmy/objects/person.json"); - let person = ApubPerson::from_apub(&person_json, context, url, &mut 0) - .await - .unwrap(); - let community_json = file_to_json_object("assets/lemmy/objects/group.json"); - let community = ApubCommunity::from_apub(&community_json, context, url, &mut 0) - .await - .unwrap(); + let person = parse_lemmy_person(context).await; + let community = parse_lemmy_community(context).await; let post_json = file_to_json_object("assets/lemmy/objects/page.json"); let post = ApubPost::from_apub(&post_json, context, url, &mut 0) .await diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index e1a37e8b..89258fe0 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -222,42 +222,39 @@ impl ApubCommunity { } #[cfg(test)] -mod tests { +pub(crate) mod tests { use super::*; use crate::objects::tests::{file_to_json_object, init_context}; - use assert_json_diff::assert_json_include; use lemmy_db_schema::traits::Crud; use serial_test::serial; - #[actix_rt::test] - #[serial] - async fn test_parse_lemmy_community() { - let context = init_context(); + pub(crate) async fn parse_lemmy_community(context: &LemmyContext) -> ApubCommunity { let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json"); - let json_orig = json.clone(); // change these links so they dont fetch over the network - json.moderators = Some(ObjectId::new( - Url::parse("https://enterprise.lemmy.ml/c/tenforward/not_moderators").unwrap(), - )); + json.moderators = None; json.outbox = ObjectId::new(Url::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; - let community = ApubCommunity::from_apub(&json, &context, &url, &mut request_counter) + let community = ApubCommunity::from_apub(&json, context, &url, &mut request_counter) .await .unwrap(); + // this makes two requests to the (intentionally) broken outbox/moderators collections + assert_eq!(request_counter, 1); + community + } + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_community() { + let context = init_context(); + let community = parse_lemmy_community(&context).await; - assert_eq!(community.actor_id.clone().into_inner(), url); assert_eq!(community.title, "Ten Forward"); assert!(community.public_key.is_some()); assert!(!community.local); assert_eq!(community.description.as_ref().unwrap().len(), 132); - // this makes two requests to the (intentionally) broken outbox/moderators collections - assert_eq!(request_counter, 2); - - let to_apub = community.to_apub(&context).await.unwrap(); - assert_json_include!(actual: json_orig, expected: to_apub); Community::delete(&*context.pool().get().unwrap(), community.id).unwrap(); } diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index 9e487ed3..18522e56 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -2,13 +2,16 @@ use crate::{ check_is_apub_id_valid, generate_outbox_url, objects::get_summary_from_string_or_source, - protocol::{ImageObject, Source}, + protocol::{ + objects::person::{Person, UserTypes}, + ImageObject, + Source, + }, }; -use activitystreams::{actor::Endpoints, object::kind::ImageType, unparsed::Unparsed}; -use chrono::{DateTime, FixedOffset, NaiveDateTime}; +use activitystreams::{actor::Endpoints, object::kind::ImageType}; +use chrono::NaiveDateTime; use lemmy_api_common::blocking; use lemmy_apub_lib::{ - signatures::PublicKey, traits::{ActorType, ApubObject}, values::MediaTypeMarkdown, verify::verify_domains_match, @@ -22,54 +25,9 @@ use lemmy_utils::{ LemmyError, }; use lemmy_websocket::LemmyContext; -use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; use std::ops::Deref; use url::Url; -#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] -pub enum UserTypes { - Person, - Service, -} - -#[skip_serializing_none] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Person { - #[serde(rename = "type")] - kind: UserTypes, - id: Url, - /// username, set at account creation and can never be changed - preferred_username: String, - /// displayname (can be changed at any time) - name: Option, - summary: Option, - source: Option, - /// user avatar - icon: Option, - /// user banner - image: Option, - matrix_user_id: Option, - inbox: Url, - /// mandatory field in activitypub, currently empty in lemmy - outbox: Url, - endpoints: Endpoints, - public_key: PublicKey, - published: Option>, - updated: Option>, - #[serde(flatten)] - unparsed: Unparsed, -} - -// TODO: can generate this with a derive macro -impl Person { - pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> { - verify_domains_match(&self.id, expected_domain)?; - Ok(&self.id) - } -} - #[derive(Clone, Debug, PartialEq)] pub struct ApubPerson(DbPerson); @@ -170,7 +128,8 @@ impl ApubObject for ApubPerson { expected_domain: &Url, _request_counter: &mut i32, ) -> Result { - let actor_id = Some(person.id(expected_domain)?.clone().into()); + verify_domains_match(&person.id, expected_domain)?; + let actor_id = Some(person.id.clone().into()); let name = person.preferred_username.clone(); let display_name: Option = person.name.clone(); let bio = get_summary_from_string_or_source(&person.summary, &person.source); @@ -245,33 +204,33 @@ impl ActorType for ApubPerson { } #[cfg(test)] -mod tests { +pub(crate) mod tests { use super::*; use crate::objects::tests::{file_to_json_object, init_context}; - use assert_json_diff::assert_json_include; use lemmy_db_schema::traits::Crud; use serial_test::serial; + pub(crate) async fn parse_lemmy_person(context: &LemmyContext) -> ApubPerson { + let json = file_to_json_object("assets/lemmy/objects/person.json"); + let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap(); + let mut request_counter = 0; + let person = ApubPerson::from_apub(&json, context, &url, &mut request_counter) + .await + .unwrap(); + assert_eq!(request_counter, 0); + person + } + #[actix_rt::test] #[serial] async fn test_parse_lemmy_person() { let context = init_context(); - let json = file_to_json_object("assets/lemmy/objects/person.json"); - let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap(); - let mut request_counter = 0; - let person = ApubPerson::from_apub(&json, &context, &url, &mut request_counter) - .await - .unwrap(); + let person = parse_lemmy_person(&context).await; - assert_eq!(person.actor_id.clone().into_inner(), url); assert_eq!(person.display_name, Some("Jean-Luc Picard".to_string())); assert!(person.public_key.is_some()); assert!(!person.local); assert_eq!(person.bio.as_ref().unwrap().len(), 39); - assert_eq!(request_counter, 0); - - let to_apub = person.to_apub(&context).await.unwrap(); - assert_json_include!(actual: json, expected: to_apub); DbPerson::delete(&*context.pool().get().unwrap(), person.id).unwrap(); } diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index e5a83297..7f142be5 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -193,28 +193,22 @@ impl ApubObject for ApubPost { mod tests { use super::*; use crate::objects::{ - community::ApubCommunity, - person::ApubPerson, + community::tests::parse_lemmy_community, + person::tests::parse_lemmy_person, post::ApubPost, tests::{file_to_json_object, init_context}, }; - use assert_json_diff::assert_json_include; use serial_test::serial; #[actix_rt::test] #[serial] async fn test_parse_lemmy_post() { let context = init_context(); - let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap(); - let community_json = file_to_json_object("assets/lemmy/objects/group.json"); - let community = ApubCommunity::from_apub(&community_json, &context, &url, &mut 0) - .await - .unwrap(); - let person_json = file_to_json_object("assets/lemmy/objects/person.json"); - let person = ApubPerson::from_apub(&person_json, &context, &url, &mut 0) - .await - .unwrap(); + let community = parse_lemmy_community(&context).await; + let person = parse_lemmy_person(&context).await; + let json = file_to_json_object("assets/lemmy/objects/page.json"); + let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap(); let mut request_counter = 0; let post = ApubPost::from_apub(&json, &context, &url, &mut request_counter) .await @@ -228,9 +222,6 @@ mod tests { assert!(post.stickied); assert_eq!(request_counter, 0); - let to_apub = post.to_apub(&context).await.unwrap(); - assert_json_include!(actual: json, expected: to_apub); - Post::delete(&*context.pool().get().unwrap(), post.id).unwrap(); Person::delete(&*context.pool().get().unwrap(), person.id).unwrap(); Community::delete(&*context.pool().get().unwrap(), community.id).unwrap(); diff --git a/crates/apub/src/protocol/activities/community/mod.rs b/crates/apub/src/protocol/activities/community/mod.rs index 62f8329b..a7bc8141 100644 --- a/crates/apub/src/protocol/activities/community/mod.rs +++ b/crates/apub/src/protocol/activities/community/mod.rs @@ -5,3 +5,44 @@ pub mod remove_mod; pub mod report; pub mod undo_block_user; pub mod update; + +#[cfg(test)] +mod tests { + use crate::protocol::{ + activities::community::{ + add_mod::AddMod, + block_user::BlockUserFromCommunity, + remove_mod::RemoveMod, + report::Report, + undo_block_user::UndoBlockUserFromCommunity, + update::UpdateCommunity, + }, + tests::test_parse_lemmy_item, + }; + use activitystreams::activity::Announce; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_community() { + test_parse_lemmy_item::( + "assets/lemmy/activities/community/announce_create_page.json", + ); + + test_parse_lemmy_item::("assets/lemmy/activities/community/add_mod.json"); + test_parse_lemmy_item::("assets/lemmy/activities/community/remove_mod.json"); + + test_parse_lemmy_item::( + "assets/lemmy/activities/community/block_user.json", + ); + test_parse_lemmy_item::( + "assets/lemmy/activities/community/undo_block_user.json", + ); + + test_parse_lemmy_item::( + "assets/lemmy/activities/community/update_community.json", + ); + + test_parse_lemmy_item::("assets/lemmy/activities/community/report_page.json"); + } +} diff --git a/crates/apub/src/protocol/activities/create_or_update/mod.rs b/crates/apub/src/protocol/activities/create_or_update/mod.rs index 38203b5e..1048d46a 100644 --- a/crates/apub/src/protocol/activities/create_or_update/mod.rs +++ b/crates/apub/src/protocol/activities/create_or_update/mod.rs @@ -1,2 +1,34 @@ pub mod comment; pub mod post; + +#[cfg(test)] +mod tests { + use crate::{ + objects::tests::file_to_json_object, + protocol::{ + activities::create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost}, + tests::test_parse_lemmy_item, + }, + }; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_create_or_update() { + test_parse_lemmy_item::( + "assets/lemmy/activities/create_or_update/create_page.json", + ); + test_parse_lemmy_item::( + "assets/lemmy/activities/create_or_update/update_page.json", + ); + test_parse_lemmy_item::( + "assets/lemmy/activities/create_or_update/create_note.json", + ); + } + + #[actix_rt::test] + #[serial] + async fn test_parse_pleroma_create_or_update() { + file_to_json_object::("assets/pleroma/activities/create_note.json"); + } +} diff --git a/crates/apub/src/protocol/activities/deletion/mod.rs b/crates/apub/src/protocol/activities/deletion/mod.rs index b440edd6..c77492a3 100644 --- a/crates/apub/src/protocol/activities/deletion/mod.rs +++ b/crates/apub/src/protocol/activities/deletion/mod.rs @@ -1,2 +1,21 @@ pub mod delete; pub mod undo_delete; + +#[cfg(test)] +mod tests { + use crate::protocol::{ + activities::deletion::{delete::Delete, undo_delete::UndoDelete}, + tests::test_parse_lemmy_item, + }; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_voting() { + test_parse_lemmy_item::("assets/lemmy/activities/deletion/remove_note.json"); + test_parse_lemmy_item::("assets/lemmy/activities/deletion/delete_page.json"); + + test_parse_lemmy_item::("assets/lemmy/activities/deletion/undo_remove_note.json"); + test_parse_lemmy_item::("assets/lemmy/activities/deletion/undo_delete_page.json"); + } +} diff --git a/crates/apub/src/protocol/activities/following/mod.rs b/crates/apub/src/protocol/activities/following/mod.rs index 5dc6a3f8..473dfca3 100644 --- a/crates/apub/src/protocol/activities/following/mod.rs +++ b/crates/apub/src/protocol/activities/following/mod.rs @@ -1,3 +1,26 @@ pub(crate) mod accept; pub mod follow; pub mod undo_follow; + +#[cfg(test)] +mod tests { + use crate::protocol::{ + activities::following::{ + accept::AcceptFollowCommunity, + follow::FollowCommunity, + undo_follow::UndoFollowCommunity, + }, + tests::test_parse_lemmy_item, + }; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_accept_follow() { + test_parse_lemmy_item::("assets/lemmy/activities/following/follow.json"); + test_parse_lemmy_item::("assets/lemmy/activities/following/accept.json"); + test_parse_lemmy_item::( + "assets/lemmy/activities/following/undo_follow.json", + ); + } +} diff --git a/crates/apub/src/protocol/activities/private_message/mod.rs b/crates/apub/src/protocol/activities/private_message/mod.rs index 4bcda7c7..b54e66c0 100644 --- a/crates/apub/src/protocol/activities/private_message/mod.rs +++ b/crates/apub/src/protocol/activities/private_message/mod.rs @@ -1,3 +1,30 @@ pub mod create_or_update; pub mod delete; pub mod undo_delete; + +#[cfg(test)] +mod tests { + use crate::protocol::{ + activities::private_message::{ + create_or_update::CreateOrUpdatePrivateMessage, + delete::DeletePrivateMessage, + undo_delete::UndoDeletePrivateMessage, + }, + tests::test_parse_lemmy_item, + }; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_private_message() { + test_parse_lemmy_item::( + "assets/lemmy/activities/private_message/create.json", + ); + test_parse_lemmy_item::( + "assets/lemmy/activities/private_message/delete.json", + ); + test_parse_lemmy_item::( + "assets/lemmy/activities/private_message/undo_delete.json", + ); + } +} diff --git a/crates/apub/src/protocol/activities/voting/mod.rs b/crates/apub/src/protocol/activities/voting/mod.rs index 4583c231..48e30f0b 100644 --- a/crates/apub/src/protocol/activities/voting/mod.rs +++ b/crates/apub/src/protocol/activities/voting/mod.rs @@ -1,2 +1,21 @@ pub mod undo_vote; pub mod vote; + +#[cfg(test)] +mod tests { + use crate::protocol::{ + activities::voting::{undo_vote::UndoVote, vote::Vote}, + tests::test_parse_lemmy_item, + }; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_voting() { + test_parse_lemmy_item::("assets/lemmy/activities/voting/like_note.json"); + test_parse_lemmy_item::("assets/lemmy/activities/voting/dislike_page.json"); + + test_parse_lemmy_item::("assets/lemmy/activities/voting/undo_like_note.json"); + test_parse_lemmy_item::("assets/lemmy/activities/voting/undo_dislike_page.json"); + } +} diff --git a/crates/apub/src/protocol/collections/group_followers.rs b/crates/apub/src/protocol/collections/group_followers.rs index 41155b19..d3df5e22 100644 --- a/crates/apub/src/protocol/collections/group_followers.rs +++ b/crates/apub/src/protocol/collections/group_followers.rs @@ -10,25 +10,25 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct CommunityFollowers { +pub(crate) struct GroupFollowers { id: Url, r#type: CollectionType, total_items: i32, items: Vec<()>, } -impl CommunityFollowers { +impl GroupFollowers { pub(crate) async fn new( community: Community, context: &LemmyContext, - ) -> Result { + ) -> Result { let community_id = community.id; let community_followers = blocking(context.pool(), move |conn| { CommunityFollowerView::for_community(conn, community_id) }) .await??; - Ok(CommunityFollowers { + Ok(GroupFollowers { id: generate_followers_url(&community.actor_id)?.into_inner(), r#type: CollectionType::Collection, total_items: community_followers.len() as i32, diff --git a/crates/apub/src/protocol/collections/mod.rs b/crates/apub/src/protocol/collections/mod.rs index 646abbeb..2e2d5c20 100644 --- a/crates/apub/src/protocol/collections/mod.rs +++ b/crates/apub/src/protocol/collections/mod.rs @@ -2,3 +2,27 @@ pub(crate) mod group_followers; pub(crate) mod group_moderators; pub(crate) mod group_outbox; pub(crate) mod person_outbox; + +#[cfg(test)] +mod tests { + use crate::protocol::{ + collections::{ + group_followers::GroupFollowers, + group_moderators::GroupModerators, + group_outbox::GroupOutbox, + person_outbox::PersonOutbox, + }, + tests::test_parse_lemmy_item, + }; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_collections() { + test_parse_lemmy_item::("assets/lemmy/collections/group_followers.json"); + let outbox = test_parse_lemmy_item::("assets/lemmy/collections/group_outbox.json"); + assert_eq!(outbox.ordered_items.len() as i32, outbox.total_items); + test_parse_lemmy_item::("assets/lemmy/collections/group_moderators.json"); + test_parse_lemmy_item::("assets/lemmy/collections/person_outbox.json"); + } +} diff --git a/crates/apub/src/protocol/collections/person_outbox.rs b/crates/apub/src/protocol/collections/person_outbox.rs index 59c6975e..6ec75840 100644 --- a/crates/apub/src/protocol/collections/person_outbox.rs +++ b/crates/apub/src/protocol/collections/person_outbox.rs @@ -7,16 +7,16 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct UserOutbox { +pub(crate) struct PersonOutbox { r#type: OrderedCollectionType, id: Url, ordered_items: Vec<()>, total_items: i32, } -impl UserOutbox { - pub(crate) async fn new(user: Person) -> Result { - Ok(UserOutbox { +impl PersonOutbox { + pub(crate) async fn new(user: Person) -> Result { + Ok(PersonOutbox { r#type: OrderedCollectionType::OrderedCollection, id: generate_outbox_url(&user.actor_id)?.into_inner(), ordered_items: vec![], diff --git a/crates/apub/src/protocol/mod.rs b/crates/apub/src/protocol/mod.rs index f587dba7..22df0354 100644 --- a/crates/apub/src/protocol/mod.rs +++ b/crates/apub/src/protocol/mod.rs @@ -22,3 +22,20 @@ pub struct ImageObject { pub(crate) kind: ImageType, pub(crate) url: Url, } + +#[cfg(test)] +pub(crate) mod tests { + use crate::objects::tests::file_to_json_object; + use assert_json_diff::assert_json_include; + use serde::{de::DeserializeOwned, Serialize}; + use std::collections::HashMap; + + pub(crate) fn test_parse_lemmy_item(path: &str) -> T { + let parsed = file_to_json_object::(path); + + // ensure that no field is ignored when parsing + let raw = file_to_json_object::>(path); + assert_json_include!(actual: &parsed, expected: raw); + parsed + } +} diff --git a/crates/apub/src/protocol/objects/mod.rs b/crates/apub/src/protocol/objects/mod.rs index 3e133831..529ee637 100644 --- a/crates/apub/src/protocol/objects/mod.rs +++ b/crates/apub/src/protocol/objects/mod.rs @@ -2,4 +2,24 @@ pub(crate) mod chat_message; pub(crate) mod group; pub(crate) mod note; pub(crate) mod page; +pub(crate) mod person; pub(crate) mod tombstone; + +#[cfg(test)] +mod tests { + use crate::protocol::{ + objects::{chat_message::ChatMessage, group::Group, note::Note, page::Page, person::Person}, + tests::test_parse_lemmy_item, + }; + use serial_test::serial; + + #[actix_rt::test] + #[serial] + async fn test_parse_lemmy_object() { + test_parse_lemmy_item::("assets/lemmy/objects/person.json"); + test_parse_lemmy_item::("assets/lemmy/objects/group.json"); + test_parse_lemmy_item::("assets/lemmy/objects/page.json"); + test_parse_lemmy_item::("assets/lemmy/objects/note.json"); + test_parse_lemmy_item::("assets/lemmy/objects/chat_message.json"); + } +} diff --git a/crates/apub/src/protocol/objects/person.rs b/crates/apub/src/protocol/objects/person.rs new file mode 100644 index 00000000..2aecf945 --- /dev/null +++ b/crates/apub/src/protocol/objects/person.rs @@ -0,0 +1,41 @@ +use crate::protocol::{ImageObject, Source}; +use activitystreams::{actor::Endpoints, unparsed::Unparsed, url::Url}; +use chrono::{DateTime, FixedOffset}; +use lemmy_apub_lib::signatures::PublicKey; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; + +#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] +pub enum UserTypes { + Person, + Service, +} + +#[skip_serializing_none] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Person { + #[serde(rename = "type")] + pub(crate) kind: UserTypes, + pub(crate) id: Url, + /// username, set at account creation and can never be changed + pub(crate) preferred_username: String, + /// displayname (can be changed at any time) + pub(crate) name: Option, + pub(crate) summary: Option, + pub(crate) source: Option, + /// user avatar + pub(crate) icon: Option, + /// user banner + pub(crate) image: Option, + pub(crate) matrix_user_id: Option, + pub(crate) inbox: Url, + /// mandatory field in activitypub, currently empty in lemmy + pub(crate) outbox: Url, + pub(crate) endpoints: Endpoints, + pub(crate) public_key: PublicKey, + pub(crate) published: Option>, + pub(crate) updated: Option>, + #[serde(flatten)] + pub(crate) unparsed: Unparsed, +} diff --git a/crates/db_schema/src/impls/activity.rs b/crates/db_schema/src/impls/activity.rs index 4f1cfd2d..5efb3b23 100644 --- a/crates/db_schema/src/impls/activity.rs +++ b/crates/db_schema/src/impls/activity.rs @@ -1,7 +1,6 @@ use crate::{newtypes::DbUrl, source::activity::*, traits::Crud}; -use diesel::{dsl::*, result::Error, sql_types::Text, *}; +use diesel::{dsl::*, result::Error, *}; use serde::Serialize; -use serde_json::Value; use std::{ fmt::Debug, io::{Error as IoError, ErrorKind}, @@ -75,26 +74,6 @@ impl Activity { use crate::schema::activity::dsl::*; diesel::delete(activity.filter(published.lt(now - 6.months()))).execute(conn) } - - pub fn read_community_outbox( - conn: &PgConnection, - community_actor_id: &DbUrl, - ) -> Result, Error> { - use crate::schema::activity::dsl::*; - let res: Vec = activity - .select(data) - .filter( - sql("activity.data ->> 'type' = 'Announce'") - .sql(" AND activity.data -> 'object' ->> 'type' = 'Create'") - .sql(" AND activity.data -> 'object' -> 'object' ->> 'type' = 'Page'") - .sql(" AND activity.data ->> 'actor' = ") - .bind::(community_actor_id) - .sql(" ORDER BY activity.published DESC"), - ) - .limit(20) - .get_results(conn)?; - Ok(res) - } } #[cfg(test)] diff --git a/docker/federation/docker-compose.yml b/docker/federation/docker-compose.yml index 3a8447c1..bcd4f914 100644 --- a/docker/federation/docker-compose.yml +++ b/docker/federation/docker-compose.yml @@ -28,7 +28,7 @@ services: - ./volumes/pictrs_alpha:/mnt lemmy-alpha-ui: - image: dessalines/lemmy-ui:dev + image: dessalines/lemmy-ui:0.13.3 environment: - LEMMY_INTERNAL_HOST=lemmy-alpha:8541 - LEMMY_EXTERNAL_HOST=localhost:8541 @@ -57,7 +57,7 @@ services: - ./volumes/postgres_alpha:/var/lib/postgresql/data lemmy-beta-ui: - image: dessalines/lemmy-ui:dev + image: dessalines/lemmy-ui:0.13.3 environment: - LEMMY_INTERNAL_HOST=lemmy-beta:8551 - LEMMY_EXTERNAL_HOST=localhost:8551 From 1c6f74cad524c5ab49201d19df8c671761a1ddff Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 2 Nov 2021 12:49:28 +0100 Subject: [PATCH 15/17] Convert note.content and chat_message.content to html (fixes #1871) --- crates/apub/assets/lemmy/objects/chat_message.json | 2 +- crates/apub/assets/lemmy/objects/note.json | 2 +- crates/apub/src/objects/comment.rs | 3 ++- crates/apub/src/objects/private_message.rs | 7 +++++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/apub/assets/lemmy/objects/chat_message.json b/crates/apub/assets/lemmy/objects/chat_message.json index 45790231..c639aef9 100644 --- a/crates/apub/assets/lemmy/objects/chat_message.json +++ b/crates/apub/assets/lemmy/objects/chat_message.json @@ -5,7 +5,7 @@ "to": [ "https://queer.hacktivis.me/users/lanodan" ], - "content": "Hello hello, testing", + "content": "

Hello hello, testing

\n", "mediaType": "text/html", "source": { "content": "Hello hello, testing", diff --git a/crates/apub/assets/lemmy/objects/note.json b/crates/apub/assets/lemmy/objects/note.json index 2a20be29..c353d0b2 100644 --- a/crates/apub/assets/lemmy/objects/note.json +++ b/crates/apub/assets/lemmy/objects/note.json @@ -4,7 +4,7 @@ "attributedTo": "https://enterprise.lemmy.ml/u/picard", "to": ["https://www.w3.org/ns/activitystreams#Public"], "inReplyTo": "https://enterprise.lemmy.ml/post/55143", - "content": "first comment!", + "content": "

first comment!

\n", "mediaType": "text/html", "source": { "content": "first comment!", diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index b5f72ca3..3e3d10df 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -38,6 +38,7 @@ use crate::{ }, PostOrComment, }; +use lemmy_utils::utils::markdown_to_html; #[derive(Clone, Debug)] pub struct ApubComment(Comment); @@ -106,7 +107,7 @@ impl ApubObject for ApubComment { id: self.ap_id.to_owned().into_inner(), attributed_to: ObjectId::new(creator.actor_id), to: vec![public()], - content: self.content.clone(), + content: markdown_to_html(&self.content), media_type: Some(MediaTypeHtml::Html), source: SourceCompat::Lemmy(Source { content: self.content.clone(), diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 678435ee..81615fdc 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -19,7 +19,10 @@ use lemmy_db_schema::{ }, traits::Crud, }; -use lemmy_utils::{utils::convert_datetime, LemmyError}; +use lemmy_utils::{ + utils::{convert_datetime, markdown_to_html}, + LemmyError, +}; use lemmy_websocket::LemmyContext; use std::ops::Deref; use url::Url; @@ -81,7 +84,7 @@ impl ApubObject for ApubPrivateMessage { id: self.ap_id.clone().into(), attributed_to: ObjectId::new(creator.actor_id), to: [ObjectId::new(recipient.actor_id)], - content: self.content.clone(), + content: markdown_to_html(&self.content), media_type: Some(MediaTypeHtml::Html), source: Some(Source { content: self.content.clone(), From b396344eaeec167e21077d58dc1d18c22e0b105e Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 2 Nov 2021 14:02:39 +0100 Subject: [PATCH 16/17] Merge two functions into one --- .../apub/src/activities/community/announce.rs | 12 ++--- crates/apub/src/activities/community/mod.rs | 34 ++------------ crates/apub/src/objects/community.rs | 44 ++++++++++--------- 3 files changed, 31 insertions(+), 59 deletions(-) diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 62e7c56f..f560b09f 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -1,11 +1,5 @@ use crate::{ - activities::{ - community::list_community_follower_inboxes, - generate_activity_id, - send_lemmy_activity, - verify_activity, - verify_is_public, - }, + activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_is_public}, activity_lists::AnnouncableActivities, fetcher::object_id::ObjectId, http::is_activity_already_known, @@ -50,7 +44,9 @@ impl AnnounceActivity { )?, unparsed: Default::default(), }; - let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?; + let inboxes = community + .get_follower_inboxes(additional_inboxes, context) + .await?; send_lemmy_activity(context, &announce, &announce.id, community, inboxes, false).await } } diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index ebc6e7f9..96e692d5 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -1,19 +1,15 @@ -use itertools::Itertools; -use url::Url; - -use lemmy_apub_lib::traits::ActorType; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; - use crate::{ activities::send_lemmy_activity, activity_lists::AnnouncableActivities, - check_is_apub_id_valid, fetcher::object_id::ObjectId, insert_activity, objects::community::ApubCommunity, protocol::activities::community::announce::AnnounceActivity, }; +use lemmy_apub_lib::traits::ActorType; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use url::Url; pub mod add_mod; pub mod announce; @@ -23,28 +19,6 @@ pub mod report; pub mod undo_block_user; pub mod update; -async fn list_community_follower_inboxes( - community: &ApubCommunity, - additional_inboxes: Vec, - context: &LemmyContext, -) -> Result, LemmyError> { - Ok( - vec![ - community - .get_follower_inboxes(context.pool(), &context.settings()) - .await?, - additional_inboxes, - ] - .iter() - .flatten() - .unique() - .filter(|inbox| inbox.host_str() != Some(&context.settings().hostname)) - .filter(|inbox| check_is_apub_id_valid(inbox, false, &context.settings()).is_ok()) - .map(|inbox| inbox.to_owned()) - .collect(), - ) -} - pub(crate) async fn send_to_community( activity: AnnouncableActivities, activity_id: &Url, diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index 89258fe0..0947ebf3 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -1,5 +1,3 @@ -use std::ops::Deref; - use activitystreams::{ actor::{kind::GroupType, Endpoints}, object::kind::ImageType, @@ -7,22 +5,9 @@ use activitystreams::{ use chrono::NaiveDateTime; use itertools::Itertools; use log::debug; +use std::ops::Deref; use url::Url; -use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - traits::{ActorType, ApubObject}, - values::MediaTypeMarkdown, -}; -use lemmy_db_schema::{source::community::Community, DbPool}; -use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; -use lemmy_utils::{ - settings::structs::Settings, - utils::{convert_datetime, markdown_to_html}, - LemmyError, -}; -use lemmy_websocket::LemmyContext; - use crate::{ check_is_apub_id_valid, collections::{community_moderators::ApubCommunityModerators, CommunityContext}, @@ -35,6 +20,18 @@ use crate::{ Source, }, }; +use lemmy_api_common::blocking; +use lemmy_apub_lib::{ + traits::{ActorType, ApubObject}, + values::MediaTypeMarkdown, +}; +use lemmy_db_schema::source::community::Community; +use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; +use lemmy_utils::{ + utils::{convert_datetime, markdown_to_html}, + LemmyError, +}; +use lemmy_websocket::LemmyContext; #[derive(Clone, Debug)] pub struct ApubCommunity(Community); @@ -198,23 +195,28 @@ impl ApubCommunity { /// For a given community, returns the inboxes of all followers. pub(crate) async fn get_follower_inboxes( &self, - pool: &DbPool, - settings: &Settings, + additional_inboxes: Vec, + context: &LemmyContext, ) -> Result, LemmyError> { let id = self.id; - let follows = blocking(pool, move |conn| { + let follows = blocking(context.pool(), move |conn| { CommunityFollowerView::for_community(conn, id) }) .await??; - let inboxes = follows + let follower_inboxes: Vec = follows .into_iter() .filter(|f| !f.follower.local) .map(|f| f.follower.shared_inbox_url.unwrap_or(f.follower.inbox_url)) .map(|i| i.into_inner()) + .collect(); + let inboxes = vec![follower_inboxes, additional_inboxes] + .into_iter() + .flatten() .unique() + .filter(|inbox| inbox.host_str() != Some(&context.settings().hostname)) // Don't send to blocked instances - .filter(|inbox| check_is_apub_id_valid(inbox, false, settings).is_ok()) + .filter(|inbox| check_is_apub_id_valid(inbox, false, &context.settings()).is_ok()) .collect(); Ok(inboxes) From 41f7bcc0d24739c210ec389d6c6759761bec56ae Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 2 Nov 2021 14:18:12 +0100 Subject: [PATCH 17/17] Correctly use and document check_is_apub_id_valid() param use_strict_allowlist --- crates/apub/src/lib.rs | 2 ++ crates/apub/src/objects/comment.rs | 2 ++ crates/apub/src/objects/post.rs | 2 ++ crates/apub/src/protocol/objects/group.rs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index f38a9f86..75d7a62f 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -30,6 +30,8 @@ use url::{ParseError, Url}; /// - URL being in the allowlist (if it is active) /// - URL not being in the blocklist (if it is active) /// +/// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a +/// post/comment in a local community. pub(crate) fn check_is_apub_id_valid( apub_id: &Url, use_strict_allowlist: bool, diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 3e3d10df..e5ffb7ca 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -28,6 +28,7 @@ use lemmy_websocket::LemmyContext; use crate::{ activities::verify_person_in_community, + check_is_apub_id_valid, fetcher::object_id::ObjectId, protocol::{ objects::{ @@ -149,6 +150,7 @@ impl ApubObject for ApubComment { Community::read(conn, community_id) }) .await??; + check_is_apub_id_valid(¬e.id, community.local, &context.settings())?; verify_person_in_community( ¬e.attributed_to, &community.into(), diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 7f142be5..c19c6277 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -1,5 +1,6 @@ use crate::{ activities::verify_person_in_community, + check_is_apub_id_valid, fetcher::object_id::ObjectId, protocol::{ objects::{page::Page, tombstone::Tombstone}, @@ -148,6 +149,7 @@ impl ApubObject for ApubPost { .dereference(context, request_counter) .await?; let community = page.extract_community(context, request_counter).await?; + check_is_apub_id_valid(&page.id, community.local, &context.settings())?; verify_person_in_community(&page.attributed_to, &community, context, request_counter).await?; let thumbnail_url: Option = page.image.clone().map(|i| i.url); diff --git a/crates/apub/src/protocol/objects/group.rs b/crates/apub/src/protocol/objects/group.rs index 94587890..4da987a2 100644 --- a/crates/apub/src/protocol/objects/group.rs +++ b/crates/apub/src/protocol/objects/group.rs @@ -1,4 +1,5 @@ use crate::{ + check_is_apub_id_valid, collections::{ community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, @@ -60,6 +61,7 @@ impl Group { expected_domain: &Url, settings: &Settings, ) -> Result { + check_is_apub_id_valid(&group.id, true, settings)?; verify_domains_match(expected_domain, &group.id)?; let name = group.preferred_username.clone(); let title = group.name.clone();