Dont require leading ! or @ for webfinger resolve (#4513)

* Dont require leading ! or @ for webfinger resolve

* fmt

* clippy
This commit is contained in:
Nutomic 2024-03-15 13:42:09 +01:00 committed by GitHub
parent 43378c5bb3
commit 9d4299aaac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 46 additions and 52 deletions

View file

@ -45,7 +45,7 @@ test("Create user", async () => {
if (!site.my_user) { if (!site.my_user) {
throw "Missing site 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 () => { 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); let user = await registerUser(alpha, alphaUrl);
// make a local post and comment // 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; .community;
if (!alphaCommunity) { if (!alphaCommunity) {
throw "Missing alpha community"; throw "Missing alpha community";
@ -134,7 +134,7 @@ test("Create user with Arabic name", async () => {
if (!site.my_user) { if (!site.my_user) {
throw "Missing site 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; let alphaPerson = (await resolvePerson(alpha, apShortname)).person;
expect(alphaPerson).toBeDefined(); expect(alphaPerson).toBeDefined();

View file

@ -1,7 +1,6 @@
use crate::fetcher::search::{ use crate::fetcher::{
search_query_to_object_id, search::{search_query_to_object_id, search_query_to_object_id_local, SearchableObjects},
search_query_to_object_id_local, user_or_community::UserOrCommunity,
SearchableObjects,
}; };
use activitypub_federation::config::Data; use activitypub_federation::config::Data;
use actix_web::web::{Json, Query}; use actix_web::web::{Json, Query};
@ -31,7 +30,7 @@ pub async fn resolve_object(
let res = if is_authenticated { let res = if is_authenticated {
// user is fully authenticated; allow remote lookups as well. // 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 { } else {
// user isn't authenticated only allow a local search. // user isn't authenticated only allow a local search.
search_query_to_object_id_local(&data.q, &context).await search_query_to_object_id_local(&data.q, &context).await
@ -52,14 +51,6 @@ async fn convert_response(
let removed_or_deleted; let removed_or_deleted;
let mut res = ResolveObjectResponse::default(); let mut res = ResolveObjectResponse::default();
match object { 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) => { Post(p) => {
removed_or_deleted = p.deleted || p.removed; removed_or_deleted = p.deleted || p.removed;
res.post = Some(PostView::read(pool, p.id, user_id, false).await?) res.post = Some(PostView::read(pool, p.id, user_id, false).await?)
@ -68,6 +59,16 @@ async fn convert_response(
removed_or_deleted = c.deleted || c.removed; removed_or_deleted = c.deleted || c.removed;
res.comment = Some(CommentView::read(pool, c.id, user_id).await?) res.comment = Some(CommentView::read(pool, c.id, 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 the object was deleted from database, dont return it
if removed_or_deleted { if removed_or_deleted {

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
fetcher::user_or_community::{PersonOrGroup, UserOrCommunity},
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, 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::{ use activitypub_federation::{
config::Data, config::Data,
@ -9,7 +10,7 @@ use activitypub_federation::{
}; };
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::{LemmyError, LemmyErrorType}; use lemmy_utils::error::LemmyError;
use serde::Deserialize; use serde::Deserialize;
use url::Url; use url::Url;
@ -18,28 +19,22 @@ use url::Url;
/// which gets resolved to an URL. /// which gets resolved to an URL.
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn search_query_to_object_id( pub(crate) async fn search_query_to_object_id(
query: &str, mut query: String,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> Result<SearchableObjects, LemmyError> { ) -> Result<SearchableObjects, LemmyError> {
Ok(match Url::parse(query) { Ok(match Url::parse(&query) {
Ok(url) => { Ok(url) => {
// its already an url, just go with it // its already an url, just go with it
ObjectId::from(url).dereference(context).await? ObjectId::from(url).dereference(context).await?
} }
Err(_) => { Err(_) => {
// not an url, try to resolve via webfinger // not an url, try to resolve via webfinger
let mut chars = query.chars(); if query.starts_with('!') || query.starts_with('@') {
let kind = chars.next(); query.remove(0);
let identifier = chars.as_str();
match kind {
Some('@') => SearchableObjects::Person(
webfinger_resolve_actor::<LemmyContext, ApubPerson>(identifier, context).await?,
),
Some('!') => SearchableObjects::Community(
webfinger_resolve_actor::<LemmyContext, ApubCommunity>(identifier, context).await?,
),
_ => return Err(LemmyErrorType::InvalidQuery)?,
} }
SearchableObjects::PersonOrCommunity(Box::new(
webfinger_resolve_actor::<LemmyContext, UserOrCommunity>(&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. /// The types of ActivityPub objects that can be fetched directly by searching for their ID.
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum SearchableObjects { pub(crate) enum SearchableObjects {
Person(ApubPerson),
Community(ApubCommunity),
Post(ApubPost), Post(ApubPost),
Comment(ApubComment), Comment(ApubComment),
PersonOrCommunity(Box<UserOrCommunity>),
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub(crate) enum SearchableKinds { pub(crate) enum SearchableKinds {
Group(Group), Page(Box<Page>),
Person(Person),
Page(Page),
Note(Note), Note(Note),
PersonOrGroup(Box<PersonOrGroup>),
} }
#[async_trait::async_trait] #[async_trait::async_trait]
@ -82,10 +75,9 @@ impl Object for SearchableObjects {
fn last_refreshed_at(&self) -> Option<DateTime<Utc>> { fn last_refreshed_at(&self) -> Option<DateTime<Utc>> {
match self { match self {
SearchableObjects::Person(p) => p.last_refreshed_at(),
SearchableObjects::Community(c) => c.last_refreshed_at(),
SearchableObjects::Post(p) => p.last_refreshed_at(), SearchableObjects::Post(p) => p.last_refreshed_at(),
SearchableObjects::Comment(c) => c.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, object_id: Url,
context: &Data<Self::DataType>, context: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> { ) -> Result<Option<Self>, LemmyError> {
let c = ApubCommunity::read_from_id(object_id.clone(), context).await?; let uc = UserOrCommunity::read_from_id(object_id.clone(), context).await?;
if let Some(c) = c { if let Some(uc) = uc {
return Ok(Some(SearchableObjects::Community(c))); return Ok(Some(SearchableObjects::PersonOrCommunity(Box::new(uc))));
}
let p = ApubPerson::read_from_id(object_id.clone(), context).await?;
if let Some(p) = p {
return Ok(Some(SearchableObjects::Person(p)));
} }
let p = ApubPost::read_from_id(object_id.clone(), context).await?; let p = ApubPost::read_from_id(object_id.clone(), context).await?;
if let Some(p) = p { if let Some(p) = p {
@ -121,10 +109,12 @@ impl Object for SearchableObjects {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn delete(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> { async fn delete(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
match self { match self {
SearchableObjects::Person(p) => p.delete(data).await,
SearchableObjects::Community(c) => c.delete(data).await,
SearchableObjects::Post(p) => p.delete(data).await, SearchableObjects::Post(p) => p.delete(data).await,
SearchableObjects::Comment(c) => c.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 +129,12 @@ impl Object for SearchableObjects {
data: &Data<Self::DataType>, data: &Data<Self::DataType>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
match apub { 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::Page(a) => ApubPost::verify(a, expected_domain, data).await,
SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await, SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await,
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,
},
} }
} }
@ -151,10 +143,11 @@ impl Object for SearchableObjects {
use SearchableKinds as SAT; use SearchableKinds as SAT;
use SearchableObjects as SO; use SearchableObjects as SO;
Ok(match apub { Ok(match apub {
SAT::Group(g) => SO::Community(ApubCommunity::from_json(g, context).await?), SAT::Page(p) => SO::Post(ApubPost::from_json(*p, 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::Note(n) => SO::Comment(ApubComment::from_json(n, context).await?),
SAT::PersonOrGroup(pg) => {
SO::PersonOrCommunity(Box::new(UserOrCommunity::from_json(*pg, context).await?))
}
}) })
} }
} }