Skip to content

Commit

Permalink
Implement instance actor (#1798)
Browse files Browse the repository at this point in the history
* Implement instance actor

* wip: make site bans federate

* finish implementation and unit tests for federated bans

* start adding api tests

* fix api test

* remve site from GetCommunityResponse

* only federate site bans originating from user's home instance

* dont expose site.private_key in api
  • Loading branch information
Nutomic authored and dessalines committed Feb 7, 2022
1 parent 560fc26 commit d912b00
Show file tree
Hide file tree
Showing 55 changed files with 1,483 additions and 517 deletions.
84 changes: 56 additions & 28 deletions api_tests/src/post.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import {
reportPost,
listPostReports,
randomString,
registerUser,
API,
getSite
} from './shared';
import { PostView, CommunityView } from 'lemmy-js-client';

Expand Down Expand Up @@ -297,63 +300,88 @@ test('A and G subscribe to B (center) A posts, it gets announced to G', async ()
});

test('Enforce site ban for federated user', async () => {
let alphaShortname = `@lemmy_alpha@lemmy-alpha:8541`;
let alphaPerson = (await resolvePerson(beta, alphaShortname)).person;
// create a test user
let alphaUserJwt = await registerUser(alpha);
expect(alphaUserJwt).toBeDefined();
let alphaUser: API = {
client: alpha.client,
auth: alphaUserJwt.jwt,
};
let alphaUserActorId = (await getSite(alphaUser)).my_user.local_user_view.person.actor_id;
expect(alphaUserActorId).toBeDefined();
let alphaPerson = (await resolvePerson(alphaUser, alphaUserActorId)).person;
expect(alphaPerson).toBeDefined();

// ban alpha from beta site
let banAlpha = await banPersonFromSite(beta, alphaPerson.person.id, true);
// alpha makes post in beta community, it federates to beta instance
let postRes1 = await createPost(alphaUser, betaCommunity.community.id);
let searchBeta1 = await searchPostLocal(beta, postRes1.post_view.post);
expect(searchBeta1.posts[0]).toBeDefined();

// ban alpha from its instance
let banAlpha = await banPersonFromSite(alpha, alphaPerson.person.id, true, true);
expect(banAlpha.banned).toBe(true);

// Alpha makes post on beta
let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined();
expect(postRes.post_view.community.local).toBe(false);
expect(postRes.post_view.creator.local).toBe(true);
expect(postRes.post_view.counts.score).toBe(1);
// alpha ban should be federated to beta
let alphaUserOnBeta1 = await resolvePerson(beta, alphaUserActorId);
expect(alphaUserOnBeta1.person.person.banned).toBe(true);

// Make sure that post doesn't make it to beta
let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
let betaPost = searchBeta.posts[0];
expect(betaPost).toBeUndefined();
// existing alpha post should be removed on beta
let searchBeta2 = await searchPostLocal(beta, postRes1.post_view.post);
expect(searchBeta2.posts[0]).toBeUndefined();

// Unban alpha
let unBanAlpha = await banPersonFromSite(beta, alphaPerson.person.id, false);
let unBanAlpha = await banPersonFromSite(alpha, alphaPerson.person.id, false, false);
expect(unBanAlpha.banned).toBe(false);

// alpha makes new post in beta community, it federates
let postRes2 = await createPost(alphaUser, betaCommunity.community.id);
let searchBeta3 = await searchPostLocal(beta, postRes2.post_view.post);
expect(searchBeta3.posts[0]).toBeDefined();

let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId)
expect(alphaUserOnBeta2.person.person.banned).toBe(false);
});

test('Enforce community ban for federated user', async () => {
let alphaShortname = `@lemmy_alpha@lemmy-alpha:8541`;
let alphaPerson = (await resolvePerson(beta, alphaShortname)).person;
expect(alphaPerson).toBeDefined();

// ban alpha from beta site
await banPersonFromCommunity(beta, alphaPerson.person.id, 2, false);
let banAlpha = await banPersonFromCommunity(beta, alphaPerson.person.id, 2, true);
// make a post in beta, it goes through
let postRes1 = await createPost(alpha, betaCommunity.community.id);
let searchBeta1 = await searchPostLocal(beta, postRes1.post_view.post);
expect(searchBeta1.posts[0]).toBeDefined();

// ban alpha from beta community
let banAlpha = await banPersonFromCommunity(beta, alphaPerson.person.id, 2, true, true);
expect(banAlpha.banned).toBe(true);

// ensure that the post by alpha got removed
let searchAlpha1 = await searchPostLocal(alpha, postRes1.post_view.post);
expect(searchAlpha1.posts[0]).toBeUndefined();

// Alpha tries to make post on beta, but it fails because of ban
let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view).toBeUndefined();
let postRes2 = await createPost(alpha, betaCommunity.community.id);
expect(postRes2.post_view).toBeUndefined();

// Unban alpha
let unBanAlpha = await banPersonFromCommunity(
beta,
alphaPerson.person.id,
2,
false,
false
);
expect(unBanAlpha.banned).toBe(false);
let postRes2 = await createPost(alpha, betaCommunity.community.id);
expect(postRes2.post_view.post).toBeDefined();
expect(postRes2.post_view.community.local).toBe(false);
expect(postRes2.post_view.creator.local).toBe(true);
expect(postRes2.post_view.counts.score).toBe(1);
let postRes3 = await createPost(alpha, betaCommunity.community.id);
expect(postRes3.post_view.post).toBeDefined();
expect(postRes3.post_view.community.local).toBe(false);
expect(postRes3.post_view.creator.local).toBe(true);
expect(postRes3.post_view.counts.score).toBe(1);

// Make sure that post makes it to beta community
let searchBeta = await searchPostLocal(beta, postRes2.post_view.post);
let betaPost = searchBeta.posts[0];
expect(betaPost).toBeDefined();
let searchBeta2 = await searchPostLocal(beta, postRes3.post_view.post);
expect(searchBeta2.posts[0]).toBeDefined();
});

test('Report a post', async () => {
Expand Down
19 changes: 10 additions & 9 deletions api_tests/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,23 @@ export interface API {
}

export let alpha: API = {
client: new LemmyHttp('http://localhost:8541'),
client: new LemmyHttp('http://127.0.0.1:8541'),
};

export let beta: API = {
client: new LemmyHttp('http://localhost:8551'),
client: new LemmyHttp('http://127.0.0.1:8551'),
};

export let gamma: API = {
client: new LemmyHttp('http://localhost:8561'),
client: new LemmyHttp('http://127.0.0.1:8561'),
};

export let delta: API = {
client: new LemmyHttp('http://localhost:8571'),
client: new LemmyHttp('http://127.0.0.1:8571'),
};

export let epsilon: API = {
client: new LemmyHttp('http://localhost:8581'),
client: new LemmyHttp('http://127.0.0.1:8581'),
};

const password = 'lemmylemmy'
Expand Down Expand Up @@ -289,13 +289,14 @@ export async function resolvePerson(
export async function banPersonFromSite(
api: API,
person_id: number,
ban: boolean
ban: boolean,
remove_data: boolean,
): Promise<BanPersonResponse> {
// Make sure lemmy-beta/c/main is cached on lemmy_alpha
let form: BanPerson = {
person_id,
ban,
remove_data: false,
remove_data,
auth: api.auth,
};
return api.client.banPerson(form);
Expand All @@ -305,13 +306,13 @@ export async function banPersonFromCommunity(
api: API,
person_id: number,
community_id: number,
remove_data: boolean,
ban: boolean
): Promise<BanFromCommunityResponse> {
// Make sure lemmy-beta/c/main is cached on lemmy_alpha
let form: BanFromCommunity = {
person_id,
community_id,
remove_data: false,
remove_data,
ban,
auth: api.auth,
};
Expand Down
53 changes: 14 additions & 39 deletions crates/api/src/community.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,19 @@ use lemmy_api_common::{
community::*,
get_local_user_view_from_jwt,
is_mod_or_admin,
remove_user_data_in_community,
};
use lemmy_apub::{
activities::block::SiteOrCommunity,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::{
community::{
add_mod::AddMod,
block_user::BlockUserFromCommunity,
remove_mod::RemoveMod,
undo_block_user::UndoBlockUserFromCommunity,
},
block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
community::{add_mod::AddMod, remove_mod::RemoveMod},
following::{follow::FollowCommunity as FollowCommunityApub, undo_follow::UndoFollowCommunity},
},
};
use lemmy_db_schema::{
source::{
comment::Comment,
community::{
Community,
CommunityFollower,
Expand All @@ -43,12 +40,9 @@ use lemmy_db_schema::{
ModTransferCommunityForm,
},
person::Person,
post::Post,
site::Site,
},
traits::{Bannable, Blockable, Crud, Followable, Joinable},
};
use lemmy_db_views::comment_view::CommentQueryBuilder;
use lemmy_db_views_actor::{
community_moderator_view::CommunityModeratorView,
community_view::CommunityView,
Expand Down Expand Up @@ -214,6 +208,7 @@ impl Perform for BanFromCommunity {

let community_id = data.community_id;
let banned_person_id = data.person_id;
let remove_data = data.remove_data.unwrap_or(false);
let expires = data.expires.map(naive_from_unix);

// Verify that only mods or admins can ban
Expand Down Expand Up @@ -255,10 +250,12 @@ impl Perform for BanFromCommunity {
.await?
.ok();

BlockUserFromCommunity::send(
&community,
BlockUser::send(
&SiteOrCommunity::Community(community),
&banned_person,
&local_user_view.person.clone().into(),
remove_data,
data.reason.clone(),
expires,
context,
)
Expand All @@ -269,41 +266,19 @@ impl Perform for BanFromCommunity {
.await?
.map_err(LemmyError::from)
.map_err(|e| e.with_message("community_user_already_banned"))?;
UndoBlockUserFromCommunity::send(
&community,
UndoBlockUser::send(
&SiteOrCommunity::Community(community),
&banned_person,
&local_user_view.person.clone().into(),
data.reason.clone(),
context,
)
.await?;
}

// Remove/Restore their data if that's desired
if data.remove_data.unwrap_or(false) {
// Posts
blocking(context.pool(), move |conn: &'_ _| {
Post::update_removed_for_creator(conn, banned_person_id, Some(community_id), true)
})
.await??;

// Comments
// TODO Diesel doesn't allow updates with joins, so this has to be a loop
let comments = blocking(context.pool(), move |conn| {
CommentQueryBuilder::create(conn)
.creator_id(banned_person_id)
.community_id(community_id)
.limit(std::i64::MAX)
.list()
})
.await??;

for comment_view in &comments {
let comment_id = comment_view.comment.id;
blocking(context.pool(), move |conn: &'_ _| {
Comment::update_removed(conn, comment_id, true)
})
.await??;
}
if remove_data {
remove_user_data_in_community(community_id, banned_person_id, context.pool()).await?;
}

// Mod tables
Expand Down
Loading

0 comments on commit d912b00

Please sign in to comment.