mirror of
https://github.com/LemmyNet/lemmy
synced 2024-11-26 14:30:21 +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_activity_already_known,
|
||||||
is_addressed_to_public,
|
is_addressed_to_public,
|
||||||
receive_for_community::{
|
receive_for_community::{
|
||||||
|
receive_add_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,
|
||||||
receive_like_for_community,
|
receive_like_for_community,
|
||||||
|
receive_remove_for_community,
|
||||||
receive_undo_for_community,
|
receive_undo_for_community,
|
||||||
receive_update_for_community,
|
receive_update_for_community,
|
||||||
},
|
},
|
||||||
|
@ -51,7 +53,8 @@ pub enum CommunityValidTypes {
|
||||||
Like, // upvote post or comment
|
Like, // upvote post or comment
|
||||||
Dislike, // downvote post or comment
|
Dislike, // downvote post or comment
|
||||||
Delete, // post or comment deleted by creator
|
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>;
|
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?;
|
receive_delete_for_community(context, any_base.clone(), &actor_url).await?;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
CommunityValidTypes::Add => {
|
||||||
|
receive_add_for_community(context, any_base.clone(), &actor_url, request_counter).await?;
|
||||||
|
true
|
||||||
|
}
|
||||||
CommunityValidTypes::Remove => {
|
CommunityValidTypes::Remove => {
|
||||||
// TODO: we dont support remote mods, so this is ignored for now
|
receive_remove_for_community(context, any_base.clone(), &actor_url, request_counter).await?;
|
||||||
//receive_remove_for_community(context, any_base.clone(), &user_url).await?
|
true
|
||||||
false
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,21 +31,32 @@ use crate::{
|
||||||
receive_unhandled_activity,
|
receive_unhandled_activity,
|
||||||
verify_activity_domains_valid,
|
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,
|
find_post_or_comment_by_id,
|
||||||
inbox::is_addressed_to_public,
|
inbox::is_addressed_to_public,
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Create, Delete, Dislike, Like, Remove, Undo, Update},
|
activity::{ActorAndObjectRef, Add, Create, Delete, Dislike, Like, Remove, Undo, Update},
|
||||||
base::AnyBase,
|
base::AnyBase,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::{anyhow, Context};
|
||||||
use diesel::result::Error::NotFound;
|
use diesel::result::Error::NotFound;
|
||||||
use lemmy_api_structs::blocking;
|
use lemmy_api_structs::blocking;
|
||||||
use lemmy_db_queries::Crud;
|
use lemmy_db_queries::{ApubObject, Crud, Joinable};
|
||||||
use lemmy_db_schema::source::site::Site;
|
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_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use strum_macros::EnumString;
|
use strum_macros::EnumString;
|
||||||
|
@ -189,36 +200,59 @@ pub(in crate::inbox) async fn receive_remove_for_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
activity: AnyBase,
|
activity: AnyBase,
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let remove = Remove::from_any_base(activity)?.context(location_info!())?;
|
let remove = Remove::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&remove, &expected_domain, false)?;
|
verify_activity_domains_valid(&remove, &expected_domain, false)?;
|
||||||
is_addressed_to_public(&remove)?;
|
is_addressed_to_public(&remove)?;
|
||||||
|
|
||||||
let cc = remove
|
// Remove a moderator from community
|
||||||
.cc()
|
if remove.target().is_some() {
|
||||||
.map(|c| c.as_many())
|
let community = verify_actor_is_community_mod(&remove, context).await?;
|
||||||
.flatten()
|
|
||||||
.context(location_info!())?;
|
|
||||||
let community_id = cc
|
|
||||||
.first()
|
|
||||||
.map(|c| c.as_xsd_any_uri())
|
|
||||||
.flatten()
|
|
||||||
.context(location_info!())?;
|
|
||||||
|
|
||||||
let object = remove
|
let remove_mod = remove
|
||||||
.object()
|
.object()
|
||||||
.to_owned()
|
.as_single_xsd_any_uri()
|
||||||
.single_xsd_any_uri()
|
.context(location_info!())?;
|
||||||
.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
|
let object = remove
|
||||||
remove.id(community_id.domain().context(location_info!())?)?;
|
.object()
|
||||||
|
.to_owned()
|
||||||
|
.single_xsd_any_uri()
|
||||||
|
.context(location_info!())?;
|
||||||
|
|
||||||
match find_post_or_comment_by_id(context, object).await {
|
// Ensure that remove activity comes from the same domain as the community
|
||||||
Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, *p).await,
|
remove.id(community_id.domain().context(location_info!())?)?;
|
||||||
Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await,
|
|
||||||
// if we dont have the object, no need to do anything
|
match find_post_or_comment_by_id(context, object).await {
|
||||||
Err(_) => Ok(()),
|
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
|
/// A post or comment downvote being reverted
|
||||||
pub(in crate::inbox) async fn receive_undo_dislike_for_community(
|
pub(in crate::inbox) async fn receive_undo_dislike_for_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -374,3 +436,46 @@ async fn fetch_post_or_comment_by_id(
|
||||||
|
|
||||||
Err(NotFound.into())
|
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
|
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(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) => {
|
Some(Undo) => {
|
||||||
receive_undo_for_community(context, inner_activity, &inner_id, request_counter).await
|
receive_undo_for_community(context, inner_activity, &inner_id, request_counter).await
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue