diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs index 877d9464f..e6425c1ab 100644 --- a/crates/api/src/community/ban.rs +++ b/crates/api/src/community/ban.rs @@ -4,7 +4,12 @@ use lemmy_api_common::{ community::{BanFromCommunity, BanFromCommunityResponse}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_mod_action, check_expire_time, remove_user_data_in_community}, + utils::{ + check_community_mod_action, + check_expire_time, + check_is_higher_mod, + remove_user_data_in_community, + }, }; use lemmy_db_schema::{ source::{ @@ -44,6 +49,14 @@ pub async fn ban_from_community( ) .await?; + check_is_higher_mod( + &mut context.pool(), + &local_user_view, + data.community_id, + &[data.person_id], + ) + .await?; + if let Some(reason) = &data.reason { is_valid_body_field(reason, false)?; } diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs index 49cd6893a..07dc9c822 100644 --- a/crates/api/src/local_user/ban_person.rs +++ b/crates/api/src/local_user/ban_person.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ context::LemmyContext, person::{BanPerson, BanPersonResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_expire_time, is_admin, remove_user_data}, + utils::{check_expire_time, check_is_higher_admin, is_admin, remove_user_data}, }; use lemmy_db_schema::{ source::{ @@ -31,6 +31,9 @@ pub async fn ban_from_site( // Make sure user is an admin is_admin(&local_user_view)?; + // Also make sure you're a higher admin than the target + check_is_higher_admin(&mut context.pool(), &local_user_view, &[data.person_id]).await?; + if let Some(reason) = &data.reason { is_valid_body_field(reason, false)?; } diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index e61730af6..3531dc6ec 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -178,6 +178,25 @@ pub async fn check_is_higher_admin( Ok(()) } +/// Checks to make sure the acting admin is higher than the target admin. +/// This needs to be done on admin removals, and all purge functions +pub async fn check_is_higher_mod_or_admin( + pool: &mut DbPool<'_>, + local_user_view: &LocalUserView, + community_id: CommunityId, + target_person_ids: &[PersonId], +) -> LemmyResult<()> { + let higher_admin_check = check_is_higher_admin(pool, local_user_view, target_person_ids).await; + let higher_mod_check = + check_is_higher_mod(pool, local_user_view, community_id, target_person_ids).await; + + if higher_mod_check.is_ok() || higher_admin_check.is_ok() { + Ok(()) + } else { + Err(LemmyErrorType::NotHigherMod)? + } +} + /// Marks a post as read for a given person. #[tracing::instrument(skip_all)] pub async fn mark_post_as_read( diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs index 926472b94..1382119dd 100644 --- a/crates/api_crud/src/comment/remove.rs +++ b/crates/api_crud/src/comment/remove.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ comment::{CommentResponse, RemoveComment}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::check_community_mod_action, + utils::{check_community_mod_action, check_is_higher_mod_or_admin}, }; use lemmy_db_schema::{ source::{ @@ -37,6 +37,14 @@ pub async fn remove_comment( ) .await?; + check_is_higher_mod_or_admin( + &mut context.pool(), + &local_user_view, + orig_comment.community.id, + &[orig_comment.creator.id], + ) + .await?; + // Don't allow removing or restoring comment which was deleted by user, as it would reveal // the comment text in mod log. if orig_comment.comment.deleted { diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index a4720e275..d704ef574 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -241,7 +241,7 @@ impl CommunityModerator { for_community_id: CommunityId, mod_person_id: PersonId, target_person_ids: &[PersonId], - ) -> Result { + ) -> Result<(), Error> { let conn = &mut get_conn(pool).await?; // Build the list of persons @@ -259,7 +259,7 @@ impl CommunityModerator { // If the first result sorted by published is the acting mod if res.person_id == mod_person_id { - Ok(true) + Ok(()) } else { Err(diesel::result::Error::NotFound) } @@ -554,8 +554,8 @@ mod tests { inserted_bobby.id, &moderator_person_ids, ) - .await?; - assert!(bobby_higher_check); + .await; + assert!(bobby_higher_check.is_ok()); // This should throw an error, since artemis was added later let artemis_higher_check = CommunityModerator::is_higher_mod_check( diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index a7702e6eb..4388c08c4 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -221,7 +221,7 @@ impl LocalUser { pool: &mut DbPool<'_>, admin_person_id: PersonId, target_person_ids: &[PersonId], - ) -> Result { + ) -> Result<(), Error> { let conn = &mut get_conn(pool).await?; // Build the list of persons @@ -239,7 +239,7 @@ impl LocalUser { // If the first result sorted by published is the acting mod if res.person_id == admin_person_id { - Ok(true) + Ok(()) } else { Err(diesel::result::Error::NotFound) } @@ -346,8 +346,8 @@ mod tests { // Make sure fiona is marked as a higher admin than delores, and vice versa let fiona_higher_check = - LocalUser::is_higher_admin_check(pool, inserted_fiona_person.id, &admin_person_ids).await?; - assert!(fiona_higher_check); + LocalUser::is_higher_admin_check(pool, inserted_fiona_person.id, &admin_person_ids).await; + assert!(fiona_higher_check.is_ok()); // This should throw an error, since delores was added later let delores_higher_check =