diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 3531dc6ec..10c9c657f 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -186,15 +186,16 @@ pub async fn check_is_higher_mod_or_admin( 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; + LocalUser::is_higher_mod_or_admin_check( + pool, + community_id, + local_user_view.person.id, + target_person_ids, + ) + .await + .with_lemmy_type(LemmyErrorType::NotHigherMod)?; - if higher_mod_check.is_ok() || higher_admin_check.is_ok() { - Ok(()) - } else { - Err(LemmyErrorType::NotHigherMod)? - } + Ok(()) } /// Marks a post as read for a given person. diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index 4388c08c4..14ef01dca 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -1,6 +1,6 @@ use crate::{ - newtypes::{DbUrl, LanguageId, LocalUserId, PersonId}, - schema::{local_user, person, registration_application}, + newtypes::{CommunityId, DbUrl, LanguageId, LocalUserId, PersonId}, + schema::{community_moderator, local_user, person, registration_application}, source::{ actor_language::LocalUserLanguage, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, @@ -18,6 +18,7 @@ use bcrypt::{hash, DEFAULT_COST}; use diesel::{ dsl::{insert_into, not, IntervalDsl}, result::Error, + CombineDsl, ExpressionMethods, JoinOnDsl, QueryDsl, @@ -244,6 +245,46 @@ impl LocalUser { Err(diesel::result::Error::NotFound) } } + + /// Checks to make sure the acting moderator is higher than the target moderator + pub async fn is_higher_mod_or_admin_check( + pool: &mut DbPool<'_>, + for_community_id: CommunityId, + admin_person_id: PersonId, + target_person_ids: &[PersonId], + ) -> Result<(), Error> { + let conn = &mut get_conn(pool).await?; + + // Build the list of persons + let mut persons = target_person_ids.to_owned(); + persons.push(admin_person_id); + persons.dedup(); + + let admins = local_user::table + .filter(local_user::admin.eq(true)) + .filter(local_user::person_id.eq_any(&persons)) + .order_by(local_user::id) + .select(local_user::person_id); + + let mods = community_moderator::table + .filter(community_moderator::community_id.eq(for_community_id)) + .filter(community_moderator::person_id.eq_any(&persons)) + .order_by(community_moderator::published) + .select(community_moderator::person_id); + + let res = admins.union_all(mods).get_results::(conn).await?; + let first_person = res + .as_slice() + .first() + .ok_or(diesel::result::Error::NotFound)?; + + // If the first result sorted by published is the acting mod + if *first_person == admin_person_id { + Ok(()) + } else { + Err(diesel::result::Error::NotFound) + } + } } /// Adds some helper functions for an optional LocalUser