attempted fix

This commit is contained in:
Felix Ableitner 2024-11-18 14:52:04 +01:00
parent ae78eaa3b1
commit 21eedb8dc0
4 changed files with 89 additions and 17 deletions

View file

@ -298,6 +298,20 @@ test.only("Fetch remote content in private community", async () => {
const comment_id = comment.comment_view.comment.id; const comment_id = comment.comment_view.comment.id;
expect(comment_id).toBeDefined(); expect(comment_id).toBeDefined();
// Wait for post and comment to federate
/*
console.log('a');
await waitUntil(
() => resolvePost(alpha, post.post_view.post),
p => p?.post?.post.id != undefined,
);
await waitUntil(
() => resolveComment(gamma, comment.comment_view.comment),
p => p?.post?.post.id != undefined,
);
*/
console.log("b");
// create gamma user and follow community // create gamma user and follow community
const gammaCommunityId = ( const gammaCommunityId = (
await resolveCommunity(gamma, community.community_view.community.actor_id) await resolveCommunity(gamma, community.community_view.community.actor_id)
@ -310,23 +324,22 @@ test.only("Fetch remote content in private community", async () => {
await approveFollower(alpha, alphaCommunityId); await approveFollower(alpha, alphaCommunityId);
// now user can fetch posts and comments in community (using signed fetch), and create posts // now user can fetch posts and comments in community (using signed fetch), and create posts
// TODO: this fails because beta doesnt know if the gamma user was approved by alpha community
console.log(0);
let resolvedPost = await waitUntil( let resolvedPost = await waitUntil(
() => resolvePost(gamma, post.post_view.post), () => resolvePost(gamma, post.post_view.post),
p => p?.post?.post.id != undefined, p => p?.post?.post.id != undefined,
); );
console.log(post.post_view.post);
console.log(resolvedPost.post?.post);
expect(resolvedPost.post?.post.ap_id).toBe(post.post_view.post.ap_id); expect(resolvedPost.post?.post.ap_id).toBe(post.post_view.post.ap_id);
const resolvedComment = ( console.log(1);
await resolveComment(gamma, comment.comment_view.comment) const resolvedComment = await waitUntil(
).comment; () => resolveComment(gamma, comment.comment_view.comment),
expect(resolvedComment?.comment.ap_id).toBe( p => p?.post?.post.id != undefined,
);
expect(resolvedComment?.comment?.comment.ap_id).toBe(
comment.comment_view.comment.ap_id, comment.comment_view.comment.ap_id,
); );
console.log(2);
// TODO: this test should fail as check_has_followers_from_instance() on beta returns errors
// because it doesnt know the community follower. yet for some reason the test passes???
fail();
}); });
async function approveFollower(user: LemmyHttp, community_id: number) { async function approveFollower(user: LemmyHttp, community_id: number) {

View file

@ -6,16 +6,19 @@ use crate::{
community_moderators::ApubCommunityModerators, community_moderators::ApubCommunityModerators,
community_outbox::ApubCommunityOutbox, community_outbox::ApubCommunityOutbox,
}, },
fetcher::site_or_community_or_user::SiteOrCommunityOrUser,
http::{check_community_fetchable, create_apub_response, create_apub_tombstone_response}, http::{check_community_fetchable, create_apub_response, create_apub_tombstone_response},
objects::community::ApubCommunity, objects::community::ApubCommunity,
}; };
use activitypub_federation::{ use activitypub_federation::{
config::Data, config::Data,
fetch::object_id::ObjectId,
traits::{Collection, Object}, traits::{Collection, Object},
}; };
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{source::community::Community, traits::ApubActor}; use lemmy_db_schema::{source::community::Community, traits::ApubActor};
use lemmy_db_views_actor::structs::CommunityFollowerView;
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use serde::Deserialize; use serde::Deserialize;
@ -24,6 +27,11 @@ pub(crate) struct CommunityQuery {
community_name: String, community_name: String,
} }
#[derive(Deserialize)]
pub struct CommunityIsFollowerQuery {
is_follower: Option<ObjectId<SiteOrCommunityOrUser>>,
}
/// Return the ActivityPub json representation of a local community over HTTP. /// Return the ActivityPub json representation of a local community over HTTP.
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_community_http( pub(crate) async fn get_apub_community_http(
@ -48,11 +56,26 @@ pub(crate) async fn get_apub_community_http(
/// Returns an empty followers collection, only populating the size (for privacy). /// Returns an empty followers collection, only populating the size (for privacy).
pub(crate) async fn get_apub_community_followers( pub(crate) async fn get_apub_community_followers(
info: web::Path<CommunityQuery>, info: web::Path<CommunityQuery>,
query: web::Query<CommunityIsFollowerQuery>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<HttpResponse> { ) -> LemmyResult<HttpResponse> {
let community = Community::read_from_name(&mut context.pool(), &info.community_name, false) let community = Community::read_from_name(&mut context.pool(), &info.community_name, false)
.await? .await?
.ok_or(LemmyErrorType::NotFound)?; .ok_or(LemmyErrorType::NotFound)?;
if let Some(is_follower) = &query.is_follower {
let instance_id = is_follower.dereference(&context).await?.instance_id();
let has_followers = CommunityFollowerView::check_has_followers_from_instance(
community.id,
instance_id,
&mut context.pool(),
)
.await;
return if has_followers.is_ok() {
Ok(HttpResponse::Ok().finish())
} else {
Ok(HttpResponse::NotFound().finish())
};
}
check_community_fetchable(&community)?; check_community_fetchable(&community)?;
let followers = ApubCommunityFollower::read_local(&community.into(), &context).await?; let followers = ApubCommunityFollower::read_local(&community.into(), &context).await?;
create_apub_response(&followers) create_apub_response(&followers)

View file

@ -8,6 +8,7 @@ use activitypub_federation::{
actix_web::{inbox::receive_activity, signing_actor}, actix_web::{inbox::receive_activity, signing_actor},
config::Data, config::Data,
protocol::context::WithContext, protocol::context::WithContext,
traits::Actor,
FEDERATION_CONTENT_TYPE, FEDERATION_CONTENT_TYPE,
}; };
use actix_web::{web, web::Bytes, HttpRequest, HttpResponse}; use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
@ -145,6 +146,7 @@ async fn check_community_content_fetchable(
// from the fetching instance then fetching is allowed // from the fetching instance then fetching is allowed
Private => { Private => {
let signing_actor = signing_actor::<SiteOrCommunityOrUser>(request, None, context).await?; let signing_actor = signing_actor::<SiteOrCommunityOrUser>(request, None, context).await?;
if community.local {
Ok( Ok(
CommunityFollowerView::check_has_followers_from_instance( CommunityFollowerView::check_has_followers_from_instance(
community.id, community.id,
@ -153,6 +155,21 @@ async fn check_community_content_fetchable(
) )
.await?, .await?,
) )
} else if let Some(followers_url) = community.followers_url.clone() {
let mut followers_url = followers_url.inner().clone();
followers_url
.query_pairs_mut()
.append_pair("is_follower", signing_actor.id().as_str());
context
.client()
.get(followers_url.as_str())
.send()
.await?
.error_for_status()?;
Ok(())
} else {
Err(LemmyErrorType::NotFound.into())
}
} }
} }
} }

View file

@ -232,6 +232,25 @@ impl CommunityFollowerView {
.then_some(()) .then_some(())
.ok_or(diesel::NotFound) .ok_or(diesel::NotFound)
} }
pub async fn is_follower(
community_id: CommunityId,
instance_id: InstanceId,
pool: &mut DbPool<'_>,
) -> Result<(), Error> {
let conn = &mut get_conn(pool).await?;
select(exists(
action_query(community_actions::followed)
.inner_join(person::table.on(community_actions::person_id.eq(person::id)))
.filter(community_actions::community_id.eq(community_id))
.filter(person::instance_id.eq(instance_id))
.filter(community_actions::follow_state.eq(CommunityFollowerState::Accepted)),
))
.get_result::<bool>(conn)
.await?
.then_some(())
.ok_or(diesel::NotFound)
}
} }
#[cfg(test)] #[cfg(test)]