Fix verify_mod_action check for remote admin actions (#2190)

* Fix verify_mod_action check for remote admin actions

* fix federation test
This commit is contained in:
Nutomic 2022-04-04 14:46:49 +00:00 committed by GitHub
parent c7f5337099
commit 65cac21713
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 74 additions and 14 deletions

View file

@ -225,9 +225,10 @@ test('Delete a post', async () => {
}); });
test('Remove a post from admin and community on different instance', async () => { test('Remove a post from admin and community on different instance', async () => {
let postRes = await createPost(alpha, betaCommunity.community.id); let postRes = await createPost(gamma, betaCommunity.community.id);
let removedPost = await removePost(alpha, true, postRes.post_view.post); let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post;
let removedPost = await removePost(alpha, true, alphaPost.post);
expect(removedPost.post_view.post.removed).toBe(true); expect(removedPost.post_view.post.removed).toBe(true);
expect(removedPost.post_view.post.name).toBe(postRes.post_view.post.name); expect(removedPost.post_view.post.name).toBe(postRes.post_view.post.name);
@ -236,7 +237,7 @@ test('Remove a post from admin and community on different instance', async () =>
expect(betaPost.post.removed).toBe(false); expect(betaPost.post.removed).toBe(false);
// Undelete // Undelete
let undeletedPost = await removePost(alpha, false, postRes.post_view.post); let undeletedPost = await removePost(alpha, false, alphaPost.post);
expect(undeletedPost.post_view.post.removed).toBe(false); expect(undeletedPost.post_view.post.removed).toBe(false);
// Make sure lemmy beta sees post is undeleted // Make sure lemmy beta sees post is undeleted

View file

@ -133,7 +133,14 @@ impl ActivityHandler for BlockUser {
} }
SiteOrCommunity::Community(community) => { SiteOrCommunity::Community(community) => {
verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?;
verify_mod_action(&self.actor, &community, context, request_counter).await?; verify_mod_action(
&self.actor,
self.object.inner(),
&community,
context,
request_counter,
)
.await?;
} }
} }
Ok(()) Ok(())

View file

@ -74,7 +74,14 @@ impl ActivityHandler for AddMod {
verify_activity(&self.id, self.actor.inner(), &context.settings())?; verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?;
verify_mod_action(&self.actor, &community, context, request_counter).await?; verify_mod_action(
&self.actor,
self.object.inner(),
&community,
context,
request_counter,
)
.await?;
verify_add_remove_moderator_target(&self.target, &community)?; verify_add_remove_moderator_target(&self.target, &community)?;
Ok(()) Ok(())
} }

View file

@ -74,7 +74,14 @@ impl ActivityHandler for RemoveMod {
verify_activity(&self.id, self.actor.inner(), &context.settings())?; verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?;
verify_mod_action(&self.actor, &community, context, request_counter).await?; verify_mod_action(
&self.actor,
self.object.inner(),
&community,
context,
request_counter,
)
.await?;
verify_add_remove_moderator_target(&self.target, &community)?; verify_add_remove_moderator_target(&self.target, &community)?;
Ok(()) Ok(())
} }

View file

@ -65,7 +65,14 @@ impl ActivityHandler for UpdateCommunity {
verify_activity(&self.id, self.actor.inner(), &context.settings())?; verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?;
verify_mod_action(&self.actor, &community, context, request_counter).await?; verify_mod_action(
&self.actor,
self.object.id.inner(),
&community,
context,
request_counter,
)
.await?;
ApubCommunity::verify( ApubCommunity::verify(
&self.object, &self.object,
&community.actor_id.clone().into(), &community.actor_id.clone().into(),

View file

@ -103,7 +103,14 @@ impl ActivityHandler for CreateOrUpdatePost {
CreateOrUpdateType::Update => { CreateOrUpdateType::Update => {
let is_mod_action = self.object.is_mod_action(context).await?; let is_mod_action = self.object.is_mod_action(context).await?;
if is_mod_action { if is_mod_action {
verify_mod_action(&self.actor, &community, context, request_counter).await?; verify_mod_action(
&self.actor,
self.object.id.inner(),
&community,
context,
request_counter,
)
.await?;
} else { } else {
verify_domains_match(self.actor.inner(), self.object.id.inner())?; verify_domains_match(self.actor.inner(), self.object.id.inner())?;
verify_urls_match(self.actor.inner(), self.object.attributed_to.inner())?; verify_urls_match(self.actor.inner(), self.object.attributed_to.inner())?;

View file

@ -162,7 +162,14 @@ pub(in crate::activities) async fn verify_delete_activity(
verify_person_in_community(&activity.actor, &community, context, request_counter).await?; verify_person_in_community(&activity.actor, &community, context, request_counter).await?;
} }
// community deletion is always a mod (or admin) action // community deletion is always a mod (or admin) action
verify_mod_action(&activity.actor, &community, context, request_counter).await?; verify_mod_action(
&activity.actor,
activity.object.id(),
&community,
context,
request_counter,
)
.await?;
} }
DeletableObjects::Post(p) => { DeletableObjects::Post(p) => {
verify_is_public(&activity.to, &[])?; verify_is_public(&activity.to, &[])?;
@ -207,7 +214,7 @@ async fn verify_delete_post_or_comment(
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_person_in_community(actor, community, context, request_counter).await?; verify_person_in_community(actor, community, context, request_counter).await?;
if is_mod_action { if is_mod_action {
verify_mod_action(actor, community, context, request_counter).await?; verify_mod_action(actor, object_id, community, context, request_counter).await?;
} else { } else {
// domain of post ap_id and post.creator ap_id are identical, so we just check the former // domain of post ap_id and post.creator ap_id are identical, so we just check the former
verify_domains_match(actor.inner(), object_id)?; verify_domains_match(actor.inner(), object_id)?;

View file

@ -86,15 +86,20 @@ fn verify_activity(id: &Url, actor: &Url, settings: &Settings) -> Result<(), Lem
/// Verify that the actor is a community mod. This check is only run if the community is local, /// Verify that the actor is a community mod. This check is only run if the community is local,
/// because in case of remote communities, admins can also perform mod actions. As admin status /// because in case of remote communities, admins can also perform mod actions. As admin status
/// is not federated, we cant verify their actions remotely. /// is not federated, we cant verify their actions remotely.
///
/// * `mod_id` - Activitypub ID of the mod or admin who performed the action
/// * `object_id` - Activitypub ID of the actor or object that is being moderated
/// * `community` - The community inside which moderation is happening
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn verify_mod_action( pub(crate) async fn verify_mod_action(
actor_id: &ObjectId<ApubPerson>, mod_id: &ObjectId<ApubPerson>,
object_id: &Url,
community: &ApubCommunity, community: &ApubCommunity,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if community.local { if community.local {
let actor = actor_id let actor = mod_id
.dereference(context, context.client(), request_counter) .dereference(context, context.client(), request_counter)
.await?; .await?;
@ -102,13 +107,25 @@ pub(crate) async fn verify_mod_action(
// remote admins, it doesnt make any difference. // remote admins, it doesnt make any difference.
let community_id = community.id; let community_id = community.id;
let actor_id = actor.id; let actor_id = actor.id;
let is_mod_or_admin = blocking(context.pool(), move |conn| { let is_mod_or_admin = blocking(context.pool(), move |conn| {
CommunityView::is_mod_or_admin(conn, actor_id, community_id) CommunityView::is_mod_or_admin(conn, actor_id, community_id)
}) })
.await?; .await?;
if !is_mod_or_admin {
return Err(LemmyError::from_message("Not a mod")); // mod action was done either by a community mod or a local admin, so its allowed
if is_mod_or_admin {
return Ok(());
} }
// mod action comes from the same instance as the moderated object, so it was presumably done
// by an instance admin and is legitimate (admin status is not federated).
if mod_id.inner().domain() == object_id.domain() {
return Ok(());
}
// the user is not a valid mod
return Err(LemmyError::from_message("Not a mod"));
} }
Ok(()) Ok(())
} }