From a5a674a270f0b9daf8519b5dd30c83fc2c8c3488 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Sat, 6 Nov 2021 18:35:14 +0100 Subject: [PATCH] Add method ApubObject.verify() --- .../activities/comment/create_or_update.rs | 7 +-- .../apub/src/activities/community/update.rs | 12 ++-- .../src/activities/post/create_or_update.rs | 6 +- .../private_message/create_or_update.rs | 5 +- .../src/collections/community_moderators.rs | 17 +++++- .../apub/src/collections/community_outbox.rs | 12 +++- crates/apub/src/fetcher/post_or_comment.rs | 25 +++++--- crates/apub/src/fetcher/search.rs | 31 ++++++++-- crates/apub/src/fetcher/user_or_community.rs | 29 +++++++--- crates/apub/src/objects/comment.rs | 57 +++++++++++++------ crates/apub/src/objects/community.rs | 17 +++++- crates/apub/src/objects/person.rs | 54 ++++++++++-------- crates/apub/src/objects/post.rs | 34 ++++++++--- crates/apub/src/objects/private_message.rs | 44 +++++++++++--- .../apub/src/protocol/objects/chat_message.rs | 23 +------- crates/apub/src/protocol/objects/group.rs | 34 +++++------ crates/apub/src/protocol/objects/note.rs | 34 +---------- crates/apub/src/protocol/objects/page.rs | 19 +------ crates/apub_lib/src/object_id.rs | 3 +- crates/apub_lib/src/traits.rs | 8 ++- 20 files changed, 282 insertions(+), 189 deletions(-) diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 0095a511..1ba2c8bb 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -85,9 +85,7 @@ impl ActivityHandler for CreateOrUpdateComment { check_community_deleted_or_removed(&community)?; check_post_deleted_or_removed(&post)?; - // TODO: should add a check that the correct community is in cc (probably needs changes to - // comment deserialization) - self.object.verify(context, request_counter).await?; + ApubComment::verify(&self.object, self.actor.inner(), context, request_counter).await?; Ok(()) } @@ -96,8 +94,7 @@ impl ActivityHandler for CreateOrUpdateComment { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let comment = - ApubComment::from_apub(self.object, context, self.actor.inner(), request_counter).await?; + let comment = ApubComment::from_apub(self.object, context, request_counter).await?; let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?; let notif_type = match self.kind { CreateOrUpdateType::Create => UserOperationCrud::CreateComment, diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index f5d7e565..eebfe336 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -63,6 +63,13 @@ impl ActivityHandler for UpdateCommunity { 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, &community, context, request_counter).await?; + ApubCommunity::verify( + &self.object, + &community.actor_id.clone().into(), + context, + request_counter, + ) + .await?; Ok(()) } @@ -73,10 +80,7 @@ impl ActivityHandler for UpdateCommunity { ) -> Result<(), LemmyError> { let community = self.get_community(context, request_counter).await?; - let updated_community = self - .object - .into_form(&community.actor_id.clone().into(), &context.settings()) - .await?; + let updated_community = self.object.into_form()?; let cf = CommunityForm { name: updated_community.name, title: updated_community.title, diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index 1c71c8ec..db5d725f 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -104,7 +104,7 @@ impl ActivityHandler for CreateOrUpdatePost { } } } - self.object.verify(context, request_counter).await?; + ApubPost::verify(&self.object, self.actor.inner(), context, request_counter).await?; Ok(()) } @@ -113,9 +113,7 @@ impl ActivityHandler for CreateOrUpdatePost { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = self.actor.dereference(context, request_counter).await?; - let post = - ApubPost::from_apub(self.object, context, &actor.actor_id(), request_counter).await?; + let post = ApubPost::from_apub(self.object, context, request_counter).await?; let notif_type = match self.kind { CreateOrUpdateType::Create => UserOperationCrud::CreatePost, 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 7f40b5fb..e3d72f54 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -57,7 +57,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage { verify_activity(&self.id, self.actor.inner(), &context.settings())?; verify_person(&self.actor, context, request_counter).await?; verify_domains_match(self.actor.inner(), self.object.id.inner())?; - self.object.verify(context, request_counter).await?; + ApubPrivateMessage::verify(&self.object, self.actor.inner(), context, request_counter).await?; Ok(()) } @@ -67,8 +67,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage { request_counter: &mut i32, ) -> Result<(), LemmyError> { let private_message = - ApubPrivateMessage::from_apub(self.object, context, self.actor.inner(), request_counter) - .await?; + ApubPrivateMessage::from_apub(self.object, context, request_counter).await?; let notif_type = match self.kind { CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage, diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index cbbe6670..695f7aca 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -66,13 +66,21 @@ impl ApubObject for ApubCommunityModerators { unimplemented!() } + async fn verify( + group_moderators: &GroupModerators, + expected_domain: &Url, + _context: &CommunityContext, + _request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_domains_match(&group_moderators.id, expected_domain)?; + Ok(()) + } + async fn from_apub( apub: Self::ApubType, data: &Self::DataType, - expected_domain: &Url, request_counter: &mut i32, ) -> Result { - verify_domains_match(expected_domain, &apub.id)?; let community_id = data.0.id; let current_moderators = blocking(data.1.pool(), move |conn| { CommunityModeratorView::for_community(conn, community_id) @@ -165,7 +173,10 @@ mod tests { 0: community, 1: context, }; - ApubCommunityModerators::from_apub(json, &community_context, &url, &mut request_counter) + ApubCommunityModerators::verify(&json, &url, &community_context, &mut request_counter) + .await + .unwrap(); + ApubCommunityModerators::from_apub(json, &community_context, &mut request_counter) .await .unwrap(); assert_eq!(request_counter, 0); diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index cbad5c48..3632f61a 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -85,13 +85,21 @@ impl ApubObject for ApubCommunityOutbox { unimplemented!() } + async fn verify( + group_outbox: &GroupOutbox, + expected_domain: &Url, + _context: &CommunityContext, + _request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_domains_match(expected_domain, &group_outbox.id)?; + Ok(()) + } + async fn from_apub( apub: Self::ApubType, data: &Self::DataType, - expected_domain: &Url, request_counter: &mut i32, ) -> Result { - verify_domains_match(expected_domain, &apub.id)?; let mut outbox_activities = apub.ordered_items; if outbox_activities.len() > 20 { outbox_activities = outbox_activities[0..20].to_vec(); diff --git a/crates/apub/src/fetcher/post_or_comment.rs b/crates/apub/src/fetcher/post_or_comment.rs index dc1546f9..2ca84bee 100644 --- a/crates/apub/src/fetcher/post_or_comment.rs +++ b/crates/apub/src/fetcher/post_or_comment.rs @@ -61,19 +61,30 @@ impl ApubObject for PostOrComment { unimplemented!() } + async fn verify( + apub: &Self::ApubType, + expected_domain: &Url, + data: &Self::DataType, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + match apub { + PageOrNote::Page(a) => ApubPost::verify(a, expected_domain, data, request_counter).await, + PageOrNote::Note(a) => ApubComment::verify(a, expected_domain, data, request_counter).await, + } + } + async fn from_apub( apub: PageOrNote, context: &LemmyContext, - expected_domain: &Url, request_counter: &mut i32, ) -> Result { Ok(match apub { - PageOrNote::Page(p) => PostOrComment::Post( - ApubPost::from_apub(p, context, expected_domain, request_counter).await?, - ), - PageOrNote::Note(n) => PostOrComment::Comment( - ApubComment::from_apub(n, context, expected_domain, request_counter).await?, - ), + PageOrNote::Page(p) => { + PostOrComment::Post(ApubPost::from_apub(p, context, request_counter).await?) + } + PageOrNote::Note(n) => { + PostOrComment::Comment(ApubComment::from_apub(n, context, request_counter).await?) + } }) } } diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index 557fc1b5..1c03a10b 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -163,19 +163,40 @@ impl ApubObject for SearchableObjects { unimplemented!() } + async fn verify( + apub: &Self::ApubType, + expected_domain: &Url, + data: &Self::DataType, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + match apub { + SearchableApubTypes::Group(a) => { + ApubCommunity::verify(a, expected_domain, data, request_counter).await + } + SearchableApubTypes::Person(a) => { + ApubPerson::verify(a, expected_domain, data, request_counter).await + } + SearchableApubTypes::Page(a) => { + ApubPost::verify(a, expected_domain, data, request_counter).await + } + SearchableApubTypes::Note(a) => { + ApubComment::verify(a, expected_domain, data, request_counter).await + } + } + } + async fn from_apub( apub: Self::ApubType, context: &LemmyContext, - ed: &Url, rc: &mut i32, ) -> Result { use SearchableApubTypes as SAT; use SearchableObjects as SO; Ok(match apub { - SAT::Group(g) => SO::Community(ApubCommunity::from_apub(g, context, ed, rc).await?), - SAT::Person(p) => SO::Person(ApubPerson::from_apub(p, context, ed, rc).await?), - SAT::Page(p) => SO::Post(ApubPost::from_apub(p, context, ed, rc).await?), - SAT::Note(n) => SO::Comment(ApubComment::from_apub(n, context, ed, rc).await?), + SAT::Group(g) => SO::Community(ApubCommunity::from_apub(g, context, rc).await?), + SAT::Person(p) => SO::Person(ApubPerson::from_apub(p, context, rc).await?), + SAT::Page(p) => SO::Post(ApubPost::from_apub(p, context, rc).await?), + SAT::Note(n) => SO::Comment(ApubComment::from_apub(n, context, rc).await?), }) } } diff --git a/crates/apub/src/fetcher/user_or_community.rs b/crates/apub/src/fetcher/user_or_community.rs index 5f7c388e..e5bc49ba 100644 --- a/crates/apub/src/fetcher/user_or_community.rs +++ b/crates/apub/src/fetcher/user_or_community.rs @@ -62,19 +62,34 @@ impl ApubObject for UserOrCommunity { unimplemented!() } + async fn verify( + apub: &Self::ApubType, + expected_domain: &Url, + data: &Self::DataType, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + match apub { + PersonOrGroup::Person(a) => { + ApubPerson::verify(a, expected_domain, data, request_counter).await + } + PersonOrGroup::Group(a) => { + ApubCommunity::verify(a, expected_domain, data, request_counter).await + } + } + } + async fn from_apub( apub: Self::ApubType, data: &Self::DataType, - expected_domain: &Url, request_counter: &mut i32, ) -> Result { Ok(match apub { - PersonOrGroup::Person(p) => UserOrCommunity::User( - ApubPerson::from_apub(p, data, expected_domain, request_counter).await?, - ), - PersonOrGroup::Group(p) => UserOrCommunity::Community( - ApubCommunity::from_apub(p, data, expected_domain, request_counter).await?, - ), + PersonOrGroup::Person(p) => { + UserOrCommunity::User(ApubPerson::from_apub(p, data, request_counter).await?) + } + PersonOrGroup::Group(p) => { + UserOrCommunity::Community(ApubCommunity::from_apub(p, data, request_counter).await?) + } }) } } diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index da96f8fa..d0f74acf 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -1,5 +1,5 @@ use crate::{ - activities::verify_person_in_community, + activities::{verify_is_public, verify_person_in_community}, check_is_apub_id_valid, protocol::{ objects::{ @@ -129,21 +129,16 @@ impl ApubObject for ApubComment { )) } - /// Converts a `Note` to `Comment`. - /// - /// If the parent community, post and comment(s) are not known locally, these are also fetched. - async fn from_apub( - note: Note, - context: &LemmyContext, + async fn verify( + note: &Note, expected_domain: &Url, + context: &LemmyContext, request_counter: &mut i32, - ) -> Result { + ) -> Result<(), LemmyError> { verify_domains_match(note.id.inner(), expected_domain)?; - let creator = note - .attributed_to - .dereference(context, request_counter) - .await?; - let (post, parent_comment_id) = note.get_parents(context, request_counter).await?; + verify_domains_match(note.attributed_to.inner(), note.id.inner())?; + verify_is_public(¬e.to)?; + let (post, _) = 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) @@ -160,6 +155,22 @@ impl ApubObject for ApubComment { if post.locked { return Err(anyhow!("Post is locked").into()); } + Ok(()) + } + + /// Converts a `Note` to `Comment`. + /// + /// If the parent community, post and comment(s) are not known locally, these are also fetched. + async fn from_apub( + note: Note, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { + let creator = note + .attributed_to + .dereference(context, request_counter) + .await?; + let (post, parent_comment_id) = note.get_parents(context, request_counter).await?; let content = if let SourceCompat::Lemmy(source) = ¬e.source { source.content.clone() @@ -205,7 +216,10 @@ pub(crate) mod tests { 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) + ApubPost::verify(&post_json, url, context, &mut 0) + .await + .unwrap(); + let post = ApubPost::from_apub(post_json, context, &mut 0) .await .unwrap(); (person, community, post) @@ -226,7 +240,10 @@ pub(crate) mod tests { let json: Note = file_to_json_object("assets/lemmy/objects/note.json"); let mut request_counter = 0; - let comment = ApubComment::from_apub(json.clone(), &context, &url, &mut request_counter) + ApubComment::verify(&json, &url, &context, &mut request_counter) + .await + .unwrap(); + let comment = ApubComment::from_apub(json.clone(), &context, &mut request_counter) .await .unwrap(); @@ -254,12 +271,18 @@ pub(crate) mod tests { Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2") .unwrap(); let person_json = file_to_json_object("assets/pleroma/objects/person.json"); - ApubPerson::from_apub(person_json, &context, &pleroma_url, &mut 0) + ApubPerson::verify(&person_json, &pleroma_url, &context, &mut 0) + .await + .unwrap(); + ApubPerson::from_apub(person_json, &context, &mut 0) .await .unwrap(); 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) + ApubComment::verify(&json, &pleroma_url, &context, &mut request_counter) + .await + .unwrap(); + let comment = ApubComment::from_apub(json, &context, &mut request_counter) .await .unwrap(); diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index f6737a3a..21ca9d4f 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -124,14 +124,22 @@ impl ApubObject for ApubCommunity { )) } + async fn verify( + group: &Group, + expected_domain: &Url, + context: &LemmyContext, + _request_counter: &mut i32, + ) -> Result<(), LemmyError> { + group.verify(expected_domain, context).await + } + /// Converts a `Group` to `Community`, inserts it into the database and updates moderators. async fn from_apub( group: Group, context: &LemmyContext, - expected_domain: &Url, request_counter: &mut i32, ) -> Result { - let form = Group::into_form(group.clone(), expected_domain, &context.settings()).await?; + let form = Group::into_form(group.clone())?; // Fetching mods and outbox is not necessary for Lemmy to work, so ignore errors. Besides, // we need to ignore these errors so that tests can work entirely offline. @@ -232,7 +240,10 @@ pub(crate) mod tests { 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) + ApubCommunity::verify(&json, &url, context, &mut request_counter) + .await + .unwrap(); + let community = ApubCommunity::from_apub(json, context, &mut request_counter) .await .unwrap(); // this makes two requests to the (intentionally) broken outbox/moderators collections diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index ea088321..e3fcdd2e 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -125,32 +125,31 @@ impl ApubObject for ApubPerson { unimplemented!() } - async fn from_apub( - person: Person, - context: &LemmyContext, + async fn verify( + person: &Person, expected_domain: &Url, + context: &LemmyContext, _request_counter: &mut i32, - ) -> Result { + ) -> Result<(), LemmyError> { verify_domains_match(person.id.inner(), expected_domain)?; - let name = person.preferred_username; - let display_name: Option = person.name; - let bio = get_summary_from_string_or_source(&person.summary, &person.source); - let shared_inbox = person.endpoints.shared_inbox.map(|s| s.into()); - let bot_account = match person.kind { - UserTypes::Person => false, - UserTypes::Service => true, - }; + check_is_apub_id_valid(person.id.inner(), false, &context.settings())?; let slur_regex = &context.settings().slur_regex(); - check_slurs(&name, slur_regex)?; - check_slurs_opt(&display_name, slur_regex)?; + check_slurs(&person.preferred_username, slur_regex)?; + check_slurs_opt(&person.name, slur_regex)?; + let bio = get_summary_from_string_or_source(&person.summary, &person.source); check_slurs_opt(&bio, slur_regex)?; + Ok(()) + } - check_is_apub_id_valid(person.id.inner(), false, &context.settings())?; - + async fn from_apub( + person: Person, + context: &LemmyContext, + _request_counter: &mut i32, + ) -> Result { let person_form = PersonForm { - name, - display_name: Some(display_name), + name: person.preferred_username, + display_name: Some(person.name), banned: None, deleted: None, avatar: Some(person.icon.map(|i| i.url.into())), @@ -158,15 +157,18 @@ impl ApubObject for ApubPerson { published: person.published.map(|u| u.naive_local()), updated: person.updated.map(|u| u.naive_local()), actor_id: Some(person.id.into()), - bio: Some(bio), + bio: Some(get_summary_from_string_or_source( + &person.summary, + &person.source, + )), local: Some(false), admin: Some(false), - bot_account: Some(bot_account), + bot_account: Some(person.kind == UserTypes::Service), private_key: None, public_key: Some(Some(person.public_key.public_key_pem)), last_refreshed_at: Some(naive_now()), inbox_url: Some(person.inbox.into()), - shared_inbox_url: Some(shared_inbox), + shared_inbox_url: Some(person.endpoints.shared_inbox.map(|s| s.into())), matrix_user_id: Some(person.matrix_user_id), }; let person = blocking(context.pool(), move |conn| { @@ -210,7 +212,10 @@ pub(crate) mod tests { 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) + ApubPerson::verify(&json, &url, context, &mut request_counter) + .await + .unwrap(); + let person = ApubPerson::from_apub(json, context, &mut request_counter) .await .unwrap(); assert_eq!(request_counter, 0); @@ -238,7 +243,10 @@ pub(crate) mod tests { 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) + ApubPerson::verify(&json, &url, &context, &mut request_counter) + .await + .unwrap(); + let person = ApubPerson::from_apub(json, &context, &mut request_counter) .await .unwrap(); diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index bbb5537e..9e68cbb1 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -1,5 +1,5 @@ use crate::{ - activities::verify_person_in_community, + activities::{verify_is_public, verify_person_in_community}, check_is_apub_id_valid, protocol::{ objects::{page::Page, tombstone::Tombstone}, @@ -30,7 +30,7 @@ use lemmy_db_schema::{ }; use lemmy_utils::{ request::fetch_site_data, - utils::{convert_datetime, markdown_to_html, remove_slurs}, + utils::{check_slurs, convert_datetime, markdown_to_html, remove_slurs}, LemmyError, }; use lemmy_websocket::LemmyContext; @@ -133,24 +133,37 @@ impl ApubObject for ApubPost { )) } - async fn from_apub( - page: Page, - context: &LemmyContext, + async fn verify( + page: &Page, expected_domain: &Url, + context: &LemmyContext, request_counter: &mut i32, - ) -> Result { + ) -> Result<(), LemmyError> { // We can't verify the domain in case of mod action, because the mod may be on a different // instance from the post author. if !page.is_mod_action(context).await? { verify_domains_match(page.id.inner(), expected_domain)?; }; + + let community = page.extract_community(context, request_counter).await?; + check_is_apub_id_valid(page.id.inner(), community.local, &context.settings())?; + verify_person_in_community(&page.attributed_to, &community, context, request_counter).await?; + check_slurs(&page.name, &context.settings().slur_regex())?; + verify_domains_match(page.attributed_to.inner(), page.id.inner())?; + verify_is_public(&page.to.clone())?; + Ok(()) + } + + async fn from_apub( + page: Page, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result { let creator = page .attributed_to .dereference(context, request_counter) .await?; let community = page.extract_community(context, request_counter).await?; - check_is_apub_id_valid(page.id.inner(), community.local, &context.settings())?; - verify_person_in_community(&page.attributed_to, &community, context, request_counter).await?; let thumbnail_url: Option = page.image.map(|i| i.url); let (metadata_res, pictrs_thumbnail) = if let Some(url) = &page.url { @@ -212,7 +225,10 @@ mod tests { 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) + ApubPost::verify(&json, &url, &context, &mut request_counter) + .await + .unwrap(); + let post = ApubPost::from_apub(json, &context, &mut request_counter) .await .unwrap(); diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 334f37c5..30c8e4dc 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -2,6 +2,7 @@ use crate::protocol::{ objects::chat_message::{ChatMessage, ChatMessageType}, Source, }; +use anyhow::anyhow; use chrono::NaiveDateTime; use html2md::parse_html; use lemmy_api_common::blocking; @@ -100,14 +101,29 @@ impl ApubObject for ApubPrivateMessage { unimplemented!() } + async fn verify( + note: &ChatMessage, + expected_domain: &Url, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_domains_match(note.id.inner(), expected_domain)?; + verify_domains_match(note.attributed_to.inner(), note.id.inner())?; + let person = note + .attributed_to + .dereference(context, request_counter) + .await?; + if person.banned { + return Err(anyhow!("Person is banned from site").into()); + } + Ok(()) + } + async fn from_apub( note: ChatMessage, context: &LemmyContext, - expected_domain: &Url, request_counter: &mut i32, ) -> Result { - verify_domains_match(note.id.inner(), expected_domain)?; - let ap_id = Some(note.id.into()); let creator = note .attributed_to .dereference(context, request_counter) @@ -127,7 +143,7 @@ impl ApubObject for ApubPrivateMessage { updated: note.updated.map(|u| u.naive_local()), deleted: None, read: None, - ap_id, + ap_id: Some(note.id.into()), local: Some(false), }; let pm = blocking(context.pool(), move |conn| { @@ -150,12 +166,18 @@ mod tests { async fn prepare_comment_test(url: &Url, context: &LemmyContext) -> (ApubPerson, ApubPerson) { let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json"); - let person1 = ApubPerson::from_apub(lemmy_person, context, url, &mut 0) + ApubPerson::verify(&lemmy_person, url, context, &mut 0) + .await + .unwrap(); + let person1 = ApubPerson::from_apub(lemmy_person, context, &mut 0) .await .unwrap(); 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) + ApubPerson::verify(&pleroma_person, &pleroma_url, context, &mut 0) + .await + .unwrap(); + let person2 = ApubPerson::from_apub(pleroma_person, context, &mut 0) .await .unwrap(); (person1, person2) @@ -174,7 +196,10 @@ mod tests { let data = prepare_comment_test(&url, &context).await; let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json"); let mut request_counter = 0; - let pm = ApubPrivateMessage::from_apub(json.clone(), &context, &url, &mut request_counter) + ApubPrivateMessage::verify(&json, &url, &context, &mut request_counter) + .await + .unwrap(); + let pm = ApubPrivateMessage::from_apub(json.clone(), &context, &mut request_counter) .await .unwrap(); @@ -199,7 +224,10 @@ mod tests { let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2").unwrap(); 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) + ApubPrivateMessage::verify(&json, &pleroma_url, &context, &mut request_counter) + .await + .unwrap(); + let pm = ApubPrivateMessage::from_apub(json, &context, &mut request_counter) .await .unwrap(); diff --git a/crates/apub/src/protocol/objects/chat_message.rs b/crates/apub/src/protocol/objects/chat_message.rs index 3a019967..b8692913 100644 --- a/crates/apub/src/protocol/objects/chat_message.rs +++ b/crates/apub/src/protocol/objects/chat_message.rs @@ -6,10 +6,7 @@ use activitystreams::{ chrono::{DateTime, FixedOffset}, unparsed::Unparsed, }; -use anyhow::anyhow; -use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml, verify::verify_domains_match}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; +use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -35,21 +32,3 @@ pub struct ChatMessage { pub enum ChatMessageType { ChatMessage, } - -impl ChatMessage { - pub(crate) async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_domains_match(self.attributed_to.inner(), self.id.inner())?; - 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 index a761a442..8a6e6720 100644 --- a/crates/apub/src/protocol/objects/group.rs +++ b/crates/apub/src/protocol/objects/group.rs @@ -12,10 +12,10 @@ use chrono::{DateTime, FixedOffset}; use lemmy_apub_lib::{object_id::ObjectId, 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 lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use url::Url; @@ -52,27 +52,27 @@ pub struct Group { } impl Group { - pub(crate) async fn into_form( - self, + pub(crate) async fn verify( + &self, expected_domain: &Url, - settings: &Settings, - ) -> Result { - check_is_apub_id_valid(self.id.inner(), true, settings)?; + context: &LemmyContext, + ) -> Result<(), LemmyError> { + check_is_apub_id_valid(self.id.inner(), true, &context.settings())?; verify_domains_match(expected_domain, self.id.inner())?; - let name = self.preferred_username; - let title = self.name; - let description = get_summary_from_string_or_source(&self.summary, &self.source); - let shared_inbox = self.endpoints.shared_inbox.map(|s| s.into()); - let slur_regex = &settings.slur_regex(); - check_slurs(&name, slur_regex)?; - check_slurs(&title, slur_regex)?; + let slur_regex = &context.settings().slur_regex(); + check_slurs(&self.preferred_username, slur_regex)?; + check_slurs(&self.name, slur_regex)?; + let description = get_summary_from_string_or_source(&self.summary, &self.source); check_slurs_opt(&description, slur_regex)?; + Ok(()) + } + pub(crate) fn into_form(self) -> Result { Ok(CommunityForm { - name, - title, - description, + name: self.preferred_username, + title: self.name, + description: get_summary_from_string_or_source(&self.summary, &self.source), removed: None, published: self.published.map(|u| u.naive_local()), updated: self.updated.map(|u| u.naive_local()), @@ -87,7 +87,7 @@ impl Group { banner: Some(self.image.map(|i| i.url.into())), followers_url: Some(self.followers.into()), inbox_url: Some(self.inbox.into()), - shared_inbox_url: Some(shared_inbox), + shared_inbox_url: Some(self.endpoints.shared_inbox.map(|s| s.into())), }) } } diff --git a/crates/apub/src/protocol/objects/note.rs b/crates/apub/src/protocol/objects/note.rs index 34cb6bed..acb4b6fb 100644 --- a/crates/apub/src/protocol/objects/note.rs +++ b/crates/apub/src/protocol/objects/note.rs @@ -1,19 +1,13 @@ use crate::{ - activities::{verify_is_public, verify_person_in_community}, fetcher::post_or_comment::PostOrComment, - objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, + objects::{comment::ApubComment, 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::{object_id::ObjectId, values::MediaTypeHtml, verify::verify_domains_match}; -use lemmy_db_schema::{ - newtypes::CommentId, - source::{community::Community, post::Post}, - traits::Crud, -}; +use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml}; +use lemmy_db_schema::{newtypes::CommentId, source::post::Post, traits::Crud}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; @@ -76,26 +70,4 @@ impl Note { } } } - - 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.inner())?; - 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 index 285a0e04..ef7ab6e6 100644 --- a/crates/apub/src/protocol/objects/page.rs +++ b/crates/apub/src/protocol/objects/page.rs @@ -1,13 +1,12 @@ use crate::{ - activities::{verify_is_public, verify_person_in_community}, 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::{object_id::ObjectId, values::MediaTypeHtml, verify::verify_domains_match}; -use lemmy_utils::{utils::check_slurs, LemmyError}; +use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml}; +use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -54,20 +53,6 @@ impl Page { 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.inner())?; - 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, diff --git a/crates/apub_lib/src/object_id.rs b/crates/apub_lib/src/object_id.rs index 8e4b70a2..54643068 100644 --- a/crates/apub_lib/src/object_id.rs +++ b/crates/apub_lib/src/object_id.rs @@ -140,7 +140,8 @@ where let res2: Kind::ApubType = res.json().await?; - Ok(Kind::from_apub(res2, data, self.inner(), request_counter).await?) + Kind::verify(&res2, self.inner(), data, request_counter).await?; + Ok(Kind::from_apub(res2, data, request_counter).await?) } } diff --git a/crates/apub_lib/src/traits.rs b/crates/apub_lib/src/traits.rs index e5dcbc1e..c0cdb1af 100644 --- a/crates/apub_lib/src/traits.rs +++ b/crates/apub_lib/src/traits.rs @@ -44,6 +44,13 @@ pub trait ApubObject { async fn into_apub(self, data: &Self::DataType) -> Result; fn to_tombstone(&self) -> Result; + async fn verify( + apub: &Self::ApubType, + expected_domain: &Url, + data: &Self::DataType, + request_counter: &mut i32, + ) -> Result<(), LemmyError>; + /// Converts an object from ActivityPub type to Lemmy internal type. /// /// * `apub` The object to read from @@ -53,7 +60,6 @@ pub trait ApubObject { async fn from_apub( apub: Self::ApubType, data: &Self::DataType, - expected_domain: &Url, request_counter: &mut i32, ) -> Result where -- 2.44.1