From dc4572460e8458753907dc9659bdd015e2f7bb27 Mon Sep 17 00:00:00 2001 From: marsara9 <1316726+marsara9@users.noreply.github.com> Date: Wed, 26 Jul 2023 12:17:42 -0400 Subject: [PATCH] Make resolve_object not require auth #3685 (#3716) * Resolves issue #3685 If user isn't authenticated with resolve_object, only allow a local search instead of possibly making an http request. * Making sure to validate auth before doing a potential remote lookup. --- crates/api_common/src/site.rs | 2 +- crates/apub/src/api/resolve_object.rs | 37 ++++++++++++++++++--------- crates/apub/src/fetcher/search.rs | 12 +++++++++ 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index bc7687e3c..35b6d77ec 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -84,7 +84,7 @@ pub struct SearchResponse { pub struct ResolveObject { /// Can be the full url, or a shortened version like: !fediverse@lemmy.ml pub q: String, - pub auth: Sensitive, + pub auth: Option>, } #[skip_serializing_none] diff --git a/crates/apub/src/api/resolve_object.rs b/crates/apub/src/api/resolve_object.rs index d86c28d60..898cc8d51 100644 --- a/crates/apub/src/api/resolve_object.rs +++ b/crates/apub/src/api/resolve_object.rs @@ -1,11 +1,15 @@ -use crate::fetcher::search::{search_query_to_object_id, SearchableObjects}; +use crate::fetcher::search::{ + search_query_to_object_id, + search_query_to_object_id_local, + SearchableObjects, +}; use activitypub_federation::config::Data; use actix_web::web::{Json, Query}; use diesel::NotFound; use lemmy_api_common::{ context::LemmyContext, site::{ResolveObject, ResolveObjectResponse}, - utils::{check_private_instance, local_user_view_from_jwt}, + utils::{check_private_instance, local_user_view_from_jwt_opt}, }; use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool}; use lemmy_db_views::structs::{CommentView, PostView}; @@ -17,14 +21,23 @@ pub async fn resolve_object( data: Query, context: Data, ) -> Result, LemmyError> { - let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; + let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await; let local_site = LocalSite::read(&mut context.pool()).await?; - let person_id = local_user_view.person.id; - check_private_instance(&Some(local_user_view), &local_site)?; + check_private_instance(&local_user_view, &local_site)?; + let person_id = local_user_view.map(|v| v.person.id); + // If we get a valid personId back we can safely assume that the user is authenticated, + // if there's no personId then the JWT was missing or invalid. + let is_authenticated = person_id.is_some(); + + let res = if is_authenticated { + // user is fully authenticated; allow remote lookups as well. + search_query_to_object_id(&data.q, &context).await + } else { + // user isn't authenticated only allow a local search. + search_query_to_object_id_local(&data.q, &context).await + } + .with_lemmy_type(LemmyErrorType::CouldntFindObject)?; - let res = search_query_to_object_id(&data.q, &context) - .await - .with_lemmy_type(LemmyErrorType::CouldntFindObject)?; convert_response(res, person_id, &mut context.pool()) .await .with_lemmy_type(LemmyErrorType::CouldntFindObject) @@ -32,7 +45,7 @@ pub async fn resolve_object( async fn convert_response( object: SearchableObjects, - user_id: PersonId, + user_id: Option, pool: &mut DbPool<'_>, ) -> Result, LemmyError> { use SearchableObjects::*; @@ -45,15 +58,15 @@ async fn convert_response( } Community(c) => { removed_or_deleted = c.deleted || c.removed; - res.community = Some(CommunityView::read(pool, c.id, Some(user_id), None).await?) + res.community = Some(CommunityView::read(pool, c.id, user_id, None).await?) } Post(p) => { removed_or_deleted = p.deleted || p.removed; - res.post = Some(PostView::read(pool, p.id, Some(user_id), None).await?) + res.post = Some(PostView::read(pool, p.id, user_id, None).await?) } Comment(c) => { removed_or_deleted = c.deleted || c.removed; - res.comment = Some(CommentView::read(pool, c.id, Some(user_id)).await?) + res.comment = Some(CommentView::read(pool, c.id, user_id).await?) } }; // if the object was deleted from database, dont return it diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index 39ecbc1be..dd8ef2ca2 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -44,6 +44,18 @@ pub(crate) async fn search_query_to_object_id( }) } +/// Converts a search query to an object id. The query MUST bbe a URL which will bbe treated +/// as the ObjectId directly. If the query is a webfinger identifier (@user@example.com or +/// !community@example.com) this method will return an error. +#[tracing::instrument(skip_all)] +pub(crate) async fn search_query_to_object_id_local( + query: &str, + context: &Data, +) -> Result { + let url = Url::parse(query)?; + ObjectId::from(url).dereference_local(context).await +} + /// The types of ActivityPub objects that can be fetched directly by searching for their ID. #[derive(Debug)] pub(crate) enum SearchableObjects {