mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-24 17:00:36 +00:00
Implemented receiving activities to add/remove remote mods
This commit is contained in:
parent
0c484e8c76
commit
9172eff65a
3 changed files with 145 additions and 32 deletions
|
@ -8,10 +8,12 @@ use crate::{
|
|||
is_activity_already_known,
|
||||
is_addressed_to_public,
|
||||
receive_for_community::{
|
||||
receive_add_for_community,
|
||||
receive_create_for_community,
|
||||
receive_delete_for_community,
|
||||
receive_dislike_for_community,
|
||||
receive_like_for_community,
|
||||
receive_remove_for_community,
|
||||
receive_undo_for_community,
|
||||
receive_update_for_community,
|
||||
},
|
||||
|
@ -51,7 +53,8 @@ pub enum CommunityValidTypes {
|
|||
Like, // upvote post or comment
|
||||
Dislike, // downvote post or comment
|
||||
Delete, // post or comment deleted by creator
|
||||
Remove, // post or comment removed by mod or admin
|
||||
Remove, // post or comment removed by mod or admin, or mod removed from community
|
||||
Add, // mod added to community
|
||||
}
|
||||
|
||||
pub type CommunityAcceptedActivities = ActorAndObject<CommunityValidTypes>;
|
||||
|
@ -160,10 +163,13 @@ pub(crate) async fn community_receive_message(
|
|||
receive_delete_for_community(context, any_base.clone(), &actor_url).await?;
|
||||
true
|
||||
}
|
||||
CommunityValidTypes::Add => {
|
||||
receive_add_for_community(context, any_base.clone(), &actor_url, request_counter).await?;
|
||||
true
|
||||
}
|
||||
CommunityValidTypes::Remove => {
|
||||
// TODO: we dont support remote mods, so this is ignored for now
|
||||
//receive_remove_for_community(context, any_base.clone(), &user_url).await?
|
||||
false
|
||||
receive_remove_for_community(context, any_base.clone(), &actor_url, request_counter).await?;
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -31,21 +31,32 @@ use crate::{
|
|||
receive_unhandled_activity,
|
||||
verify_activity_domains_valid,
|
||||
},
|
||||
fetcher::objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||
fetcher::{
|
||||
objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||
user::get_or_fetch_and_upsert_user,
|
||||
},
|
||||
find_post_or_comment_by_id,
|
||||
inbox::is_addressed_to_public,
|
||||
PostOrComment,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{Create, Delete, Dislike, Like, Remove, Undo, Update},
|
||||
activity::{ActorAndObjectRef, Add, Create, Delete, Dislike, Like, Remove, Undo, Update},
|
||||
base::AnyBase,
|
||||
prelude::*,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use anyhow::{anyhow, Context};
|
||||
use diesel::result::Error::NotFound;
|
||||
use lemmy_api_structs::blocking;
|
||||
use lemmy_db_queries::Crud;
|
||||
use lemmy_db_schema::source::site::Site;
|
||||
use lemmy_db_queries::{ApubObject, Crud, Joinable};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::{Community, CommunityModerator, CommunityModeratorForm},
|
||||
site::Site,
|
||||
user::User_,
|
||||
},
|
||||
DbUrl,
|
||||
};
|
||||
use lemmy_db_views_actor::community_view::CommunityView;
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use strum_macros::EnumString;
|
||||
|
@ -189,36 +200,59 @@ pub(in crate::inbox) async fn receive_remove_for_community(
|
|||
context: &LemmyContext,
|
||||
activity: AnyBase,
|
||||
expected_domain: &Url,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let remove = Remove::from_any_base(activity)?.context(location_info!())?;
|
||||
verify_activity_domains_valid(&remove, &expected_domain, false)?;
|
||||
is_addressed_to_public(&remove)?;
|
||||
|
||||
let cc = remove
|
||||
.cc()
|
||||
.map(|c| c.as_many())
|
||||
.flatten()
|
||||
.context(location_info!())?;
|
||||
let community_id = cc
|
||||
.first()
|
||||
.map(|c| c.as_xsd_any_uri())
|
||||
.flatten()
|
||||
.context(location_info!())?;
|
||||
// Remove a moderator from community
|
||||
if remove.target().is_some() {
|
||||
let community = verify_actor_is_community_mod(&remove, context).await?;
|
||||
|
||||
let object = remove
|
||||
.object()
|
||||
.to_owned()
|
||||
.single_xsd_any_uri()
|
||||
.context(location_info!())?;
|
||||
let remove_mod = remove
|
||||
.object()
|
||||
.as_single_xsd_any_uri()
|
||||
.context(location_info!())?;
|
||||
let remove_mod = get_or_fetch_and_upsert_user(&remove_mod, context, request_counter).await?;
|
||||
let form = CommunityModeratorForm {
|
||||
community_id: community.id,
|
||||
user_id: remove_mod.id,
|
||||
};
|
||||
blocking(context.pool(), move |conn| {
|
||||
CommunityModerator::leave(conn, &form)
|
||||
})
|
||||
.await??;
|
||||
Ok(())
|
||||
}
|
||||
// Remove a post or comment
|
||||
else {
|
||||
let cc = remove
|
||||
.cc()
|
||||
.map(|c| c.as_many())
|
||||
.flatten()
|
||||
.context(location_info!())?;
|
||||
let community_id = cc
|
||||
.first()
|
||||
.map(|c| c.as_xsd_any_uri())
|
||||
.flatten()
|
||||
.context(location_info!())?;
|
||||
|
||||
// Ensure that remove activity comes from the same domain as the community
|
||||
remove.id(community_id.domain().context(location_info!())?)?;
|
||||
let object = remove
|
||||
.object()
|
||||
.to_owned()
|
||||
.single_xsd_any_uri()
|
||||
.context(location_info!())?;
|
||||
|
||||
match find_post_or_comment_by_id(context, object).await {
|
||||
Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, *p).await,
|
||||
Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await,
|
||||
// if we dont have the object, no need to do anything
|
||||
Err(_) => Ok(()),
|
||||
// Ensure that remove activity comes from the same domain as the community
|
||||
remove.id(community_id.domain().context(location_info!())?)?;
|
||||
|
||||
match find_post_or_comment_by_id(context, object).await {
|
||||
Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, *p).await,
|
||||
Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await,
|
||||
// if we dont have the object, no need to do anything
|
||||
Err(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,6 +367,34 @@ pub(in crate::inbox) async fn receive_undo_like_for_community(
|
|||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
activity: AnyBase,
|
||||
expected_domain: &Url,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let add = Add::from_any_base(activity)?.context(location_info!())?;
|
||||
verify_activity_domains_valid(&add, &expected_domain, false)?;
|
||||
is_addressed_to_public(&add)?;
|
||||
let community = verify_actor_is_community_mod(&add, context).await?;
|
||||
|
||||
let new_mod = add
|
||||
.object()
|
||||
.as_single_xsd_any_uri()
|
||||
.context(location_info!())?;
|
||||
let new_mod = get_or_fetch_and_upsert_user(&new_mod, context, request_counter).await?;
|
||||
let form = CommunityModeratorForm {
|
||||
community_id: community.id,
|
||||
user_id: new_mod.id,
|
||||
};
|
||||
blocking(context.pool(), move |conn| {
|
||||
CommunityModerator::join(conn, &form)
|
||||
})
|
||||
.await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A post or comment downvote being reverted
|
||||
pub(in crate::inbox) async fn receive_undo_dislike_for_community(
|
||||
context: &LemmyContext,
|
||||
|
@ -374,3 +436,46 @@ async fn fetch_post_or_comment_by_id(
|
|||
|
||||
Err(NotFound.into())
|
||||
}
|
||||
|
||||
async fn verify_actor_is_community_mod<T, Kind>(
|
||||
activity: &T,
|
||||
context: &LemmyContext,
|
||||
) -> Result<Community, LemmyError>
|
||||
where
|
||||
T: ActorAndObjectRef + BaseExt<Kind>,
|
||||
{
|
||||
// should be the moderators collection of a local community
|
||||
// TODO: not compiling, seems to be a bug in activitystreams crate
|
||||
let target = Url::parse("")?; //activity.target().as_single_xsd_any_uri().context(location_info!())?;
|
||||
// TODO: very hacky
|
||||
let community_id: DbUrl = Url::parse(&target.to_string().replace("/moderators", ""))?.into();
|
||||
let community = blocking(&context.pool(), move |conn| {
|
||||
Community::read_from_apub_id(&conn, &community_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let actor = activity
|
||||
.actor()?
|
||||
.as_single_xsd_any_uri()
|
||||
.context(location_info!())?
|
||||
.to_owned();
|
||||
let actor = blocking(&context.pool(), move |conn| {
|
||||
User_::read_from_apub_id(&conn, &actor.into())
|
||||
})
|
||||
.await??;
|
||||
|
||||
// Note: this will also return true for admins in addition to mods, but as we dont know about
|
||||
// remote admins, it doesnt make any difference.
|
||||
let community_id = community.id;
|
||||
let actor_id = actor.id;
|
||||
let is_mod_or_admin = blocking(context.pool(), move |conn| {
|
||||
CommunityView::is_mod_or_admin(conn, actor_id, community_id)
|
||||
})
|
||||
.await?;
|
||||
if !is_mod_or_admin {
|
||||
return Err(anyhow!("Not a mod").into());
|
||||
}
|
||||
|
||||
// TODO: the function name doesnt make sense if we return the community
|
||||
Ok(community)
|
||||
}
|
||||
|
|
|
@ -296,7 +296,9 @@ pub async fn receive_announce(
|
|||
receive_dislike_for_community(context, inner_activity, &inner_id, request_counter).await
|
||||
}
|
||||
Some(Delete) => receive_delete_for_community(context, inner_activity, &inner_id).await,
|
||||
Some(Remove) => receive_remove_for_community(context, inner_activity, &inner_id).await,
|
||||
Some(Remove) => {
|
||||
receive_remove_for_community(context, inner_activity, &inner_id, request_counter).await
|
||||
}
|
||||
Some(Undo) => {
|
||||
receive_undo_for_community(context, inner_activity, &inner_id, request_counter).await
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue