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:
parent
cb559178bd
commit
6c3e984ad1
10 changed files with 53 additions and 48 deletions
|
@ -30,6 +30,7 @@ import {
|
||||||
unfollows,
|
unfollows,
|
||||||
getComments,
|
getComments,
|
||||||
getCommentParentId,
|
getCommentParentId,
|
||||||
|
resolveCommunity,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
let postRes: PostResponse;
|
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 () => {
|
test('A and G subscribe to B (center) A posts, G mentions B, it gets announced to A', async () => {
|
||||||
// Create a local post
|
// Create a local post
|
||||||
let alphaCommunity = await createCommunity(alpha, "main");
|
let alphaCommunity = (await resolveCommunity(alpha, "!main@lemmy-alpha:8541")).community.unwrap();
|
||||||
let alphaPost = await createPost(alpha, alphaCommunity.community_view.community.id);
|
let alphaPost = await createPost(alpha, alphaCommunity.community.id);
|
||||||
expect(alphaPost.post_view.community.local).toBe(true);
|
expect(alphaPost.post_view.community.local).toBe(true);
|
||||||
|
|
||||||
// Make sure gamma sees it
|
// Make sure gamma sees it
|
||||||
|
|
|
@ -37,7 +37,7 @@ test('Follow federated community', async () => {
|
||||||
c => c.community.local == false
|
c => c.community.local == false
|
||||||
).community.id;
|
).community.id;
|
||||||
expect(remoteCommunityId).toBeDefined();
|
expect(remoteCommunityId).toBeDefined();
|
||||||
expect(site.my_user.unwrap().follows.length).toBe(1);
|
expect(site.my_user.unwrap().follows.length).toBe(2);
|
||||||
|
|
||||||
// Test an unfollow
|
// Test an unfollow
|
||||||
let unfollow = await followCommunity(alpha, false, remoteCommunityId);
|
let unfollow = await followCommunity(alpha, false, remoteCommunityId);
|
||||||
|
@ -45,5 +45,5 @@ test('Follow federated community', async () => {
|
||||||
|
|
||||||
// Make sure you are unsubbed locally
|
// Make sure you are unsubbed locally
|
||||||
let siteUnfollowCheck = await getSite(alpha);
|
let siteUnfollowCheck = await getSite(alpha);
|
||||||
expect(siteUnfollowCheck.my_user.unwrap().follows.length).toBe(0);
|
expect(siteUnfollowCheck.my_user.unwrap().follows.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,7 +32,8 @@ import {
|
||||||
registerUser,
|
registerUser,
|
||||||
API,
|
API,
|
||||||
getSite,
|
getSite,
|
||||||
unfollows
|
unfollows,
|
||||||
|
resolveCommunity
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
let betaCommunity: CommunityView;
|
let betaCommunity: CommunityView;
|
||||||
|
@ -226,7 +227,8 @@ test('Delete a post', async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Remove a post from admin and community on different instance', 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 alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post.unwrap();
|
||||||
let removedPost = await removePost(alpha, true, alphaPost.post);
|
let removedPost = await removePost(alpha, true, alphaPost.post);
|
||||||
|
|
|
@ -174,9 +174,9 @@ export async function setupLogins() {
|
||||||
editSiteForm.auth = epsilon.auth.unwrap();
|
editSiteForm.auth = epsilon.auth.unwrap();
|
||||||
await epsilon.client.editSite(editSiteForm);
|
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 createCommunity(beta, "main");
|
||||||
await followBeta(beta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createPost(
|
export async function createPost(
|
||||||
|
|
|
@ -19,8 +19,13 @@ import {
|
||||||
API,
|
API,
|
||||||
resolveComment,
|
resolveComment,
|
||||||
saveUserSettingsFederated,
|
saveUserSettingsFederated,
|
||||||
|
setupLogins,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await setupLogins();
|
||||||
|
});
|
||||||
|
|
||||||
let apShortname: string;
|
let apShortname: string;
|
||||||
|
|
||||||
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {
|
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use lemmy_api_common::{
|
||||||
site::{ResolveObject, ResolveObjectResponse},
|
site::{ResolveObject, ResolveObjectResponse},
|
||||||
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
|
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_schema::{newtypes::PersonId, utils::DbPool};
|
||||||
use lemmy_db_views::structs::{CommentView, PostView};
|
use lemmy_db_views::structs::{CommentView, PostView};
|
||||||
use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe};
|
use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe};
|
||||||
|
@ -27,7 +27,7 @@ impl Perform for ResolveObject {
|
||||||
.await?;
|
.await?;
|
||||||
check_private_instance(&local_user_view, context.pool()).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
|
.await
|
||||||
.map_err(|e| e.with_message("couldnt_find_object"))?;
|
.map_err(|e| e.with_message("couldnt_find_object"))?;
|
||||||
convert_response(res, local_user_view.map(|l| l.person.id), context.pool())
|
convert_response(res, local_user_view.map(|l| l.person.id), context.pool())
|
||||||
|
|
|
@ -45,7 +45,7 @@ where
|
||||||
Ok(actor?)
|
Ok(actor?)
|
||||||
} else {
|
} else {
|
||||||
// Fetch the actor from its home instance using webfinger
|
// 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| {
|
let actor: DbActor = blocking(context.pool(), move |conn| {
|
||||||
DbActor::read_from_apub_id(conn, &id)
|
DbActor::read_from_apub_id(conn, &id)
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,52 +11,44 @@ use lemmy_websocket::LemmyContext;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
|
/// 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)
|
||||||
/// Some working examples for use with the `docker/federation/` setup:
|
/// which gets resolved to an URL.
|
||||||
/// 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
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn search_by_apub_id(
|
pub async fn search_query_to_object_id(
|
||||||
query: &str,
|
query: &str,
|
||||||
|
local_only: bool,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<SearchableObjects, LemmyError> {
|
) -> Result<SearchableObjects, LemmyError> {
|
||||||
let request_counter = &mut 0;
|
let request_counter = &mut 0;
|
||||||
let instance = local_instance(context);
|
let object_id = match Url::parse(query) {
|
||||||
match Url::parse(query) {
|
// its already an url, just go with it
|
||||||
Ok(url) => {
|
Ok(url) => ObjectId::new(url),
|
||||||
ObjectId::new(url)
|
|
||||||
.dereference(context, instance, request_counter)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
// not an url, try to resolve via webfinger
|
||||||
let mut chars = query.chars();
|
let mut chars = query.chars();
|
||||||
let kind = chars.next();
|
let kind = chars.next();
|
||||||
let identifier = chars.as_str();
|
let identifier = chars.as_str();
|
||||||
match kind {
|
let id = match kind {
|
||||||
Some('@') => {
|
Some('@') => {
|
||||||
let id =
|
webfinger_resolve_actor::<ApubPerson>(identifier, local_only, context, request_counter)
|
||||||
webfinger_resolve_actor::<ApubPerson>(identifier, context, request_counter).await?;
|
.await?
|
||||||
Ok(SearchableObjects::Person(
|
|
||||||
ObjectId::new(id)
|
|
||||||
.dereference(context, instance, request_counter)
|
|
||||||
.await?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Some('!') => {
|
Some('!') => {
|
||||||
let id =
|
webfinger_resolve_actor::<ApubCommunity>(identifier, local_only, context, request_counter)
|
||||||
webfinger_resolve_actor::<ApubCommunity>(identifier, context, request_counter).await?;
|
.await?
|
||||||
Ok(SearchableObjects::Community(
|
}
|
||||||
|
_ => return Err(LemmyError::from_message("invalid query")),
|
||||||
|
};
|
||||||
ObjectId::new(id)
|
ObjectId::new(id)
|
||||||
.dereference(context, instance, request_counter)
|
|
||||||
.await?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => Err(LemmyError::from_message("invalid query")),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
if local_only {
|
||||||
|
object_id.dereference_local(context).await
|
||||||
|
} else {
|
||||||
|
object_id
|
||||||
|
.dereference(context, local_instance(context), request_counter)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ pub struct WebfingerResponse {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub(crate) async fn webfinger_resolve_actor<Kind>(
|
pub(crate) async fn webfinger_resolve_actor<Kind>(
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
|
local_only: bool,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<DbUrl, LemmyError>
|
) -> Result<DbUrl, LemmyError>
|
||||||
|
@ -68,9 +69,14 @@ where
|
||||||
.filter_map(|l| l.href.clone())
|
.filter_map(|l| l.href.clone())
|
||||||
.collect();
|
.collect();
|
||||||
for l in links {
|
for l in links {
|
||||||
let object = ObjectId::<Kind>::new(l)
|
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)
|
.dereference(context, local_instance(context), request_counter)
|
||||||
.await;
|
.await
|
||||||
|
};
|
||||||
if object.is_ok() {
|
if object.is_ok() {
|
||||||
return object.map(|o| o.actor_id().into());
|
return object.map(|o| o.actor_id().into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,10 +73,9 @@ pub async fn collect_non_local_mentions(
|
||||||
.collect::<Vec<MentionData>>();
|
.collect::<Vec<MentionData>>();
|
||||||
|
|
||||||
for mention in &mentions {
|
for mention in &mentions {
|
||||||
// TODO should it be fetching it every time?
|
|
||||||
let identifier = format!("{}@{}", mention.name, mention.domain);
|
let identifier = format!("{}@{}", mention.name, mention.domain);
|
||||||
let actor_id =
|
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 {
|
if let Ok(actor_id) = actor_id {
|
||||||
let actor_id: ObjectId<ApubPerson> = ObjectId::new(actor_id);
|
let actor_id: ObjectId<ApubPerson> = ObjectId::new(actor_id);
|
||||||
addressed_ccs.push(actor_id.to_string().parse()?);
|
addressed_ccs.push(actor_id.to_string().parse()?);
|
||||||
|
|
Loading…
Reference in a new issue