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.
This commit is contained in:
marsara9 2023-07-26 12:17:42 -04:00 committed by GitHub
parent afac3ee7ff
commit dc4572460e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 13 deletions

View file

@ -84,7 +84,7 @@ pub struct SearchResponse {
pub struct ResolveObject { pub struct ResolveObject {
/// Can be the full url, or a shortened version like: !fediverse@lemmy.ml /// Can be the full url, or a shortened version like: !fediverse@lemmy.ml
pub q: String, pub q: String,
pub auth: Sensitive<String>, pub auth: Option<Sensitive<String>>,
} }
#[skip_serializing_none] #[skip_serializing_none]

View file

@ -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 activitypub_federation::config::Data;
use actix_web::web::{Json, Query}; use actix_web::web::{Json, Query};
use diesel::NotFound; use diesel::NotFound;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
site::{ResolveObject, ResolveObjectResponse}, 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_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool};
use lemmy_db_views::structs::{CommentView, PostView}; use lemmy_db_views::structs::{CommentView, PostView};
@ -17,14 +21,23 @@ pub async fn resolve_object(
data: Query<ResolveObject>, data: Query<ResolveObject>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> Result<Json<ResolveObjectResponse>, LemmyError> { ) -> Result<Json<ResolveObjectResponse>, 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 local_site = LocalSite::read(&mut context.pool()).await?;
let person_id = local_user_view.person.id; check_private_instance(&local_user_view, &local_site)?;
check_private_instance(&Some(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 = search_query_to_object_id(&data.q, &context) let res = if is_authenticated {
.await // 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)?; .with_lemmy_type(LemmyErrorType::CouldntFindObject)?;
convert_response(res, person_id, &mut context.pool()) convert_response(res, person_id, &mut context.pool())
.await .await
.with_lemmy_type(LemmyErrorType::CouldntFindObject) .with_lemmy_type(LemmyErrorType::CouldntFindObject)
@ -32,7 +45,7 @@ pub async fn resolve_object(
async fn convert_response( async fn convert_response(
object: SearchableObjects, object: SearchableObjects,
user_id: PersonId, user_id: Option<PersonId>,
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
) -> Result<Json<ResolveObjectResponse>, LemmyError> { ) -> Result<Json<ResolveObjectResponse>, LemmyError> {
use SearchableObjects::*; use SearchableObjects::*;
@ -45,15 +58,15 @@ async fn convert_response(
} }
Community(c) => { Community(c) => {
removed_or_deleted = c.deleted || c.removed; 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) => { Post(p) => {
removed_or_deleted = p.deleted || p.removed; 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) => { Comment(c) => {
removed_or_deleted = c.deleted || c.removed; 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 // if the object was deleted from database, dont return it

View file

@ -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<LemmyContext>,
) -> Result<SearchableObjects, LemmyError> {
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. /// 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 {