Implement federated bans (fixes #1298)

This commit is contained in:
Felix Ableitner 2021-04-06 18:33:00 +02:00
parent df7fe705eb
commit 45b39699a5
7 changed files with 212 additions and 14 deletions

View file

@ -134,6 +134,15 @@ impl Perform for BanFromCommunity {
person_id: data.person_id, person_id: data.person_id,
}; };
let community = blocking(context.pool(), move |conn: &'_ _| {
Community::read(conn, community_id)
})
.await??;
let banned_person = blocking(context.pool(), move |conn: &'_ _| {
Person::read(conn, banned_person_id)
})
.await??;
if data.ban { if data.ban {
let ban = move |conn: &'_ _| CommunityPersonBan::ban(conn, &community_user_ban_form); let ban = move |conn: &'_ _| CommunityPersonBan::ban(conn, &community_user_ban_form);
if blocking(context.pool(), ban).await?.is_err() { if blocking(context.pool(), ban).await?.is_err() {
@ -151,11 +160,17 @@ impl Perform for BanFromCommunity {
}) })
.await? .await?
.ok(); .ok();
community
.send_block_user(&local_user_view.person, banned_person, context)
.await?;
} else { } else {
let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form); let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
if blocking(context.pool(), unban).await?.is_err() { if blocking(context.pool(), unban).await?.is_err() {
return Err(ApiError::err("community_user_already_banned").into()); return Err(ApiError::err("community_user_already_banned").into());
} }
community
.send_undo_block_user(&local_user_view.person, banned_person, context)
.await?;
} }
// Remove/Restore their data if that's desired // Remove/Restore their data if that's desired

View file

@ -11,11 +11,21 @@ use crate::{
}; };
use activitystreams::{ use activitystreams::{
activity::{ activity::{
kind::{AcceptType, AddType, AnnounceType, DeleteType, LikeType, RemoveType, UndoType}, kind::{
AcceptType,
AddType,
AnnounceType,
BlockType,
DeleteType,
LikeType,
RemoveType,
UndoType,
},
Accept, Accept,
ActorAndObjectRefExt, ActorAndObjectRefExt,
Add, Add,
Announce, Announce,
Block,
Delete, Delete,
Follow, Follow,
OptTargetRefExt, OptTargetRefExt,
@ -170,7 +180,7 @@ impl CommunityType for Community {
insert_activity(inner_id, activity.clone(), true, false, context.pool()).await?; insert_activity(inner_id, activity.clone(), true, false, context.pool()).await?;
} }
let mut announce = Announce::new(self.actor_id.to_owned().into_inner(), activity); let mut announce = Announce::new(self.actor_id(), activity);
announce announce
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(AnnounceType::Announce)?) .set_id(generate_activity_id(AnnounceType::Announce)?)
@ -209,10 +219,7 @@ impl CommunityType for Community {
added_mod: Person, added_mod: Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let mut add = Add::new( let mut add = Add::new(actor.actor_id(), added_mod.actor_id());
actor.actor_id.clone().into_inner(),
added_mod.actor_id.into_inner(),
);
add add
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(AddType::Add)?) .set_id(generate_activity_id(AddType::Add)?)
@ -230,10 +237,7 @@ impl CommunityType for Community {
removed_mod: Person, removed_mod: Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let mut remove = Remove::new( let mut remove = Remove::new(actor.actor_id(), removed_mod.actor_id());
actor.actor_id.clone().into_inner(),
removed_mod.actor_id.into_inner(),
);
remove remove
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(RemoveType::Remove)?) .set_id(generate_activity_id(RemoveType::Remove)?)
@ -244,4 +248,46 @@ impl CommunityType for Community {
send_to_community(remove, &actor, self, context).await?; send_to_community(remove, &actor, self, context).await?;
Ok(()) Ok(())
} }
// / TODO: also need to implement the Undo for this
async fn send_block_user(
&self,
actor: &Person,
blocked_user: Person,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let mut block = Block::new(actor.actor_id(), blocked_user.actor_id());
block
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(BlockType::Block)?)
.set_to(public())
.set_many_ccs(vec![self.actor_id()]);
send_to_community(block, &actor, self, context).await?;
Ok(())
}
async fn send_undo_block_user(
&self,
actor: &Person,
blocked_user: Person,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let mut block = Block::new(actor.actor_id(), blocked_user.actor_id());
block
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(BlockType::Block)?)
.set_to(public())
.set_many_ccs(vec![self.actor_id()]);
// Undo that fake activity
let mut undo = Undo::new(actor.actor_id(), block.into_any_base()?);
undo
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(UndoType::Undo)?)
.set_to(public())
.set_many_ccs(vec![self.followers_url.clone().into_inner()]);
send_to_community(undo, &actor, self, context).await?;
Ok(())
}
} }

View file

@ -74,7 +74,7 @@ impl UserType for Person {
}) })
.await?; .await?;
let mut follow = Follow::new(self.actor_id.to_owned().into_inner(), community.actor_id()); let mut follow = Follow::new(self.actor_id(), community.actor_id());
follow follow
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(FollowType::Follow)?) .set_id(generate_activity_id(FollowType::Follow)?)
@ -95,7 +95,7 @@ impl UserType for Person {
}) })
.await??; .await??;
let mut follow = Follow::new(self.actor_id.to_owned().into_inner(), community.actor_id()); let mut follow = Follow::new(self.actor_id(), community.actor_id());
follow follow
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(FollowType::Follow)?) .set_id(generate_activity_id(FollowType::Follow)?)

View file

@ -222,6 +222,19 @@ pub trait CommunityType {
removed_mod: Person, removed_mod: Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError>; ) -> Result<(), LemmyError>;
async fn send_block_user(
&self,
actor: &Person,
blocked_user: Person,
context: &LemmyContext,
) -> Result<(), LemmyError>;
async fn send_undo_block_user(
&self,
actor: &Person,
blocked_user: Person,
context: &LemmyContext,
) -> Result<(), LemmyError>;
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]

View file

@ -7,6 +7,7 @@ use crate::{
is_activity_already_known, is_activity_already_known,
receive_for_community::{ receive_for_community::{
receive_add_for_community, receive_add_for_community,
receive_block_user_for_community,
receive_create_for_community, receive_create_for_community,
receive_delete_for_community, receive_delete_for_community,
receive_dislike_for_community, receive_dislike_for_community,
@ -58,6 +59,7 @@ pub enum CommunityValidTypes {
Delete, // post or comment deleted by creator Delete, // post or comment deleted by creator
Remove, // post or comment removed by mod or admin, or mod removed from community Remove, // post or comment removed by mod or admin, or mod removed from community
Add, // mod added to community Add, // mod added to community
Block, // user blocked by community
} }
pub type CommunityAcceptedActivities = ActorAndObject<CommunityValidTypes>; pub type CommunityAcceptedActivities = ActorAndObject<CommunityValidTypes>;
@ -224,6 +226,16 @@ pub(crate) async fn community_receive_message(
.await?; .await?;
true true
} }
CommunityValidTypes::Block => {
Box::pin(receive_block_user_for_community(
context,
any_base.clone(),
None,
request_counter,
))
.await?;
true
}
}; };
if do_announce { if do_announce {

View file

@ -25,6 +25,7 @@ use crate::{
is_addressed_to_local_person, is_addressed_to_local_person,
receive_for_community::{ receive_for_community::{
receive_add_for_community, receive_add_for_community,
receive_block_user_for_community,
receive_create_for_community, receive_create_for_community,
receive_delete_for_community, receive_delete_for_community,
receive_dislike_for_community, receive_dislike_for_community,
@ -276,6 +277,7 @@ enum AnnouncableActivities {
Remove, Remove,
Undo, Undo,
Add, Add,
Block,
} }
/// Takes an announce and passes the inner activity to the appropriate handler. /// Takes an announce and passes the inner activity to the appropriate handler.
@ -352,6 +354,10 @@ pub async fn receive_announce(
Some(Add) => { Some(Add) => {
receive_add_for_community(context, inner_activity, Some(announce), request_counter).await receive_add_for_community(context, inner_activity, Some(announce), request_counter).await
} }
Some(Block) => {
receive_block_user_for_community(context, inner_activity, Some(announce), request_counter)
.await
}
_ => receive_unhandled_activity(inner_activity), _ => receive_unhandled_activity(inner_activity),
} }
} }

View file

@ -38,6 +38,7 @@ use activitystreams::{
ActorAndObjectRef, ActorAndObjectRef,
Add, Add,
Announce, Announce,
Block,
Create, Create,
Delete, Delete,
Dislike, Dislike,
@ -64,10 +65,25 @@ use lemmy_apub::{
CommunityType, CommunityType,
PostOrComment, PostOrComment,
}; };
use lemmy_db_queries::{source::community::CommunityModerator_, ApubObject, Crud, Joinable}; use lemmy_db_queries::{
source::community::CommunityModerator_,
ApubObject,
Bannable,
Crud,
Followable,
Joinable,
};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::{Community, CommunityModerator, CommunityModeratorForm}, community::{
Community,
CommunityFollower,
CommunityFollowerForm,
CommunityModerator,
CommunityModeratorForm,
CommunityPersonBan,
CommunityPersonBanForm,
},
person::Person, person::Person,
site::Site, site::Site,
}, },
@ -271,6 +287,7 @@ enum UndoableActivities {
Remove, Remove,
Like, Like,
Dislike, Dislike,
Block,
} }
/// A post/comment action being reverted (either a delete, remove, upvote or downvote) /// A post/comment action being reverted (either a delete, remove, upvote or downvote)
@ -301,6 +318,16 @@ pub(in crate::inbox) async fn receive_undo_for_community(
Some(Dislike) => { Some(Dislike) => {
receive_undo_dislike_for_community(context, undo, expected_domain, request_counter).await receive_undo_dislike_for_community(context, undo, expected_domain, request_counter).await
} }
Some(Block) => {
receive_undo_block_user_for_community(
context,
undo,
announce,
expected_domain,
request_counter,
)
.await
}
_ => receive_unhandled_activity(undo), _ => receive_unhandled_activity(undo),
} }
} }
@ -451,6 +478,85 @@ pub(in crate::inbox) async fn receive_undo_dislike_for_community(
} }
} }
pub(crate) async fn receive_block_user_for_community(
context: &LemmyContext,
block_any_base: AnyBase,
announce: Option<Announce>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let block = Block::from_any_base(block_any_base.to_owned())?.context(location_info!())?;
let community = extract_community_from_cc(&block, context).await?;
verify_mod_activity(&block, announce, &community, context).await?;
verify_is_addressed_to_public(&block)?;
let blocked_user = block
.object()
.as_single_xsd_any_uri()
.context(location_info!())?;
let blocked_user =
get_or_fetch_and_upsert_person(&blocked_user, context, request_counter).await?;
let community_user_ban_form = CommunityPersonBanForm {
community_id: community.id,
person_id: blocked_user.id,
};
blocking(context.pool(), move |conn: &'_ _| {
CommunityPersonBan::ban(conn, &community_user_ban_form)
})
.await??;
// Also unsubscribe them from the community, if they are subscribed
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
person_id: blocked_user.id,
pending: false,
};
blocking(context.pool(), move |conn: &'_ _| {
CommunityFollower::unfollow(conn, &community_follower_form)
})
.await?
.ok();
Ok(())
}
pub(crate) async fn receive_undo_block_user_for_community(
context: &LemmyContext,
undo: Undo,
announce: Option<Announce>,
expected_domain: &Url,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let object = undo.object().clone().one().context(location_info!())?;
let block = Block::from_any_base(object)?.context(location_info!())?;
let community = extract_community_from_cc(&block, context).await?;
verify_activity_domains_valid(&block, &expected_domain, false)?;
verify_is_addressed_to_public(&block)?;
verify_undo_remove_actor_instance(&undo, &block, &announce, context).await?;
let blocked_user = block
.object()
.as_single_xsd_any_uri()
.context(location_info!())?;
let blocked_user =
get_or_fetch_and_upsert_person(&blocked_user, context, request_counter).await?;
let community_user_ban_form = CommunityPersonBanForm {
community_id: community.id,
person_id: blocked_user.id,
};
blocking(context.pool(), move |conn: &'_ _| {
CommunityPersonBan::unban(conn, &community_user_ban_form)
})
.await??;
Ok(())
}
async fn fetch_post_or_comment_by_id( async fn fetch_post_or_comment_by_id(
apub_id: &Url, apub_id: &Url,
context: &LemmyContext, context: &LemmyContext,