move block/unblock activities

This commit is contained in:
Felix Ableitner 2021-06-29 03:28:43 +02:00
parent ab5b072e21
commit e1e54672c6
7 changed files with 161 additions and 284 deletions

View file

@ -0,0 +1,76 @@
use crate::{activities_new::verify_mod_action, inbox::new_inbox_routing::Activity};
use activitystreams::activity::kind::BlockType;
use lemmy_api_common::blocking;
use lemmy_apub::{
check_is_apub_id_valid,
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
};
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
use lemmy_db_queries::{Bannable, Followable};
use lemmy_db_schema::source::community::{
CommunityFollower,
CommunityFollowerForm,
CommunityPersonBan,
CommunityPersonBanForm,
};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BlockUserFromCommunity {
actor: Url,
to: PublicUrl,
pub(in crate::activities_new::community) object: Url,
cc: [Url; 1],
#[serde(rename = "type")]
kind: BlockType,
}
#[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<BlockUserFromCommunity> {
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
check_is_apub_id_valid(&self.inner.actor, false)?;
verify_mod_action(self.inner.actor.clone(), self.inner.cc[0].clone(), context).await
}
}
#[async_trait::async_trait(?Send)]
impl ReceiveActivity for Activity<BlockUserFromCommunity> {
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let community =
get_or_fetch_and_upsert_community(&self.inner.cc[0], context, request_counter).await?;
let blocked_user =
get_or_fetch_and_upsert_person(&self.inner.object, 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(())
}
}

View file

@ -10,8 +10,10 @@ use lemmy_utils::LemmyError;
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext};
use url::Url;
pub mod block_user;
pub mod delete;
pub mod remove;
pub mod undo_block_user;
pub mod undo_delete;
pub mod undo_remove;
pub mod update;
@ -38,6 +40,7 @@ async fn send_websocket_message<OP: ToString + Send + lemmy_websocket::Operation
Ok(())
}
// TODO: why do we have this and verify_mod_action() ?
async fn verify_is_community_mod(
actor: Url,
community: Url,

View file

@ -0,0 +1,64 @@
use crate::{
activities_new::{community::block_user::BlockUserFromCommunity, verify_mod_action},
inbox::new_inbox_routing::Activity,
};
use activitystreams::activity::kind::BlockType;
use lemmy_api_common::blocking;
use lemmy_apub::{
check_is_apub_id_valid,
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
};
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
use lemmy_db_queries::Bannable;
use lemmy_db_schema::source::community::{CommunityPersonBan, CommunityPersonBanForm};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UndoBlockUserFromCommunity {
actor: Url,
to: PublicUrl,
object: Activity<BlockUserFromCommunity>,
cc: [Url; 1],
#[serde(rename = "type")]
kind: BlockType,
}
#[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<UndoBlockUserFromCommunity> {
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
check_is_apub_id_valid(&self.inner.actor, false)?;
verify_mod_action(self.inner.actor.clone(), self.inner.cc[0].clone(), context).await?;
self.inner.object.verify(context).await
}
}
#[async_trait::async_trait(?Send)]
impl ReceiveActivity for Activity<UndoBlockUserFromCommunity> {
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let community =
get_or_fetch_and_upsert_community(&self.inner.cc[0], context, request_counter).await?;
let blocked_user =
get_or_fetch_and_upsert_person(&self.inner.object.inner.object, 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(())
}
}

View file

@ -3,18 +3,10 @@ use crate::inbox::{
get_activity_id,
inbox_verify_http_signature,
is_activity_already_known,
receive_for_community::{
receive_add_for_community,
receive_block_user_for_community,
receive_remove_for_community,
receive_undo_for_community,
},
receive_for_community::receive_add_for_community,
verify_is_addressed_to_public,
};
use activitystreams::{
activity::{kind::FollowType, ActorAndObject},
prelude::*,
};
use activitystreams::{activity::ActorAndObject, prelude::*};
use actix_web::{web, HttpRequest, HttpResponse};
use anyhow::{anyhow, Context};
use lemmy_api_common::blocking;
@ -32,7 +24,6 @@ use lemmy_websocket::LemmyContext;
use log::info;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use url::Url;
/// Allowed activities for community inbox.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
@ -122,20 +113,10 @@ pub(crate) async fn community_receive_message(
);
let any_base = activity.clone().into_any_base()?;
let actor_url = actor.actor_id();
let activity_kind = activity.kind().context(location_info!())?;
let do_announce = match activity_kind {
CommunityValidTypes::Follow => todo!(),
CommunityValidTypes::Undo => {
Box::pin(handle_undo(
context,
activity.clone(),
actor_url,
&to_community,
request_counter,
))
.await?
}
CommunityValidTypes::Undo => todo!(),
CommunityValidTypes::Create => todo!(),
CommunityValidTypes::Update => todo!(),
CommunityValidTypes::Like => todo!(),
@ -151,26 +132,8 @@ pub(crate) async fn community_receive_message(
.await?;
true
}
CommunityValidTypes::Remove => {
Box::pin(receive_remove_for_community(
context,
any_base.clone(),
None,
request_counter,
))
.await?;
true
}
CommunityValidTypes::Block => {
Box::pin(receive_block_user_for_community(
context,
any_base.clone(),
None,
request_counter,
))
.await?;
true
}
CommunityValidTypes::Remove => todo!(),
CommunityValidTypes::Block => todo!(),
};
if do_announce {
@ -195,22 +158,3 @@ pub(crate) async fn community_receive_message(
Ok(HttpResponse::Ok().finish())
}
async fn handle_undo(
context: &LemmyContext,
activity: CommunityAcceptedActivities,
actor_url: Url,
_to_community: &Community,
request_counter: &mut i32,
) -> Result<bool, LemmyError> {
let inner_kind = activity
.object()
.is_single_kind(&FollowType::Follow.to_string());
let any_base = activity.into_any_base()?;
if inner_kind {
todo!()
} else {
receive_undo_for_community(context, any_base, None, &actor_url, request_counter).await?;
Ok(true)
}
}

View file

@ -68,9 +68,8 @@ impl<Kind> Activity<Kind> {
}
}
// TODO: this is probably wrong, it contains all activities
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub enum PersonAcceptedActivitiesNew {
pub enum SharedInboxActivities {
FollowCommunity(FollowCommunity),
AcceptFollowCommunity(AcceptFollowCommunity),
UndoFollowCommunity(UndoFollowCommunity),
@ -107,14 +106,14 @@ pub enum PersonAcceptedActivitiesNew {
// todo: can probably get rid of these?
#[async_trait::async_trait(?Send)]
impl VerifyActivity for PersonAcceptedActivitiesNew {
impl VerifyActivity for SharedInboxActivities {
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
self.verify(context).await
}
}
#[async_trait::async_trait(?Send)]
impl ReceiveActivity for PersonAcceptedActivitiesNew {
impl ReceiveActivity for SharedInboxActivities {
async fn receive(
&self,
context: &LemmyContext,

View file

@ -4,13 +4,8 @@ use crate::{
is_activity_already_known,
is_addressed_to_community_followers,
is_addressed_to_local_person,
new_inbox_routing::{Activity, PersonAcceptedActivitiesNew},
receive_for_community::{
receive_add_for_community,
receive_block_user_for_community,
receive_remove_for_community,
receive_undo_for_community,
},
new_inbox_routing::{Activity, SharedInboxActivities},
receive_for_community::receive_add_for_community,
verify_is_addressed_to_public,
},
};
@ -51,7 +46,7 @@ pub type PersonAcceptedActivities = ActorAndObject<PersonValidTypes>;
/// Handler for all incoming activities to person inboxes.
pub async fn person_inbox(
_request: HttpRequest,
input: web::Json<Activity<PersonAcceptedActivitiesNew>>,
input: web::Json<Activity<SharedInboxActivities>>,
_path: web::Path<String>,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
@ -217,26 +212,12 @@ pub async fn receive_announce(
Some(Like) => todo!(),
Some(Dislike) => todo!(),
Some(Delete) => todo!(),
Some(Remove) => {
receive_remove_for_community(context, inner_activity, Some(announce), request_counter).await
}
Some(Undo) => {
receive_undo_for_community(
context,
inner_activity,
Some(announce),
&inner_id,
request_counter,
)
.await
}
Some(Remove) => todo!(),
Some(Undo) => todo!(),
Some(Add) => {
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
}
Some(Block) => todo!(),
_ => receive_unhandled_activity(inner_activity),
}
}

View file

@ -1,9 +1,9 @@
use crate::{
activities::receive::{receive_unhandled_activity, verify_activity_domains_valid},
activities::receive::verify_activity_domains_valid,
inbox::verify_is_addressed_to_public,
};
use activitystreams::{
activity::{ActorAndObjectRef, Add, Announce, Block, OptTargetRef, Remove, Undo},
activity::{ActorAndObjectRef, Add, Announce, OptTargetRef},
base::AnyBase,
object::AsObject,
prelude::*,
@ -15,24 +15,10 @@ use lemmy_apub::{
generate_moderators_url,
CommunityType,
};
use lemmy_db_queries::{
source::community::CommunityModerator_,
ApubObject,
Bannable,
Followable,
Joinable,
};
use lemmy_db_queries::{source::community::CommunityModerator_, ApubObject, Joinable};
use lemmy_db_schema::{
source::{
community::{
Community,
CommunityFollower,
CommunityFollowerForm,
CommunityModerator,
CommunityModeratorForm,
CommunityPersonBan,
CommunityPersonBanForm,
},
community::{Community, CommunityModerator, CommunityModeratorForm},
person::Person,
},
DbUrl,
@ -41,7 +27,6 @@ use lemmy_db_views_actor::community_view::CommunityView;
use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext;
use strum_macros::EnumString;
use url::Url;
#[derive(EnumString)]
enum PageOrNote {
@ -57,48 +42,6 @@ enum ObjectTypes {
Person,
}
/// This file is for post/comment activities received by the community, and for post/comment
/// activities announced by the community and received by the person.
/// A post or comment being removed by a mod/admin
pub(in crate::inbox) async fn receive_remove_for_community(
context: &LemmyContext,
remove_any_base: AnyBase,
announce: Option<Announce>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let remove = Remove::from_any_base(remove_any_base.to_owned())?.context(location_info!())?;
let community = extract_community_from_cc(&remove, context).await?;
verify_mod_activity(&remove, announce, &community, context).await?;
verify_is_addressed_to_public(&remove)?;
if remove.target().is_some() {
let remove_mod = remove
.object()
.as_single_xsd_any_uri()
.context(location_info!())?;
let remove_mod = get_or_fetch_and_upsert_person(&remove_mod, context, request_counter).await?;
let form = CommunityModeratorForm {
community_id: community.id,
person_id: remove_mod.id,
};
blocking(context.pool(), move |conn| {
CommunityModerator::leave(conn, &form)
})
.await??;
community
.send_announce(
remove_any_base,
remove.object().clone().single_xsd_any_uri(),
context,
)
.await?;
// TODO: send websocket notification about removed mod
}
Ok(())
}
#[derive(EnumString)]
enum UndoableActivities {
Delete,
@ -108,42 +51,6 @@ enum UndoableActivities {
Block,
}
/// A post/comment action being reverted (either a delete, remove, upvote or downvote)
pub(in crate::inbox) async fn receive_undo_for_community(
context: &LemmyContext,
activity: AnyBase,
announce: Option<Announce>,
expected_domain: &Url,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let undo = Undo::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&undo, &expected_domain.to_owned(), true)?;
verify_is_addressed_to_public(&undo)?;
use UndoableActivities::*;
match undo
.object()
.as_single_kind_str()
.and_then(|s| s.parse().ok())
{
Some(Delete) => todo!(),
Some(Remove) => todo!(),
Some(Like) => todo!(),
Some(Dislike) => todo!(),
Some(Block) => {
receive_undo_block_user_for_community(
context,
undo,
announce,
expected_domain,
request_counter,
)
.await
}
_ => receive_unhandled_activity(undo),
}
}
/// Add a new mod to the community (can only be done by an existing mod).
pub(in crate::inbox) async fn receive_add_for_community(
context: &LemmyContext,
@ -194,85 +101,6 @@ pub(in crate::inbox) async fn receive_add_for_community(
Ok(())
}
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(())
}
/// Searches the activity's cc field for a Community ID, and returns the community.
async fn extract_community_from_cc<T, Kind>(
activity: &T,
@ -384,21 +212,3 @@ where
}
Ok(())
}
pub(crate) async fn verify_undo_remove_actor_instance<T, Kind>(
undo: &Undo,
inner: &T,
announce: &Option<Announce>,
context: &LemmyContext,
) -> Result<(), LemmyError>
where
T: ActorAndObjectRef + BaseExt<Kind> + AsObject<Kind>,
{
if announce.is_none() {
let community = extract_community_from_cc(undo, context).await?;
verify_mod_activity(undo, announce.to_owned(), &community, context).await?;
verify_mod_activity(inner, announce.to_owned(), &community, context).await?;
}
Ok(())
}