From ba044c7d987acfbdedafac16cc409ba0db7709ef Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 16 Jul 2024 06:22:47 -0400 Subject: [PATCH] Adding checks for higher admin and mod. (#4860) * Adding checks for higher admin and mod. * Adding admin/mod checks for ban and comment removal. * Combining mod or admin check with an SQL union. * Making community ban or add mod also allow higher admins. * Making sure remove post also checks higher mods or admins. * Add unit test for is_higher_mod_or_admin_check * Fixing comment. * Addressing PR comments. * Get rid of pointless wrapper functions, return lemmyresult directly. --- crates/api/src/community/add_mod.rs | 13 + crates/api/src/community/ban.rs | 9 + crates/api/src/local_user/add_admin.rs | 10 + crates/api/src/local_user/ban_person.rs | 9 + crates/api/src/site/purge/comment.rs | 9 + crates/api/src/site/purge/community.rs | 18 ++ crates/api/src/site/purge/person.rs | 10 + crates/api/src/site/purge/post.rs | 9 + crates/api_common/src/claims.rs | 5 +- crates/api_crud/src/comment/remove.rs | 9 + crates/api_crud/src/post/remove.rs | 9 + crates/api_crud/src/user/create.rs | 22 +- crates/apub/src/api/user_settings_backup.rs | 5 +- crates/db_schema/src/impls/actor_language.rs | 10 +- crates/db_schema/src/impls/community.rs | 246 +++++++++++------- crates/db_schema/src/impls/local_user.rs | 139 +++++++++- .../src/impls/password_reset_request.rs | 5 +- crates/db_schema/src/impls/person.rs | 53 ++-- crates/db_schema/src/source/local_user.rs | 30 ++- crates/db_views/src/comment_report_view.rs | 5 +- crates/db_views/src/comment_view.rs | 7 +- crates/db_views/src/post_report_view.rs | 5 +- .../src/registration_application_view.rs | 16 +- crates/db_views_actor/src/community_view.rs | 5 +- crates/db_views_actor/src/person_view.rs | 10 +- crates/utils/src/error.rs | 2 + src/code_migrations.rs | 11 +- src/session_middleware.rs | 5 +- 28 files changed, 475 insertions(+), 211 deletions(-) diff --git a/crates/api/src/community/add_mod.rs b/crates/api/src/community/add_mod.rs index 5245f592e..8d8826cd2 100644 --- a/crates/api/src/community/add_mod.rs +++ b/crates/api/src/community/add_mod.rs @@ -9,6 +9,7 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ community::{Community, CommunityModerator, CommunityModeratorForm}, + local_user::LocalUser, moderator::{ModAddCommunity, ModAddCommunityForm}, }, traits::{Crud, Joinable}, @@ -33,6 +34,18 @@ pub async fn add_mod_to_community( &mut context.pool(), ) .await?; + + // If its a mod removal, also check that you're a higher mod. + if !data.added { + LocalUser::is_higher_mod_or_admin_check( + &mut context.pool(), + community_id, + local_user_view.person.id, + vec![data.person_id], + ) + .await?; + } + let community = Community::read(&mut context.pool(), community_id) .await? .ok_or(LemmyErrorType::CouldntFindCommunity)?; diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs index 877d9464f..8e527d2ac 100644 --- a/crates/api/src/community/ban.rs +++ b/crates/api/src/community/ban.rs @@ -14,6 +14,7 @@ use lemmy_db_schema::{ CommunityPersonBan, CommunityPersonBanForm, }, + local_user::LocalUser, moderator::{ModBanFromCommunity, ModBanFromCommunityForm}, }, traits::{Bannable, Crud, Followable}, @@ -44,6 +45,14 @@ pub async fn ban_from_community( ) .await?; + LocalUser::is_higher_mod_or_admin_check( + &mut context.pool(), + data.community_id, + local_user_view.person.id, + vec![data.person_id], + ) + .await?; + if let Some(reason) = &data.reason { is_valid_body_field(reason, false)?; } diff --git a/crates/api/src/local_user/add_admin.rs b/crates/api/src/local_user/add_admin.rs index 7db2e5653..44b36fe66 100644 --- a/crates/api/src/local_user/add_admin.rs +++ b/crates/api/src/local_user/add_admin.rs @@ -24,6 +24,16 @@ pub async fn add_admin( // Make sure user is an admin is_admin(&local_user_view)?; + // If its an admin removal, also check that you're a higher admin + if !data.added { + LocalUser::is_higher_admin_check( + &mut context.pool(), + local_user_view.person.id, + vec![data.person_id], + ) + .await?; + } + // Make sure that the person_id added is local let added_local_user = LocalUserView::read_person(&mut context.pool(), data.person_id) .await? diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs index 49cd6893a..58392cefd 100644 --- a/crates/api/src/local_user/ban_person.rs +++ b/crates/api/src/local_user/ban_person.rs @@ -9,6 +9,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ + local_user::LocalUser, login_token::LoginToken, moderator::{ModBan, ModBanForm}, person::{Person, PersonUpdateForm}, @@ -31,6 +32,14 @@ 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 + LocalUser::is_higher_admin_check( + &mut context.pool(), + local_user_view.person.id, + vec![data.person_id], + ) + .await?; + if let Some(reason) = &data.reason { is_valid_body_field(reason, false)?; } diff --git a/crates/api/src/site/purge/comment.rs b/crates/api/src/site/purge/comment.rs index f99dfc689..9f90aff99 100644 --- a/crates/api/src/site/purge/comment.rs +++ b/crates/api/src/site/purge/comment.rs @@ -10,6 +10,7 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ comment::Comment, + local_user::LocalUser, moderator::{AdminPurgeComment, AdminPurgeCommentForm}, }, traits::Crud, @@ -37,6 +38,14 @@ pub async fn purge_comment( .await? .ok_or(LemmyErrorType::CouldntFindComment)?; + // Also check that you're a higher admin + LocalUser::is_higher_admin_check( + &mut context.pool(), + local_user_view.person.id, + vec![comment_view.creator.id], + ) + .await?; + let post_id = comment_view.comment.post_id; // TODO read comments for pictrs images and purge them diff --git a/crates/api/src/site/purge/community.rs b/crates/api/src/site/purge/community.rs index 14b250681..59eded6ad 100644 --- a/crates/api/src/site/purge/community.rs +++ b/crates/api/src/site/purge/community.rs @@ -9,13 +9,16 @@ use lemmy_api_common::{ SuccessResponse, }; use lemmy_db_schema::{ + newtypes::PersonId, source::{ community::Community, + local_user::LocalUser, moderator::{AdminPurgeCommunity, AdminPurgeCommunityForm}, }, traits::Crud, }; use lemmy_db_views::structs::LocalUserView; +use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_utils::{error::LemmyResult, LemmyErrorType}; #[tracing::instrument(skip(context))] @@ -32,6 +35,21 @@ pub async fn purge_community( .await? .ok_or(LemmyErrorType::CouldntFindCommunity)?; + // Also check that you're a higher admin than all the mods + let community_mod_person_ids = + CommunityModeratorView::for_community(&mut context.pool(), community.id) + .await? + .iter() + .map(|cmv| cmv.moderator.id) + .collect::>(); + + LocalUser::is_higher_admin_check( + &mut context.pool(), + local_user_view.person.id, + community_mod_person_ids, + ) + .await?; + if let Some(banner) = &community.banner { purge_image_from_pictrs(banner, &context).await.ok(); } diff --git a/crates/api/src/site/purge/person.rs b/crates/api/src/site/purge/person.rs index 1b38752c7..dc824b163 100644 --- a/crates/api/src/site/purge/person.rs +++ b/crates/api/src/site/purge/person.rs @@ -10,6 +10,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ + local_user::LocalUser, moderator::{AdminPurgePerson, AdminPurgePersonForm}, person::{Person, PersonUpdateForm}, }, @@ -27,9 +28,18 @@ pub async fn purge_person( // Only let admin purge an item is_admin(&local_user_view)?; + // Also check that you're a higher admin + LocalUser::is_higher_admin_check( + &mut context.pool(), + local_user_view.person.id, + vec![data.person_id], + ) + .await?; + let person = Person::read(&mut context.pool(), data.person_id) .await? .ok_or(LemmyErrorType::CouldntFindPerson)?; + ban_nonlocal_user_from_local_communities( &local_user_view, &person, diff --git a/crates/api/src/site/purge/post.rs b/crates/api/src/site/purge/post.rs index 75cd021d1..6e512312f 100644 --- a/crates/api/src/site/purge/post.rs +++ b/crates/api/src/site/purge/post.rs @@ -10,6 +10,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ + local_user::LocalUser, moderator::{AdminPurgePost, AdminPurgePostForm}, post::Post, }, @@ -32,6 +33,14 @@ pub async fn purge_post( .await? .ok_or(LemmyErrorType::CouldntFindPost)?; + // Also check that you're a higher admin + LocalUser::is_higher_admin_check( + &mut context.pool(), + local_user_view.person.id, + vec![post.creator_id], + ) + .await?; + // Purge image if let Some(url) = &post.url { purge_image_from_pictrs(url, &context).await.ok(); diff --git a/crates/api_common/src/claims.rs b/crates/api_common/src/claims.rs index 6c17d4e6a..905394785 100644 --- a/crates/api_common/src/claims.rs +++ b/crates/api_common/src/claims.rs @@ -116,10 +116,7 @@ mod tests { let inserted_person = Person::create(pool, &new_person).await.unwrap(); - let local_user_form = LocalUserInsertForm::builder() - .person_id(inserted_person.id) - .password_encrypted("123456".to_string()) - .build(); + let local_user_form = LocalUserInsertForm::test_form(inserted_person.id); let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![]) .await diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs index 83ef82e3a..f810c8275 100644 --- a/crates/api_crud/src/comment/remove.rs +++ b/crates/api_crud/src/comment/remove.rs @@ -11,6 +11,7 @@ use lemmy_db_schema::{ source::{ comment::{Comment, CommentUpdateForm}, comment_report::CommentReport, + local_user::LocalUser, moderator::{ModRemoveComment, ModRemoveCommentForm}, }, traits::{Crud, Reportable}, @@ -41,6 +42,14 @@ pub async fn remove_comment( ) .await?; + LocalUser::is_higher_mod_or_admin_check( + &mut context.pool(), + orig_comment.community.id, + local_user_view.person.id, + vec![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/api_crud/src/post/remove.rs b/crates/api_crud/src/post/remove.rs index 4ca15c90f..b4fdba6fb 100644 --- a/crates/api_crud/src/post/remove.rs +++ b/crates/api_crud/src/post/remove.rs @@ -9,6 +9,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ + local_user::LocalUser, moderator::{ModRemovePost, ModRemovePostForm}, post::{Post, PostUpdateForm}, post_report::PostReport, @@ -37,6 +38,14 @@ pub async fn remove_post( ) .await?; + LocalUser::is_higher_mod_or_admin_check( + &mut context.pool(), + orig_post.community_id, + local_user_view.person.id, + vec![orig_post.creator_id], + ) + .await?; + // Update the post let post_id = data.post_id; let removed = data.removed; diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index c84bd0a50..64bef8760 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -150,18 +150,18 @@ pub async fn register( .unwrap_or(site_view.site.content_warning.is_some()); // Create the local user - let local_user_form = LocalUserInsertForm::builder() - .person_id(inserted_person.id) - .email(data.email.as_deref().map(str::to_lowercase)) - .password_encrypted(data.password.to_string()) - .show_nsfw(Some(show_nsfw)) - .accepted_application(accepted_application) - .default_listing_type(Some(local_site.default_post_listing_type)) - .post_listing_mode(Some(local_site.default_post_listing_mode)) - .interface_language(language_tags.first().cloned()) + let local_user_form = LocalUserInsertForm { + email: data.email.as_deref().map(str::to_lowercase), + password_encrypted: data.password.to_string(), + show_nsfw: Some(show_nsfw), + accepted_application, + default_listing_type: Some(local_site.default_post_listing_type), + post_listing_mode: Some(local_site.default_post_listing_mode), + interface_language: language_tags.first().cloned(), // If its the initial site setup, they are an admin - .admin(Some(!local_site.site_setup)) - .build(); + admin: Some(!local_site.site_setup), + ..LocalUserInsertForm::new(inserted_person.id, data.password.to_string()) + }; let all_languages = Language::read_all(&mut context.pool()).await?; // use hashset to avoid duplicates diff --git a/crates/apub/src/api/user_settings_backup.rs b/crates/apub/src/api/user_settings_backup.rs index 9f2cb58c5..a0879b3c9 100644 --- a/crates/apub/src/api/user_settings_backup.rs +++ b/crates/apub/src/api/user_settings_backup.rs @@ -345,10 +345,7 @@ mod tests { }; let person = Person::create(&mut context.pool(), &person_form).await?; - let user_form = LocalUserInsertForm::builder() - .person_id(person.id) - .password_encrypted("pass".to_string()) - .build(); + let user_form = LocalUserInsertForm::test_form(person.id); let local_user = LocalUser::create(&mut context.pool(), &user_form, vec![]).await?; Ok( diff --git a/crates/db_schema/src/impls/actor_language.rs b/crates/db_schema/src/impls/actor_language.rs index 8483d6c20..5a8658baf 100644 --- a/crates/db_schema/src/impls/actor_language.rs +++ b/crates/db_schema/src/impls/actor_language.rs @@ -533,10 +533,7 @@ mod tests { let person_form = PersonInsertForm::test_form(instance.id, "my test person"); let person = Person::create(pool, &person_form).await.unwrap(); - let local_user_form = LocalUserInsertForm::builder() - .person_id(person.id) - .password_encrypted("my_pw".to_string()) - .build(); + let local_user_form = LocalUserInsertForm::test_form(person.id); let local_user = LocalUser::create(pool, &local_user_form, vec![]) .await @@ -645,10 +642,7 @@ mod tests { let person_form = PersonInsertForm::test_form(instance.id, "my test person"); let person = Person::create(pool, &person_form).await.unwrap(); - let local_user_form = LocalUserInsertForm::builder() - .person_id(person.id) - .password_encrypted("my_pw".to_string()) - .build(); + let local_user_form = LocalUserInsertForm::test_form(person.id); let local_user = LocalUser::create(pool, &local_user_form, vec![]) .await .unwrap(); diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index 6cd90cc66..eaf35a90d 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -1,7 +1,14 @@ use crate::{ diesel::{DecoratableTarget, OptionalExtension}, newtypes::{CommunityId, DbUrl, PersonId}, - schema::{community, community_follower, instance}, + schema::{ + community, + community_follower, + community_moderator, + community_person_ban, + instance, + post, + }, source::{ actor_language::CommunityLanguage, community::{ @@ -42,6 +49,7 @@ use diesel::{ Queryable, }; use diesel_async::RunQueryDsl; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; #[async_trait] impl Crud for Community { @@ -83,9 +91,8 @@ impl Joinable for CommunityModerator { pool: &mut DbPool<'_>, community_moderator_form: &CommunityModeratorForm, ) -> Result { - use crate::schema::community_moderator::dsl::community_moderator; let conn = &mut get_conn(pool).await?; - insert_into(community_moderator) + insert_into(community_moderator::table) .values(community_moderator_form) .get_result::(conn) .await @@ -95,9 +102,8 @@ impl Joinable for CommunityModerator { pool: &mut DbPool<'_>, community_moderator_form: &CommunityModeratorForm, ) -> Result { - use crate::schema::community_moderator::dsl::community_moderator; let conn = &mut get_conn(pool).await?; - diesel::delete(community_moderator.find(( + diesel::delete(community_moderator::table.find(( community_moderator_form.person_id, community_moderator_form.community_id, ))) @@ -147,25 +153,23 @@ impl Community { pool: &mut DbPool<'_>, url: &DbUrl, ) -> Result, Error> { - use crate::schema::community::dsl::{featured_url, moderators_url}; - use CollectionType::*; let conn = &mut get_conn(pool).await?; let res = community::table - .filter(moderators_url.eq(url)) + .filter(community::moderators_url.eq(url)) .first(conn) .await .optional()?; if let Some(c) = res { - Ok(Some((c, Moderators))) + Ok(Some((c, CollectionType::Moderators))) } else { let res = community::table - .filter(featured_url.eq(url)) + .filter(community::featured_url.eq(url)) .first(conn) .await .optional()?; if let Some(c) = res { - Ok(Some((c, Featured))) + Ok(Some((c, CollectionType::Featured))) } else { Ok(None) } @@ -177,7 +181,6 @@ impl Community { posts: Vec, pool: &mut DbPool<'_>, ) -> Result<(), Error> { - use crate::schema::post; let conn = &mut get_conn(pool).await?; for p in &posts { debug_assert!(p.community_id == community_id); @@ -185,10 +188,10 @@ impl Community { // Mark the given posts as featured and all other posts as not featured. let post_ids = posts.iter().map(|p| p.id); update(post::table) - .filter(post::dsl::community_id.eq(community_id)) + .filter(post::community_id.eq(community_id)) // This filter is just for performance - .filter(post::dsl::featured_community.or(post::dsl::id.eq_any(post_ids.clone()))) - .set(post::dsl::featured_community.eq(post::dsl::id.eq_any(post_ids))) + .filter(post::featured_community.or(post::id.eq_any(post_ids.clone()))) + .set(post::featured_community.eq(post::id.eq_any(post_ids))) .execute(conn) .await?; Ok(()) @@ -200,37 +203,68 @@ impl CommunityModerator { pool: &mut DbPool<'_>, for_community_id: CommunityId, ) -> Result { - use crate::schema::community_moderator::dsl::{community_id, community_moderator}; let conn = &mut get_conn(pool).await?; - diesel::delete(community_moderator.filter(community_id.eq(for_community_id))) - .execute(conn) - .await + diesel::delete( + community_moderator::table.filter(community_moderator::community_id.eq(for_community_id)), + ) + .execute(conn) + .await } pub async fn leave_all_communities( pool: &mut DbPool<'_>, for_person_id: PersonId, ) -> Result { - use crate::schema::community_moderator::dsl::{community_moderator, person_id}; let conn = &mut get_conn(pool).await?; - diesel::delete(community_moderator.filter(person_id.eq(for_person_id))) - .execute(conn) - .await + diesel::delete( + community_moderator::table.filter(community_moderator::person_id.eq(for_person_id)), + ) + .execute(conn) + .await } pub async fn get_person_moderated_communities( pool: &mut DbPool<'_>, for_person_id: PersonId, ) -> Result, Error> { - use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id}; let conn = &mut get_conn(pool).await?; - community_moderator - .filter(person_id.eq(for_person_id)) - .select(community_id) + community_moderator::table + .filter(community_moderator::person_id.eq(for_person_id)) + .select(community_moderator::community_id) .load::(conn) .await } + + /// Checks to make sure the acting moderator was added earlier than the target moderator + pub async fn is_higher_mod_check( + pool: &mut DbPool<'_>, + for_community_id: CommunityId, + mod_person_id: PersonId, + target_person_ids: Vec, + ) -> LemmyResult<()> { + let conn = &mut get_conn(pool).await?; + + // Build the list of persons + let mut persons = target_person_ids; + persons.push(mod_person_id); + persons.dedup(); + + let res = 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) + // This does a limit 1 select first + .first::(conn) + .await?; + + // If the first result sorted by published is the acting mod + if res.person_id == mod_person_id { + Ok(()) + } else { + Err(LemmyErrorType::NotHigherMod)? + } + } } #[async_trait] @@ -240,11 +274,13 @@ impl Bannable for CommunityPersonBan { pool: &mut DbPool<'_>, community_person_ban_form: &CommunityPersonBanForm, ) -> Result { - use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id}; let conn = &mut get_conn(pool).await?; - insert_into(community_person_ban) + insert_into(community_person_ban::table) .values(community_person_ban_form) - .on_conflict((community_id, person_id)) + .on_conflict(( + community_person_ban::community_id, + community_person_ban::person_id, + )) .do_update() .set(community_person_ban_form) .get_result::(conn) @@ -255,9 +291,8 @@ impl Bannable for CommunityPersonBan { pool: &mut DbPool<'_>, community_person_ban_form: &CommunityPersonBanForm, ) -> Result { - use crate::schema::community_person_ban::dsl::community_person_ban; let conn = &mut get_conn(pool).await?; - diesel::delete(community_person_ban.find(( + diesel::delete(community_person_ban::table.find(( community_person_ban_form.person_id, community_person_ban_form.community_id, ))) @@ -291,11 +326,10 @@ impl CommunityFollower { pool: &mut DbPool<'_>, remote_community_id: CommunityId, ) -> Result { - use crate::schema::community_follower::dsl::{community_follower, community_id}; let conn = &mut get_conn(pool).await?; - select(exists( - community_follower.filter(community_id.eq(remote_community_id)), - )) + select(exists(community_follower::table.filter( + community_follower::community_id.eq(remote_community_id), + ))) .get_result(conn) .await } @@ -316,11 +350,13 @@ impl Queryable, Pg> for SubscribedType { impl Followable for CommunityFollower { type Form = CommunityFollowerForm; async fn follow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result { - use crate::schema::community_follower::dsl::{community_follower, community_id, person_id}; let conn = &mut get_conn(pool).await?; - insert_into(community_follower) + insert_into(community_follower::table) .values(form) - .on_conflict((community_id, person_id)) + .on_conflict(( + community_follower::community_id, + community_follower::person_id, + )) .do_update() .set(form) .get_result::(conn) @@ -331,17 +367,16 @@ impl Followable for CommunityFollower { community_id: CommunityId, person_id: PersonId, ) -> Result { - use crate::schema::community_follower::dsl::{community_follower, pending}; let conn = &mut get_conn(pool).await?; - diesel::update(community_follower.find((person_id, community_id))) - .set(pending.eq(false)) + diesel::update(community_follower::table.find((person_id, community_id))) + .set(community_follower::pending.eq(false)) .get_result::(conn) .await } + async fn unfollow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result { - use crate::schema::community_follower::dsl::community_follower; let conn = &mut get_conn(pool).await?; - diesel::delete(community_follower.find((form.person_id, form.community_id))) + diesel::delete(community_follower::table.find((form.person_id, form.community_id))) .execute(conn) .await } @@ -397,10 +432,8 @@ impl ApubActor for Community { } #[cfg(test)] -#[allow(clippy::unwrap_used)] #[allow(clippy::indexing_slicing)] mod tests { - use crate::{ source::{ community::{ @@ -415,28 +448,30 @@ mod tests { CommunityUpdateForm, }, instance::Instance, + local_user::LocalUser, person::{Person, PersonInsertForm}, }, traits::{Bannable, Crud, Followable, Joinable}, utils::build_db_pool_for_tests, CommunityVisibility, }; + use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use pretty_assertions::assert_eq; use serial_test::serial; #[tokio::test] #[serial] - async fn test_crud() { + async fn test_crud() -> LemmyResult<()> { let pool = &build_db_pool_for_tests().await; let pool = &mut pool.into(); - let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) - .await - .unwrap(); + let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; - let new_person = PersonInsertForm::test_form(inserted_instance.id, "bobbee"); + let bobby_person = PersonInsertForm::test_form(inserted_instance.id, "bobby"); + let inserted_bobby = Person::create(pool, &bobby_person).await?; - let inserted_person = Person::create(pool, &new_person).await.unwrap(); + let artemis_person = PersonInsertForm::test_form(inserted_instance.id, "artemis"); + let inserted_artemis = Person::create(pool, &artemis_person).await?; let new_community = CommunityInsertForm::builder() .name("TIL".into()) @@ -445,7 +480,7 @@ mod tests { .instance_id(inserted_instance.id) .build(); - let inserted_community = Community::create(pool, &new_community).await.unwrap(); + let inserted_community = Community::create(pool, &new_community).await?; let expected_community = Community { id: inserted_community.id, @@ -477,91 +512,120 @@ mod tests { let community_follower_form = CommunityFollowerForm { community_id: inserted_community.id, - person_id: inserted_person.id, + person_id: inserted_bobby.id, pending: false, }; - let inserted_community_follower = CommunityFollower::follow(pool, &community_follower_form) - .await - .unwrap(); + let inserted_community_follower = + CommunityFollower::follow(pool, &community_follower_form).await?; let expected_community_follower = CommunityFollower { community_id: inserted_community.id, - person_id: inserted_person.id, + person_id: inserted_bobby.id, pending: false, published: inserted_community_follower.published, }; - let community_moderator_form = CommunityModeratorForm { + let bobby_moderator_form = CommunityModeratorForm { community_id: inserted_community.id, - person_id: inserted_person.id, + person_id: inserted_bobby.id, }; - let inserted_community_moderator = CommunityModerator::join(pool, &community_moderator_form) - .await - .unwrap(); + let inserted_bobby_moderator = CommunityModerator::join(pool, &bobby_moderator_form).await?; + + let artemis_moderator_form = CommunityModeratorForm { + community_id: inserted_community.id, + person_id: inserted_artemis.id, + }; + + let _inserted_artemis_moderator = + CommunityModerator::join(pool, &artemis_moderator_form).await?; let expected_community_moderator = CommunityModerator { community_id: inserted_community.id, - person_id: inserted_person.id, - published: inserted_community_moderator.published, + person_id: inserted_bobby.id, + published: inserted_bobby_moderator.published, }; + let moderator_person_ids = vec![inserted_bobby.id, inserted_artemis.id]; + + // Make sure bobby is marked as a higher mod than artemis, and vice versa + let bobby_higher_check = CommunityModerator::is_higher_mod_check( + pool, + inserted_community.id, + inserted_bobby.id, + moderator_person_ids.clone(), + ) + .await; + assert!(bobby_higher_check.is_ok()); + + // Also check the other is_higher_mod_or_admin function just in case + let bobby_higher_check_2 = LocalUser::is_higher_mod_or_admin_check( + pool, + inserted_community.id, + inserted_bobby.id, + moderator_person_ids.clone(), + ) + .await; + assert!(bobby_higher_check_2.is_ok()); + + // This should throw an error, since artemis was added later + let artemis_higher_check = CommunityModerator::is_higher_mod_check( + pool, + inserted_community.id, + inserted_artemis.id, + moderator_person_ids, + ) + .await; + assert!(artemis_higher_check.is_err()); + let community_person_ban_form = CommunityPersonBanForm { community_id: inserted_community.id, - person_id: inserted_person.id, + person_id: inserted_bobby.id, expires: None, }; - let inserted_community_person_ban = CommunityPersonBan::ban(pool, &community_person_ban_form) - .await - .unwrap(); + let inserted_community_person_ban = + CommunityPersonBan::ban(pool, &community_person_ban_form).await?; let expected_community_person_ban = CommunityPersonBan { community_id: inserted_community.id, - person_id: inserted_person.id, + person_id: inserted_bobby.id, published: inserted_community_person_ban.published, expires: None, }; let read_community = Community::read(pool, inserted_community.id) - .await - .unwrap() - .unwrap(); + .await? + .ok_or(LemmyErrorType::CouldntFindCommunity)?; let update_community_form = CommunityUpdateForm { title: Some("nada".to_owned()), ..Default::default() }; - let updated_community = Community::update(pool, inserted_community.id, &update_community_form) - .await - .unwrap(); + let updated_community = + Community::update(pool, inserted_community.id, &update_community_form).await?; - let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form) - .await - .unwrap(); - let left_community = CommunityModerator::leave(pool, &community_moderator_form) - .await - .unwrap(); - let unban = CommunityPersonBan::unban(pool, &community_person_ban_form) - .await - .unwrap(); - let num_deleted = Community::delete(pool, inserted_community.id) - .await - .unwrap(); - Person::delete(pool, inserted_person.id).await.unwrap(); - Instance::delete(pool, inserted_instance.id).await.unwrap(); + let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form).await?; + let left_community = CommunityModerator::leave(pool, &bobby_moderator_form).await?; + let unban = CommunityPersonBan::unban(pool, &community_person_ban_form).await?; + let num_deleted = Community::delete(pool, inserted_community.id).await?; + Person::delete(pool, inserted_bobby.id).await?; + Person::delete(pool, inserted_artemis.id).await?; + Instance::delete(pool, inserted_instance.id).await?; assert_eq!(expected_community, read_community); assert_eq!(expected_community, inserted_community); assert_eq!(expected_community, updated_community); assert_eq!(expected_community_follower, inserted_community_follower); - assert_eq!(expected_community_moderator, inserted_community_moderator); + assert_eq!(expected_community_moderator, inserted_bobby_moderator); assert_eq!(expected_community_person_ban, inserted_community_person_ban); assert_eq!(1, ignored_community); assert_eq!(1, left_community); assert_eq!(1, unban); // assert_eq!(2, loaded_count); assert_eq!(1, num_deleted); + + Ok(()) } } diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index 694979ade..32e8d50b7 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::{community, local_user, person, registration_application}, + newtypes::{CommunityId, DbUrl, LanguageId, LocalUserId, PersonId}, + schema::{community, community_moderator, local_user, person, registration_application}, source::{ actor_language::LocalUserLanguage, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, @@ -19,11 +19,13 @@ use bcrypt::{hash, DEFAULT_COST}; use diesel::{ dsl::{insert_into, not, IntervalDsl}, result::Error, + CombineDsl, ExpressionMethods, JoinOnDsl, QueryDsl, }; use diesel_async::RunQueryDsl; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; impl LocalUser { pub async fn create( @@ -216,6 +218,72 @@ impl LocalUser { blocked_instances, }) } + + /// Checks to make sure the acting admin is higher than the target admin + pub async fn is_higher_admin_check( + pool: &mut DbPool<'_>, + admin_person_id: PersonId, + target_person_ids: Vec, + ) -> LemmyResult<()> { + let conn = &mut get_conn(pool).await?; + + // Build the list of persons + let mut persons = target_person_ids; + persons.push(admin_person_id); + persons.dedup(); + + let res = local_user::table + .filter(local_user::admin.eq(true)) + .filter(local_user::person_id.eq_any(persons)) + .order_by(local_user::id) + // This does a limit 1 select first + .first::(conn) + .await?; + + // If the first result sorted by published is the acting admin + if res.person_id == admin_person_id { + Ok(()) + } else { + Err(LemmyErrorType::NotHigherAdmin)? + } + } + + /// 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: Vec, + ) -> LemmyResult<()> { + let conn = &mut get_conn(pool).await?; + + // Build the list of persons + let mut persons = target_person_ids; + 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(LemmyErrorType::NotHigherMod)?; + + // If the first result sorted by published is the acting mod + if *first_person == admin_person_id { + Ok(()) + } else { + Err(LemmyErrorType::NotHigherMod)? + } + } } /// Adds some helper functions for an optional LocalUser @@ -278,10 +346,14 @@ impl LocalUserOptionHelper for Option<&LocalUser> { impl LocalUserInsertForm { pub fn test_form(person_id: PersonId) -> Self { - Self::builder() - .person_id(person_id) - .password_encrypted(String::new()) - .build() + Self::new(person_id, String::new()) + } + + pub fn test_form_admin(person_id: PersonId) -> Self { + LocalUserInsertForm { + admin: Some(true), + ..Self::test_form(person_id) + } } } @@ -293,3 +365,58 @@ pub struct UserBackupLists { pub blocked_users: Vec, pub blocked_instances: Vec, } + +#[cfg(test)] +#[allow(clippy::indexing_slicing)] +mod tests { + use crate::{ + source::{ + instance::Instance, + local_user::{LocalUser, LocalUserInsertForm}, + person::{Person, PersonInsertForm}, + }, + traits::Crud, + utils::build_db_pool_for_tests, + }; + use lemmy_utils::error::LemmyResult; + use serial_test::serial; + + #[tokio::test] + #[serial] + async fn test_admin_higher_check() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + + let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; + + let fiona_person = PersonInsertForm::test_form(inserted_instance.id, "fiona"); + let inserted_fiona_person = Person::create(pool, &fiona_person).await?; + + let fiona_local_user_form = LocalUserInsertForm::test_form_admin(inserted_fiona_person.id); + let _inserted_fiona_local_user = + LocalUser::create(pool, &fiona_local_user_form, vec![]).await?; + + let delores_person = PersonInsertForm::test_form(inserted_instance.id, "delores"); + let inserted_delores_person = Person::create(pool, &delores_person).await?; + let delores_local_user_form = LocalUserInsertForm::test_form_admin(inserted_delores_person.id); + let _inserted_delores_local_user = + LocalUser::create(pool, &delores_local_user_form, vec![]).await?; + + let admin_person_ids = vec![inserted_fiona_person.id, inserted_delores_person.id]; + + // 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.clone()) + .await; + assert!(fiona_higher_check.is_ok()); + + // This should throw an error, since delores was added later + let delores_higher_check = + LocalUser::is_higher_admin_check(pool, inserted_delores_person.id, admin_person_ids).await; + assert!(delores_higher_check.is_err()); + + Instance::delete(pool, inserted_instance.id).await?; + + Ok(()) + } +} diff --git a/crates/db_schema/src/impls/password_reset_request.rs b/crates/db_schema/src/impls/password_reset_request.rs index 0b1351af1..be05ed8ac 100644 --- a/crates/db_schema/src/impls/password_reset_request.rs +++ b/crates/db_schema/src/impls/password_reset_request.rs @@ -72,10 +72,7 @@ mod tests { let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy prw"); let inserted_person = Person::create(pool, &new_person).await?; - let new_local_user = LocalUserInsertForm::builder() - .person_id(inserted_person.id) - .password_encrypted("pass".to_string()) - .build(); + let new_local_user = LocalUserInsertForm::test_form(inserted_person.id); let inserted_local_user = LocalUser::create(pool, &new_local_user, vec![]).await?; // Create password reset token diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index f318a503a..89c108f8c 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -182,11 +182,10 @@ impl ApubActor for Person { impl Followable for PersonFollower { type Form = PersonFollowerForm; async fn follow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result { - use crate::schema::person_follower::dsl::{follower_id, person_follower, person_id}; let conn = &mut get_conn(pool).await?; - insert_into(person_follower) + insert_into(person_follower::table) .values(form) - .on_conflict((follower_id, person_id)) + .on_conflict((person_follower::follower_id, person_follower::person_id)) .do_update() .set(form) .get_result::(conn) @@ -196,9 +195,8 @@ impl Followable for PersonFollower { unimplemented!() } async fn unfollow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result { - use crate::schema::person_follower::dsl::person_follower; let conn = &mut get_conn(pool).await?; - diesel::delete(person_follower.find((form.follower_id, form.person_id))) + diesel::delete(person_follower::table.find((form.follower_id, form.person_id))) .execute(conn) .await } @@ -220,7 +218,6 @@ impl PersonFollower { } #[cfg(test)] -#[allow(clippy::unwrap_used)] #[allow(clippy::indexing_slicing)] mod tests { @@ -232,22 +229,21 @@ mod tests { traits::{Crud, Followable}, utils::build_db_pool_for_tests, }; + use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use pretty_assertions::assert_eq; use serial_test::serial; #[tokio::test] #[serial] - async fn test_crud() { + async fn test_crud() -> LemmyResult<()> { let pool = &build_db_pool_for_tests().await; let pool = &mut pool.into(); - let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) - .await - .unwrap(); + let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; let new_person = PersonInsertForm::test_form(inserted_instance.id, "holly"); - let inserted_person = Person::create(pool, &new_person).await.unwrap(); + let inserted_person = Person::create(pool, &new_person).await?; let expected_person = Person { id: inserted_person.id, @@ -274,57 +270,54 @@ mod tests { }; let read_person = Person::read(pool, inserted_person.id) - .await - .unwrap() - .unwrap(); + .await? + .ok_or(LemmyErrorType::CouldntFindPerson)?; let update_person_form = PersonUpdateForm { actor_id: Some(inserted_person.actor_id.clone()), ..Default::default() }; - let updated_person = Person::update(pool, inserted_person.id, &update_person_form) - .await - .unwrap(); + let updated_person = Person::update(pool, inserted_person.id, &update_person_form).await?; - let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap(); - Instance::delete(pool, inserted_instance.id).await.unwrap(); + let num_deleted = Person::delete(pool, inserted_person.id).await?; + Instance::delete(pool, inserted_instance.id).await?; assert_eq!(expected_person, read_person); assert_eq!(expected_person, inserted_person); assert_eq!(expected_person, updated_person); assert_eq!(1, num_deleted); + + Ok(()) } #[tokio::test] #[serial] - async fn follow() { + async fn follow() -> LemmyResult<()> { let pool = &build_db_pool_for_tests().await; let pool = &mut pool.into(); - let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) - .await - .unwrap(); + let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; let person_form_1 = PersonInsertForm::test_form(inserted_instance.id, "erich"); - let person_1 = Person::create(pool, &person_form_1).await.unwrap(); + let person_1 = Person::create(pool, &person_form_1).await?; let person_form_2 = PersonInsertForm::test_form(inserted_instance.id, "michele"); - let person_2 = Person::create(pool, &person_form_2).await.unwrap(); + let person_2 = Person::create(pool, &person_form_2).await?; let follow_form = PersonFollowerForm { person_id: person_1.id, follower_id: person_2.id, pending: false, }; - let person_follower = PersonFollower::follow(pool, &follow_form).await.unwrap(); + let person_follower = PersonFollower::follow(pool, &follow_form).await?; assert_eq!(person_1.id, person_follower.person_id); assert_eq!(person_2.id, person_follower.follower_id); assert!(!person_follower.pending); - let followers = PersonFollower::list_followers(pool, person_1.id) - .await - .unwrap(); + let followers = PersonFollower::list_followers(pool, person_1.id).await?; assert_eq!(vec![person_2], followers); - let unfollow = PersonFollower::unfollow(pool, &follow_form).await.unwrap(); + let unfollow = PersonFollower::unfollow(pool, &follow_form).await?; assert_eq!(1, unfollow); + + Ok(()) } } diff --git a/crates/db_schema/src/source/local_user.rs b/crates/db_schema/src/source/local_user.rs index 5d592ddf1..c7a5b5224 100644 --- a/crates/db_schema/src/source/local_user.rs +++ b/crates/db_schema/src/source/local_user.rs @@ -11,7 +11,6 @@ use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; #[cfg(feature = "full")] use ts_rs::TS; -use typed_builder::TypedBuilder; #[skip_serializing_none] #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] @@ -69,38 +68,59 @@ pub struct LocalUser { pub collapse_bot_comments: bool, } -#[derive(Clone, TypedBuilder)] -#[builder(field_defaults(default))] +#[derive(Clone, derive_new::new)] #[cfg_attr(feature = "full", derive(Insertable))] #[cfg_attr(feature = "full", diesel(table_name = local_user))] pub struct LocalUserInsertForm { - #[builder(!default)] pub person_id: PersonId, - #[builder(!default)] pub password_encrypted: String, + #[new(default)] pub email: Option, + #[new(default)] pub show_nsfw: Option, + #[new(default)] pub theme: Option, + #[new(default)] pub default_sort_type: Option, + #[new(default)] pub default_listing_type: Option, + #[new(default)] pub interface_language: Option, + #[new(default)] pub show_avatars: Option, + #[new(default)] pub send_notifications_to_email: Option, + #[new(default)] pub show_bot_accounts: Option, + #[new(default)] pub show_scores: Option, + #[new(default)] pub show_read_posts: Option, + #[new(default)] pub email_verified: Option, + #[new(default)] pub accepted_application: Option, + #[new(default)] pub totp_2fa_secret: Option>, + #[new(default)] pub open_links_in_new_tab: Option, + #[new(default)] pub blur_nsfw: Option, + #[new(default)] pub auto_expand: Option, + #[new(default)] pub infinite_scroll_enabled: Option, + #[new(default)] pub admin: Option, + #[new(default)] pub post_listing_mode: Option, + #[new(default)] pub totp_2fa_enabled: Option, + #[new(default)] pub enable_keyboard_navigation: Option, + #[new(default)] pub enable_animated_images: Option, + #[new(default)] pub collapse_bot_comments: Option, } diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index 950d061ba..d7b26a1ed 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -301,10 +301,7 @@ mod tests { let inserted_timmy = Person::create(pool, &new_person).await.unwrap(); - let new_local_user = LocalUserInsertForm::builder() - .person_id(inserted_timmy.id) - .password_encrypted("123".to_string()) - .build(); + let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id); let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]) .await .unwrap(); diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index 0e476b3b6..e2752a0c7 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -484,11 +484,8 @@ mod tests { let timmy_person_form = PersonInsertForm::test_form(inserted_instance.id, "timmy"); let inserted_timmy_person = Person::create(pool, &timmy_person_form).await?; - let timmy_local_user_form = LocalUserInsertForm::builder() - .person_id(inserted_timmy_person.id) - .admin(Some(true)) - .password_encrypted(String::new()) - .build(); + let timmy_local_user_form = LocalUserInsertForm::test_form_admin(inserted_timmy_person.id); + let inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?; let sara_person_form = PersonInsertForm::test_form(inserted_instance.id, "sara"); diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index e89b7d545..0cd06dd4e 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -323,10 +323,7 @@ mod tests { let inserted_timmy = Person::create(pool, &new_person).await.unwrap(); - let new_local_user = LocalUserInsertForm::builder() - .person_id(inserted_timmy.id) - .password_encrypted("123".to_string()) - .build(); + let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id); let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]) .await .unwrap(); diff --git a/crates/db_views/src/registration_application_view.rs b/crates/db_views/src/registration_application_view.rs index cd63859af..9a85d534a 100644 --- a/crates/db_views/src/registration_application_view.rs +++ b/crates/db_views/src/registration_application_view.rs @@ -167,11 +167,7 @@ mod tests { let inserted_timmy_person = Person::create(pool, &timmy_person_form).await.unwrap(); - let timmy_local_user_form = LocalUserInsertForm::builder() - .person_id(inserted_timmy_person.id) - .password_encrypted("nada".to_string()) - .admin(Some(true)) - .build(); + let timmy_local_user_form = LocalUserInsertForm::test_form_admin(inserted_timmy_person.id); let _inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]) .await @@ -181,10 +177,7 @@ mod tests { let inserted_sara_person = Person::create(pool, &sara_person_form).await.unwrap(); - let sara_local_user_form = LocalUserInsertForm::builder() - .person_id(inserted_sara_person.id) - .password_encrypted("nada".to_string()) - .build(); + let sara_local_user_form = LocalUserInsertForm::test_form(inserted_sara_person.id); let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form, vec![]) .await @@ -209,10 +202,7 @@ mod tests { let inserted_jess_person = Person::create(pool, &jess_person_form).await.unwrap(); - let jess_local_user_form = LocalUserInsertForm::builder() - .person_id(inserted_jess_person.id) - .password_encrypted("nada".to_string()) - .build(); + let jess_local_user_form = LocalUserInsertForm::test_form(inserted_jess_person.id); let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form, vec![]) .await diff --git a/crates/db_views_actor/src/community_view.rs b/crates/db_views_actor/src/community_view.rs index 7d8a7277f..0e731878a 100644 --- a/crates/db_views_actor/src/community_view.rs +++ b/crates/db_views_actor/src/community_view.rs @@ -275,10 +275,7 @@ mod tests { let inserted_person = Person::create(pool, &new_person).await.unwrap(); - let local_user_form = LocalUserInsertForm::builder() - .person_id(inserted_person.id) - .password_encrypted(String::new()) - .build(); + let local_user_form = LocalUserInsertForm::test_form(inserted_person.id); let local_user = LocalUser::create(pool, &local_user_form, vec![]) .await .unwrap(); diff --git a/crates/db_views_actor/src/person_view.rs b/crates/db_views_actor/src/person_view.rs index 98a0ca38d..26e4ee680 100644 --- a/crates/db_views_actor/src/person_view.rs +++ b/crates/db_views_actor/src/person_view.rs @@ -196,10 +196,7 @@ mod tests { ..PersonInsertForm::test_form(inserted_instance.id, "alice") }; let alice = Person::create(pool, &alice_form).await?; - let alice_local_user_form = LocalUserInsertForm::builder() - .person_id(alice.id) - .password_encrypted(String::new()) - .build(); + let alice_local_user_form = LocalUserInsertForm::test_form(alice.id); let alice_local_user = LocalUser::create(pool, &alice_local_user_form, vec![]).await?; let bob_form = PersonInsertForm { @@ -208,10 +205,7 @@ mod tests { ..PersonInsertForm::test_form(inserted_instance.id, "bob") }; let bob = Person::create(pool, &bob_form).await?; - let bob_local_user_form = LocalUserInsertForm::builder() - .person_id(bob.id) - .password_encrypted(String::new()) - .build(); + let bob_local_user_form = LocalUserInsertForm::test_form(bob.id); let bob_local_user = LocalUser::create(pool, &bob_local_user_form, vec![]).await?; Ok(Data { diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index 324c08ccb..b848916b2 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -38,6 +38,8 @@ pub enum LemmyErrorType { NotTopAdmin, NotTopMod, NotLoggedIn, + NotHigherMod, + NotHigherAdmin, SiteBan, Deleted, BannedFromCommunity, diff --git a/src/code_migrations.rs b/src/code_migrations.rs index fd4ef66de..ae6155bb6 100644 --- a/src/code_migrations.rs +++ b/src/code_migrations.rs @@ -468,12 +468,11 @@ async fn initialize_local_site_2022_10_10( }; let person_inserted = Person::create(pool, &person_form).await?; - let local_user_form = LocalUserInsertForm::builder() - .person_id(person_inserted.id) - .password_encrypted(setup.admin_password.clone()) - .email(setup.admin_email.clone()) - .admin(Some(true)) - .build(); + let local_user_form = LocalUserInsertForm { + email: setup.admin_email.clone(), + admin: Some(true), + ..LocalUserInsertForm::new(person_inserted.id, setup.admin_password.clone()) + }; LocalUser::create(pool, &local_user_form, vec![]).await?; }; diff --git a/src/session_middleware.rs b/src/session_middleware.rs index 8b3090a47..a72d84920 100644 --- a/src/session_middleware.rs +++ b/src/session_middleware.rs @@ -146,10 +146,7 @@ mod tests { let inserted_person = Person::create(pool, &new_person).await.unwrap(); - let local_user_form = LocalUserInsertForm::builder() - .person_id(inserted_person.id) - .password_encrypted("123456".to_string()) - .build(); + let local_user_form = LocalUserInsertForm::test_form(inserted_person.id); let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![]) .await