From c2249c009a2bdadac26fd8aa1651c703b8df7d44 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 6 Mar 2024 12:32:32 +0100 Subject: [PATCH 1/3] Dont require leading ! or @ for webfinger resolve --- api_tests/src/user.spec.ts | 6 +-- crates/apub/src/api/resolve_object.rs | 23 ++++++---- crates/apub/src/fetcher/search.rs | 65 +++++++++++++-------------- 3 files changed, 48 insertions(+), 46 deletions(-) diff --git a/api_tests/src/user.spec.ts b/api_tests/src/user.spec.ts index ccfc5e1fe1..4846d60f70 100644 --- a/api_tests/src/user.spec.ts +++ b/api_tests/src/user.spec.ts @@ -45,7 +45,7 @@ test("Create user", async () => { if (!site.my_user) { throw "Missing site user"; } - apShortname = `@${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`; + apShortname = `${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`; }); test("Set some user settings, check that they are federated", async () => { @@ -68,7 +68,7 @@ test("Delete user", async () => { let user = await registerUser(alpha, alphaUrl); // make a local post and comment - let alphaCommunity = (await resolveCommunity(user, "!main@lemmy-alpha:8541")) + let alphaCommunity = (await resolveCommunity(user, "main@lemmy-alpha:8541")) .community; if (!alphaCommunity) { throw "Missing alpha community"; @@ -134,7 +134,7 @@ test("Create user with Arabic name", async () => { if (!site.my_user) { throw "Missing site user"; } - apShortname = `@${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`; + apShortname = `${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`; let alphaPerson = (await resolvePerson(alpha, apShortname)).person; expect(alphaPerson).toBeDefined(); diff --git a/crates/apub/src/api/resolve_object.rs b/crates/apub/src/api/resolve_object.rs index e081377f6f..d7edb28463 100644 --- a/crates/apub/src/api/resolve_object.rs +++ b/crates/apub/src/api/resolve_object.rs @@ -15,6 +15,7 @@ use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils:: use lemmy_db_views::structs::{CommentView, LocalUserView, PostView}; use lemmy_db_views_actor::structs::{CommunityView, PersonView}; use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType}; +use crate::fetcher::user_or_community::UserOrCommunity; #[tracing::instrument(skip(context))] pub async fn resolve_object( @@ -31,7 +32,7 @@ pub async fn resolve_object( let res = if is_authenticated { // user is fully authenticated; allow remote lookups as well. - search_query_to_object_id(&data.q, &context).await + search_query_to_object_id(data.q.clone(), &context).await } else { // user isn't authenticated only allow a local search. search_query_to_object_id_local(&data.q, &context).await @@ -52,14 +53,6 @@ async fn convert_response( let removed_or_deleted; let mut res = ResolveObjectResponse::default(); match object { - Person(p) => { - removed_or_deleted = p.deleted; - res.person = Some(PersonView::read(pool, p.id).await?) - } - Community(c) => { - removed_or_deleted = c.deleted || c.removed; - res.community = Some(CommunityView::read(pool, c.id, user_id, false).await?) - } Post(p) => { removed_or_deleted = p.deleted || p.removed; res.post = Some(PostView::read(pool, p.id, user_id, false).await?) @@ -68,6 +61,18 @@ async fn convert_response( removed_or_deleted = c.deleted || c.removed; res.comment = Some(CommentView::read(pool, c.id, user_id).await?) } + PersonOrCommunity(p) => { + match p { + UserOrCommunity::User(u) => { + removed_or_deleted = u.deleted; + res.person = Some(PersonView::read(pool, u.id).await?) + } + UserOrCommunity::Community(c) => { + removed_or_deleted = c.deleted || c.removed; + res.community = Some(CommunityView::read(pool, c.id, user_id, false).await?) + } + } + } }; // if the object was deleted from database, dont return it if removed_or_deleted { diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index 54951edd99..e78a9e998b 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -1,45 +1,40 @@ use crate::{ objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, - protocol::objects::{group::Group, note::Note, page::Page, person::Person}, + protocol::objects::{note::Note, page::Page}, }; use activitypub_federation::{ config::Data, fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor}, traits::Object, }; + use chrono::{DateTime, Utc}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::{LemmyError, LemmyErrorType}; +use lemmy_utils::error::{LemmyError}; use serde::Deserialize; use url::Url; +use crate::fetcher::user_or_community::{PersonOrGroup, UserOrCommunity}; + /// Converts search query to object id. The query can either be an URL, which will be treated as /// ObjectId directly, or a webfinger identifier (@user@example.com or !community@example.com) /// which gets resolved to an URL. #[tracing::instrument(skip_all)] pub(crate) async fn search_query_to_object_id( - query: &str, + mut query: String, context: &Data, ) -> Result { - Ok(match Url::parse(query) { + Ok(match Url::parse(&query) { Ok(url) => { // its already an url, just go with it ObjectId::from(url).dereference(context).await? } Err(_) => { // not an url, try to resolve via webfinger - let mut chars = query.chars(); - let kind = chars.next(); - let identifier = chars.as_str(); - match kind { - Some('@') => SearchableObjects::Person( - webfinger_resolve_actor::(identifier, context).await?, - ), - Some('!') => SearchableObjects::Community( - webfinger_resolve_actor::(identifier, context).await?, - ), - _ => return Err(LemmyErrorType::InvalidQuery)?, + if query.starts_with("!") || query.starts_with("@") { + query.remove(0); } + SearchableObjects::PersonOrCommunity(webfinger_resolve_actor::(&query, context).await?) } }) } @@ -59,19 +54,17 @@ pub(crate) async fn search_query_to_object_id_local( /// The types of ActivityPub objects that can be fetched directly by searching for their ID. #[derive(Debug)] pub(crate) enum SearchableObjects { - Person(ApubPerson), - Community(ApubCommunity), Post(ApubPost), Comment(ApubComment), + PersonOrCommunity(UserOrCommunity) } #[derive(Deserialize)] #[serde(untagged)] pub(crate) enum SearchableKinds { - Group(Group), - Person(Person), Page(Page), Note(Note), + PersonOrGroup(PersonOrGroup) } #[async_trait::async_trait] @@ -82,10 +75,9 @@ impl Object for SearchableObjects { fn last_refreshed_at(&self) -> Option> { match self { - SearchableObjects::Person(p) => p.last_refreshed_at(), - SearchableObjects::Community(c) => c.last_refreshed_at(), SearchableObjects::Post(p) => p.last_refreshed_at(), SearchableObjects::Comment(c) => c.last_refreshed_at(), + SearchableObjects::PersonOrCommunity(p) => p.last_refreshed_at(), } } @@ -99,13 +91,9 @@ impl Object for SearchableObjects { object_id: Url, context: &Data, ) -> Result, LemmyError> { - let c = ApubCommunity::read_from_id(object_id.clone(), context).await?; - if let Some(c) = c { - return Ok(Some(SearchableObjects::Community(c))); - } - let p = ApubPerson::read_from_id(object_id.clone(), context).await?; - if let Some(p) = p { - return Ok(Some(SearchableObjects::Person(p))); + let uc = UserOrCommunity::read_from_id(object_id.clone(), context).await?; + if let Some(uc) = uc { + return Ok(Some(SearchableObjects::PersonOrCommunity(uc))); } let p = ApubPost::read_from_id(object_id.clone(), context).await?; if let Some(p) = p { @@ -121,10 +109,14 @@ impl Object for SearchableObjects { #[tracing::instrument(skip_all)] async fn delete(self, data: &Data) -> Result<(), LemmyError> { match self { - SearchableObjects::Person(p) => p.delete(data).await, - SearchableObjects::Community(c) => c.delete(data).await, SearchableObjects::Post(p) => p.delete(data).await, SearchableObjects::Comment(c) => c.delete(data).await, + SearchableObjects::PersonOrCommunity(pc) => { + match pc { + UserOrCommunity::User(p) => p.delete(data).await, + UserOrCommunity::Community(c) => c.delete(data).await, + } + } } } @@ -139,10 +131,14 @@ impl Object for SearchableObjects { data: &Data, ) -> Result<(), LemmyError> { match apub { - SearchableKinds::Group(a) => ApubCommunity::verify(a, expected_domain, data).await, - SearchableKinds::Person(a) => ApubPerson::verify(a, expected_domain, data).await, SearchableKinds::Page(a) => ApubPost::verify(a, expected_domain, data).await, SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await, + SearchableKinds::PersonOrGroup(pg) => { + match pg { + PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await, + PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await, + } + } } } @@ -151,10 +147,11 @@ impl Object for SearchableObjects { use SearchableKinds as SAT; use SearchableObjects as SO; Ok(match apub { - SAT::Group(g) => SO::Community(ApubCommunity::from_json(g, context).await?), - SAT::Person(p) => SO::Person(ApubPerson::from_json(p, context).await?), SAT::Page(p) => SO::Post(ApubPost::from_json(p, context).await?), SAT::Note(n) => SO::Comment(ApubComment::from_json(n, context).await?), + SAT::PersonOrGroup(pg) => { + SO::PersonOrCommunity(UserOrCommunity::from_json(pg, context).await?) + } }) } } From 9da28935e012687f90e57ec999f93207c27f032e Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 6 Mar 2024 15:33:16 +0100 Subject: [PATCH 2/3] fmt --- crates/apub/src/api/resolve_object.rs | 28 ++++++++++------------ crates/apub/src/fetcher/search.rs | 34 ++++++++++++--------------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/crates/apub/src/api/resolve_object.rs b/crates/apub/src/api/resolve_object.rs index d7edb28463..3447dc9047 100644 --- a/crates/apub/src/api/resolve_object.rs +++ b/crates/apub/src/api/resolve_object.rs @@ -1,7 +1,6 @@ -use crate::fetcher::search::{ - search_query_to_object_id, - search_query_to_object_id_local, - SearchableObjects, +use crate::fetcher::{ + search::{search_query_to_object_id, search_query_to_object_id_local, SearchableObjects}, + user_or_community::UserOrCommunity, }; use activitypub_federation::config::Data; use actix_web::web::{Json, Query}; @@ -15,7 +14,6 @@ use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils:: use lemmy_db_views::structs::{CommentView, LocalUserView, PostView}; use lemmy_db_views_actor::structs::{CommunityView, PersonView}; use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType}; -use crate::fetcher::user_or_community::UserOrCommunity; #[tracing::instrument(skip(context))] pub async fn resolve_object( @@ -61,18 +59,16 @@ async fn convert_response( removed_or_deleted = c.deleted || c.removed; res.comment = Some(CommentView::read(pool, c.id, user_id).await?) } - PersonOrCommunity(p) => { - match p { - UserOrCommunity::User(u) => { - removed_or_deleted = u.deleted; - res.person = Some(PersonView::read(pool, u.id).await?) - } - UserOrCommunity::Community(c) => { - removed_or_deleted = c.deleted || c.removed; - res.community = Some(CommunityView::read(pool, c.id, user_id, false).await?) - } + PersonOrCommunity(p) => match p { + UserOrCommunity::User(u) => { + removed_or_deleted = u.deleted; + res.person = Some(PersonView::read(pool, u.id).await?) } - } + UserOrCommunity::Community(c) => { + removed_or_deleted = c.deleted || c.removed; + res.community = Some(CommunityView::read(pool, c.id, user_id, false).await?) + } + }, }; // if the object was deleted from database, dont return it if removed_or_deleted { diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index e78a9e998b..f2cbad9472 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -1,4 +1,5 @@ use crate::{ + fetcher::user_or_community::{PersonOrGroup, UserOrCommunity}, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::objects::{note::Note, page::Page}, }; @@ -7,15 +8,12 @@ use activitypub_federation::{ fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor}, traits::Object, }; - use chrono::{DateTime, Utc}; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::{LemmyError}; +use lemmy_utils::error::LemmyError; use serde::Deserialize; use url::Url; -use crate::fetcher::user_or_community::{PersonOrGroup, UserOrCommunity}; - /// Converts search query to object id. The query can either be an URL, which will be treated as /// ObjectId directly, or a webfinger identifier (@user@example.com or !community@example.com) /// which gets resolved to an URL. @@ -34,7 +32,9 @@ pub(crate) async fn search_query_to_object_id( if query.starts_with("!") || query.starts_with("@") { query.remove(0); } - SearchableObjects::PersonOrCommunity(webfinger_resolve_actor::(&query, context).await?) + SearchableObjects::PersonOrCommunity( + webfinger_resolve_actor::(&query, context).await?, + ) } }) } @@ -56,7 +56,7 @@ pub(crate) async fn search_query_to_object_id_local( pub(crate) enum SearchableObjects { Post(ApubPost), Comment(ApubComment), - PersonOrCommunity(UserOrCommunity) + PersonOrCommunity(UserOrCommunity), } #[derive(Deserialize)] @@ -64,7 +64,7 @@ pub(crate) enum SearchableObjects { pub(crate) enum SearchableKinds { Page(Page), Note(Note), - PersonOrGroup(PersonOrGroup) + PersonOrGroup(PersonOrGroup), } #[async_trait::async_trait] @@ -111,12 +111,10 @@ impl Object for SearchableObjects { match self { SearchableObjects::Post(p) => p.delete(data).await, SearchableObjects::Comment(c) => c.delete(data).await, - SearchableObjects::PersonOrCommunity(pc) => { - match pc { - UserOrCommunity::User(p) => p.delete(data).await, - UserOrCommunity::Community(c) => c.delete(data).await, - } - } + SearchableObjects::PersonOrCommunity(pc) => match pc { + UserOrCommunity::User(p) => p.delete(data).await, + UserOrCommunity::Community(c) => c.delete(data).await, + }, } } @@ -133,12 +131,10 @@ impl Object for SearchableObjects { match apub { SearchableKinds::Page(a) => ApubPost::verify(a, expected_domain, data).await, SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await, - SearchableKinds::PersonOrGroup(pg) => { - match pg { - PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await, - PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await, - } - } + SearchableKinds::PersonOrGroup(pg) => match pg { + PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await, + PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await, + }, } } From 97cdbdce04414315f8d6ede145c59fc3d4ef4ae9 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 6 Mar 2024 17:11:22 +0100 Subject: [PATCH 3/3] clippy --- crates/apub/src/api/resolve_object.rs | 2 +- crates/apub/src/fetcher/search.rs | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/apub/src/api/resolve_object.rs b/crates/apub/src/api/resolve_object.rs index 3447dc9047..6d672a8cd3 100644 --- a/crates/apub/src/api/resolve_object.rs +++ b/crates/apub/src/api/resolve_object.rs @@ -59,7 +59,7 @@ async fn convert_response( removed_or_deleted = c.deleted || c.removed; res.comment = Some(CommentView::read(pool, c.id, user_id).await?) } - PersonOrCommunity(p) => match p { + PersonOrCommunity(p) => match *p { UserOrCommunity::User(u) => { removed_or_deleted = u.deleted; res.person = Some(PersonView::read(pool, u.id).await?) diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index f2cbad9472..74d755da0e 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -29,12 +29,12 @@ pub(crate) async fn search_query_to_object_id( } Err(_) => { // not an url, try to resolve via webfinger - if query.starts_with("!") || query.starts_with("@") { + if query.starts_with('!') || query.starts_with('@') { query.remove(0); } - SearchableObjects::PersonOrCommunity( + SearchableObjects::PersonOrCommunity(Box::new( webfinger_resolve_actor::(&query, context).await?, - ) + )) } }) } @@ -56,15 +56,15 @@ pub(crate) async fn search_query_to_object_id_local( pub(crate) enum SearchableObjects { Post(ApubPost), Comment(ApubComment), - PersonOrCommunity(UserOrCommunity), + PersonOrCommunity(Box), } #[derive(Deserialize)] #[serde(untagged)] pub(crate) enum SearchableKinds { - Page(Page), + Page(Box), Note(Note), - PersonOrGroup(PersonOrGroup), + PersonOrGroup(Box), } #[async_trait::async_trait] @@ -93,7 +93,7 @@ impl Object for SearchableObjects { ) -> Result, LemmyError> { let uc = UserOrCommunity::read_from_id(object_id.clone(), context).await?; if let Some(uc) = uc { - return Ok(Some(SearchableObjects::PersonOrCommunity(uc))); + return Ok(Some(SearchableObjects::PersonOrCommunity(Box::new(uc)))); } let p = ApubPost::read_from_id(object_id.clone(), context).await?; if let Some(p) = p { @@ -111,7 +111,7 @@ impl Object for SearchableObjects { match self { SearchableObjects::Post(p) => p.delete(data).await, SearchableObjects::Comment(c) => c.delete(data).await, - SearchableObjects::PersonOrCommunity(pc) => match pc { + SearchableObjects::PersonOrCommunity(pc) => match *pc { UserOrCommunity::User(p) => p.delete(data).await, UserOrCommunity::Community(c) => c.delete(data).await, }, @@ -131,7 +131,7 @@ impl Object for SearchableObjects { match apub { SearchableKinds::Page(a) => ApubPost::verify(a, expected_domain, data).await, SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await, - SearchableKinds::PersonOrGroup(pg) => match pg { + SearchableKinds::PersonOrGroup(pg) => match pg.as_ref() { PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await, PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await, }, @@ -143,10 +143,10 @@ impl Object for SearchableObjects { use SearchableKinds as SAT; use SearchableObjects as SO; Ok(match apub { - SAT::Page(p) => SO::Post(ApubPost::from_json(p, context).await?), + SAT::Page(p) => SO::Post(ApubPost::from_json(*p, context).await?), SAT::Note(n) => SO::Comment(ApubComment::from_json(n, context).await?), SAT::PersonOrGroup(pg) => { - SO::PersonOrCommunity(UserOrCommunity::from_json(pg, context).await?) + SO::PersonOrCommunity(Box::new(UserOrCommunity::from_json(*pg, context).await?)) } }) }