Only allow authenticated users to fetch remote objects (#2493)

* Only allow authenticated users to fetch remote objects

* try to fix api tests
This commit is contained in:
Nutomic 2022-10-13 16:30:31 +00:00 committed by GitHub
parent cb559178bd
commit 6c3e984ad1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 53 additions and 48 deletions

View file

@ -30,6 +30,7 @@ import {
unfollows,
getComments,
getCommentParentId,
resolveCommunity,
} from './shared';
let postRes: PostResponse;
@ -293,8 +294,8 @@ test('Comment Search', async () => {
test('A and G subscribe to B (center) A posts, G mentions B, it gets announced to A', async () => {
// Create a local post
let alphaCommunity = await createCommunity(alpha, "main");
let alphaPost = await createPost(alpha, alphaCommunity.community_view.community.id);
let alphaCommunity = (await resolveCommunity(alpha, "!main@lemmy-alpha:8541")).community.unwrap();
let alphaPost = await createPost(alpha, alphaCommunity.community.id);
expect(alphaPost.post_view.community.local).toBe(true);
// Make sure gamma sees it

View file

@ -37,7 +37,7 @@ test('Follow federated community', async () => {
c => c.community.local == false
).community.id;
expect(remoteCommunityId).toBeDefined();
expect(site.my_user.unwrap().follows.length).toBe(1);
expect(site.my_user.unwrap().follows.length).toBe(2);
// Test an unfollow
let unfollow = await followCommunity(alpha, false, remoteCommunityId);
@ -45,5 +45,5 @@ test('Follow federated community', async () => {
// Make sure you are unsubbed locally
let siteUnfollowCheck = await getSite(alpha);
expect(siteUnfollowCheck.my_user.unwrap().follows.length).toBe(0);
expect(siteUnfollowCheck.my_user.unwrap().follows.length).toBe(1);
});

View file

@ -32,7 +32,8 @@ import {
registerUser,
API,
getSite,
unfollows
unfollows,
resolveCommunity
} from './shared';
let betaCommunity: CommunityView;
@ -226,7 +227,8 @@ test('Delete a post', async () => {
});
test('Remove a post from admin and community on different instance', async () => {
let postRes = await createPost(gamma, betaCommunity.community.id);
let gammaCommunity = await resolveCommunity(gamma, betaCommunity.community.actor_id);
let postRes = await createPost(gamma, gammaCommunity.community.unwrap().community.id);
let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post.unwrap();
let removedPost = await removePost(alpha, true, alphaPost.post);

View file

@ -174,9 +174,9 @@ export async function setupLogins() {
editSiteForm.auth = epsilon.auth.unwrap();
await epsilon.client.editSite(editSiteForm);
// Create the main beta community, follow it
// Create the main alpha/beta communities
await createCommunity(alpha, "main");
await createCommunity(beta, "main");
await followBeta(beta);
}
export async function createPost(

View file

@ -19,8 +19,13 @@ import {
API,
resolveComment,
saveUserSettingsFederated,
setupLogins,
} from './shared';
beforeAll(async () => {
await setupLogins();
});
let apShortname: string;
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {

View file

@ -5,7 +5,7 @@ use lemmy_api_common::{
site::{ResolveObject, ResolveObjectResponse},
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
};
use lemmy_apub::fetcher::search::{search_by_apub_id, SearchableObjects};
use lemmy_apub::fetcher::search::{search_query_to_object_id, SearchableObjects};
use lemmy_db_schema::{newtypes::PersonId, utils::DbPool};
use lemmy_db_views::structs::{CommentView, PostView};
use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe};
@ -27,7 +27,7 @@ impl Perform for ResolveObject {
.await?;
check_private_instance(&local_user_view, context.pool()).await?;
let res = search_by_apub_id(&self.q, context)
let res = search_query_to_object_id(&self.q, local_user_view.is_none(), context)
.await
.map_err(|e| e.with_message("couldnt_find_object"))?;
convert_response(res, local_user_view.map(|l| l.person.id), context.pool())

View file

@ -45,7 +45,7 @@ where
Ok(actor?)
} else {
// Fetch the actor from its home instance using webfinger
let id = webfinger_resolve_actor::<Actor>(identifier, context, &mut 0).await?;
let id = webfinger_resolve_actor::<Actor>(identifier, true, context, &mut 0).await?;
let actor: DbActor = blocking(context.pool(), move |conn| {
DbActor::read_from_apub_id(conn, &id)
})

View file

@ -11,52 +11,44 @@ use lemmy_websocket::LemmyContext;
use serde::Deserialize;
use url::Url;
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
///
/// Some working examples for use with the `docker/federation/` setup:
/// http://lemmy_alpha:8541/c/main, or !main@lemmy_alpha:8541
/// http://lemmy_beta:8551/u/lemmy_alpha, or @lemmy_beta@lemmy_beta:8551
/// http://lemmy_gamma:8561/post/3
/// http://lemmy_delta:8571/comment/2
/// 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 async fn search_by_apub_id(
pub async fn search_query_to_object_id(
query: &str,
local_only: bool,
context: &LemmyContext,
) -> Result<SearchableObjects, LemmyError> {
let request_counter = &mut 0;
let instance = local_instance(context);
match Url::parse(query) {
Ok(url) => {
ObjectId::new(url)
.dereference(context, instance, request_counter)
.await
}
let object_id = match Url::parse(query) {
// its already an url, just go with it
Ok(url) => ObjectId::new(url),
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 {
let id = match kind {
Some('@') => {
let id =
webfinger_resolve_actor::<ApubPerson>(identifier, context, request_counter).await?;
Ok(SearchableObjects::Person(
ObjectId::new(id)
.dereference(context, instance, request_counter)
.await?,
))
webfinger_resolve_actor::<ApubPerson>(identifier, local_only, context, request_counter)
.await?
}
Some('!') => {
let id =
webfinger_resolve_actor::<ApubCommunity>(identifier, context, request_counter).await?;
Ok(SearchableObjects::Community(
ObjectId::new(id)
.dereference(context, instance, request_counter)
.await?,
))
webfinger_resolve_actor::<ApubCommunity>(identifier, local_only, context, request_counter)
.await?
}
_ => Err(LemmyError::from_message("invalid query")),
}
_ => return Err(LemmyError::from_message("invalid query")),
};
ObjectId::new(id)
}
};
if local_only {
object_id.dereference_local(context).await
} else {
object_id
.dereference(context, local_instance(context), request_counter)
.await
}
}

View file

@ -28,6 +28,7 @@ pub struct WebfingerResponse {
#[tracing::instrument(skip_all)]
pub(crate) async fn webfinger_resolve_actor<Kind>(
identifier: &str,
local_only: bool,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<DbUrl, LemmyError>
@ -68,9 +69,14 @@ where
.filter_map(|l| l.href.clone())
.collect();
for l in links {
let object = ObjectId::<Kind>::new(l)
.dereference(context, local_instance(context), request_counter)
.await;
let object_id = ObjectId::<Kind>::new(l);
let object = if local_only {
object_id.dereference_local(context).await
} else {
object_id
.dereference(context, local_instance(context), request_counter)
.await
};
if object.is_ok() {
return object.map(|o| o.actor_id().into());
}

View file

@ -73,10 +73,9 @@ pub async fn collect_non_local_mentions(
.collect::<Vec<MentionData>>();
for mention in &mentions {
// TODO should it be fetching it every time?
let identifier = format!("{}@{}", mention.name, mention.domain);
let actor_id =
webfinger_resolve_actor::<ApubPerson>(&identifier, context, request_counter).await;
webfinger_resolve_actor::<ApubPerson>(&identifier, true, context, request_counter).await;
if let Ok(actor_id) = actor_id {
let actor_id: ObjectId<ApubPerson> = ObjectId::new(actor_id);
addressed_ccs.push(actor_id.to_string().parse()?);