-use crate::{activities::receive::verify_activity_domains_valid, inbox::is_addressed_to_public};
+use crate::{
+ activities::receive::verify_activity_domains_valid,
+ inbox::verify_is_addressed_to_public,
+};
use activitystreams::{
activity::{ActorAndObjectRefExt, Delete, Remove, Undo},
base::{AnyBase, ExtendsExt},
) -> Result<(), LemmyError> {
let remove = Remove::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&remove, expected_domain, true)?;
- is_addressed_to_public(&remove)?;
+ verify_is_addressed_to_public(&remove)?;
let community_uri = remove
.object()
community: Community,
expected_domain: &Url,
) -> Result<(), LemmyError> {
- is_addressed_to_public(&undo)?;
+ verify_is_addressed_to_public(&undo)?;
let inner = undo.object().to_owned().one().context(location_info!())?;
let delete = Delete::from_any_base(inner)?.context(location_info!())?;
verify_activity_domains_valid(&delete, expected_domain, true)?;
- is_addressed_to_public(&delete)?;
+ verify_is_addressed_to_public(&delete)?;
let deleted_community = blocking(context.pool(), move |conn| {
Community::update_deleted(conn, community.id, false)
undo: Undo,
expected_domain: &Url,
) -> Result<(), LemmyError> {
- is_addressed_to_public(&undo)?;
+ verify_is_addressed_to_public(&undo)?;
let inner = undo.object().to_owned().one().context(location_info!())?;
let remove = Remove::from_any_base(inner)?.context(location_info!())?;
verify_activity_domains_valid(&remove, &expected_domain, true)?;
- is_addressed_to_public(&remove)?;
+ verify_is_addressed_to_public(&remove)?;
let community_uri = remove
.object()
user::get_or_fetch_and_upsert_user,
},
find_post_or_comment_by_id,
- inbox::is_addressed_to_public,
+ generate_moderators_url,
+ inbox::verify_is_addressed_to_public,
ActorType,
PostOrComment,
};
activity::{
ActorAndObjectRef,
Add,
+ Announce,
Create,
Delete,
Dislike,
Update,
},
base::AnyBase,
+ object::AsObject,
prelude::*,
};
use anyhow::{anyhow, Context};
) -> Result<(), LemmyError> {
let create = Create::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&create, &expected_domain, true)?;
- is_addressed_to_public(&create)?;
+ verify_is_addressed_to_public(&create)?;
let kind = create
.object()
) -> Result<(), LemmyError> {
let update = Update::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&update, &expected_domain, true)?;
- is_addressed_to_public(&update)?;
+ verify_is_addressed_to_public(&update)?;
let kind = update
.object()
) -> Result<(), LemmyError> {
let like = Like::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&like, &expected_domain, false)?;
- is_addressed_to_public(&like)?;
+ verify_is_addressed_to_public(&like)?;
let object_id = like
.object()
let dislike = Dislike::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&dislike, &expected_domain, false)?;
- is_addressed_to_public(&dislike)?;
+ verify_is_addressed_to_public(&dislike)?;
let object_id = dislike
.object()
) -> Result<(), LemmyError> {
let delete = Delete::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&delete, &expected_domain, true)?;
- is_addressed_to_public(&delete)?;
+ verify_is_addressed_to_public(&delete)?;
let object = delete
.object()
/// A post or comment being removed by a mod/admin
pub(in crate::inbox) async fn receive_remove_for_community(
context: &LemmyContext,
- activity: AnyBase,
- expected_domain: &Url,
+ remove_any_base: AnyBase,
+ announce: Option<Announce>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let remove = Remove::from_any_base(activity.to_owned())?.context(location_info!())?;
- verify_activity_domains_valid(&remove, &expected_domain, false)?;
- is_addressed_to_public(&remove)?;
+ let remove = Remove::from_any_base(remove_any_base.to_owned())?.context(location_info!())?;
+ let community = extract_community_from_cc(&remove, context).await?;
- // Remove a moderator from community
- if remove.target().is_some() {
- let community = verify_actor_is_community_mod(&remove, context).await?;
+ verify_mod_activity(&remove, announce, &community, context).await?;
+ verify_is_addressed_to_public(&remove)?;
+ verify_actor_is_community_mod(&remove, &community, context).await?;
+ if remove.target().is_some() {
let remove_mod = remove
.object()
.as_single_xsd_any_uri()
CommunityModerator::leave(conn, &form)
})
.await??;
- community.send_announce(activity, context).await?;
+ community.send_announce(remove_any_base, context).await?;
// TODO: send websocket notification about removed mod
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!())?;
-
let object = remove
.object()
.to_owned()
.single_xsd_any_uri()
.context(location_info!())?;
- // 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,
) -> Result<(), LemmyError> {
let undo = Undo::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&undo, &expected_domain.to_owned(), true)?;
- is_addressed_to_public(&undo)?;
+ verify_is_addressed_to_public(&undo)?;
use UndoableActivities::*;
match undo
let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
verify_activity_domains_valid(&delete, &expected_domain, true)?;
- is_addressed_to_public(&delete)?;
+ verify_is_addressed_to_public(&delete)?;
let object = delete
.object()
let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
verify_activity_domains_valid(&remove, &expected_domain, false)?;
- is_addressed_to_public(&remove)?;
+ verify_is_addressed_to_public(&remove)?;
let object = remove
.object()
let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
verify_activity_domains_valid(&like, &expected_domain, false)?;
- is_addressed_to_public(&like)?;
+ verify_is_addressed_to_public(&like)?;
let object_id = like
.object()
/// 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,
+ add_any_base: AnyBase,
+ announce: Option<Announce>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let add = Add::from_any_base(activity.to_owned())?.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 add = Add::from_any_base(add_any_base.to_owned())?.context(location_info!())?;
+ let community = extract_community_from_cc(&add, context).await?;
+
+ verify_mod_activity(&add, announce, &community, context).await?;
+ verify_is_addressed_to_public(&add)?;
+ verify_actor_is_community_mod(&add, &community, context).await?;
+ verify_add_remove_moderator_target(&add, &community)?;
let new_mod = add
.object()
.await??;
}
if community.local {
- community.send_announce(activity, context).await?;
+ community.send_announce(add_any_base, context).await?;
}
// TODO: send websocket notification about added mod
Ok(())
let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
verify_activity_domains_valid(&dislike, &expected_domain, false)?;
- is_addressed_to_public(&dislike)?;
+ verify_is_addressed_to_public(&dislike)?;
let object_id = dislike
.object()
Err(NotFound.into())
}
-async fn verify_actor_is_community_mod<T, Kind>(
+async fn extract_community_from_cc<T, Kind>(
activity: &T,
context: &LemmyContext,
) -> Result<Community, LemmyError>
where
- T: ActorAndObjectRef + BaseExt<Kind> + OptTargetRef,
+ T: AsObject<Kind>,
{
- // should be the moderators collection of a local community
- let target = activity
- .target()
- .map(|t| t.as_single_xsd_any_uri())
+ let cc = activity
+ .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!())?;
- // TODO: very hacky, we should probably store the moderators url in db
- let community_id: DbUrl = Url::parse(&target.to_string().replace("/moderators", ""))?.into();
+ let community_id: DbUrl = community_id.to_owned().into();
let community = blocking(&context.pool(), move |conn| {
Community::read_from_apub_id(&conn, &community_id)
})
.await??;
+ Ok(community)
+}
+async fn verify_actor_is_community_mod<T, Kind>(
+ activity: &T,
+ community: &Community,
+ context: &LemmyContext,
+) -> Result<(), LemmyError>
+where
+ T: ActorAndObjectRef + BaseExt<Kind>,
+{
let actor = activity
.actor()?
.as_single_xsd_any_uri()
return Err(anyhow!("Not a mod").into());
}
- // TODO: the function name doesnt make sense if we return the community
- Ok(community)
+ Ok(())
+}
+
+async fn verify_mod_activity<T, Kind>(
+ mod_action: &T,
+ announce: Option<Announce>,
+ community: &Community,
+ context: &LemmyContext,
+) -> Result<(), LemmyError>
+where
+ T: ActorAndObjectRef + OptTargetRef + BaseExt<Kind>,
+{
+ // Remove was sent by community to user, we just check that it came from the right domain
+ if let Some(announce) = announce {
+ verify_activity_domains_valid(&announce, &community.actor_id.to_owned().into(), false)?;
+ }
+ // Remove was sent by a remote mod to community, check that they are actually mod
+ else {
+ verify_actor_is_community_mod(mod_action, community, context).await?;
+ }
+
+ Ok(())
+}
+fn verify_add_remove_moderator_target<T, Kind>(
+ activity: &T,
+ community: &Community,
+) -> Result<(), LemmyError>
+where
+ T: ActorAndObjectRef + BaseExt<Kind> + OptTargetRef,
+{
+ let target = activity
+ .target()
+ .map(|t| t.as_single_xsd_any_uri())
+ .flatten()
+ .context(location_info!())?;
+ if target != &generate_moderators_url(&community.actor_id)?.into_inner() {
+ return Err(anyhow!("Unkown target url").into());
+ }
+ Ok(())
}
is_activity_already_known,
is_addressed_to_community_followers,
is_addressed_to_local_user,
- is_addressed_to_public,
receive_for_community::{
receive_add_for_community,
receive_create_for_community,
receive_undo_for_community,
receive_update_for_community,
},
+ verify_is_addressed_to_public,
},
insert_activity,
ActorType,
) -> Result<(), LemmyError> {
let announce = Announce::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&announce, &actor.actor_id(), false)?;
- is_addressed_to_public(&announce)?;
+ verify_is_addressed_to_public(&announce)?;
let kind = announce
.object()
}
Some(Delete) => receive_delete_for_community(context, inner_activity, &inner_id).await,
Some(Remove) => {
- receive_remove_for_community(context, inner_activity, &inner_id, request_counter).await
+ receive_remove_for_community(context, inner_activity, Some(announce), request_counter).await
}
Some(Undo) => {
receive_undo_for_community(context, inner_activity, &inner_id, request_counter).await
}
Some(Add) => {
- receive_add_for_community(context, inner_activity, &inner_id, request_counter).await
+ receive_add_for_community(context, inner_activity, Some(announce), request_counter).await
}
_ => receive_unhandled_activity(inner_activity),
}
) -> Result<(), LemmyError> {
let create = Create::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&create, &expected_domain, true)?;
- if is_addressed_to_public(&create).is_ok() {
+ if verify_is_addressed_to_public(&create).is_ok() {
receive_create_comment(create, context, request_counter).await
} else {
receive_create_private_message(&context, create, expected_domain, request_counter).await
) -> Result<(), LemmyError> {
let update = Update::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&update, &expected_domain, true)?;
- if is_addressed_to_public(&update).is_ok() {
+ if verify_is_addressed_to_public(&update).is_ok() {
receive_update_comment(update, context, request_counter).await
} else {
receive_update_private_message(&context, update, expected_domain, request_counter).await