pull/4860/merge
Dessalines 2024-07-02 20:15:32 -05:00 committed by GitHub
commit f76cfd4677
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 451 additions and 218 deletions

View File

@ -4,7 +4,7 @@ use lemmy_api_common::{
community::{AddModToCommunity, AddModToCommunityResponse}, community::{AddModToCommunity, AddModToCommunityResponse},
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
utils::check_community_mod_action, utils::{check_community_mod_action, check_is_higher_mod},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
@ -33,6 +33,18 @@ pub async fn add_mod_to_community(
&mut context.pool(), &mut context.pool(),
) )
.await?; .await?;
// If its a mod removal, also check that you're a higher mod.
if !data.added {
check_is_higher_mod(
&mut context.pool(),
&local_user_view,
community_id,
&[data.person_id],
)
.await?;
}
let community = Community::read(&mut context.pool(), community_id) let community = Community::read(&mut context.pool(), community_id)
.await? .await?
.ok_or(LemmyErrorType::CouldntFindCommunity)?; .ok_or(LemmyErrorType::CouldntFindCommunity)?;

View File

@ -4,7 +4,12 @@ use lemmy_api_common::{
community::{BanFromCommunity, BanFromCommunityResponse}, community::{BanFromCommunity, BanFromCommunityResponse},
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData}, 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::{ use lemmy_db_schema::{
source::{ source::{
@ -44,6 +49,14 @@ pub async fn ban_from_community(
) )
.await?; .await?;
check_is_higher_mod(
&mut context.pool(),
&local_user_view,
data.community_id,
&[data.person_id],
)
.await?;
if let Some(reason) = &data.reason { if let Some(reason) = &data.reason {
is_valid_body_field(reason, false)?; is_valid_body_field(reason, false)?;
} }

View File

@ -2,7 +2,7 @@ use actix_web::web::{Data, Json};
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
person::{AddAdmin, AddAdminResponse}, person::{AddAdmin, AddAdminResponse},
utils::is_admin, utils::{check_is_higher_admin, is_admin},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
@ -24,6 +24,11 @@ pub async fn add_admin(
// Make sure user is an admin // Make sure user is an admin
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
// If its an admin removal, also check that you're a higher admin
if !data.added {
check_is_higher_admin(&mut context.pool(), &local_user_view, &[data.person_id]).await?;
}
// Make sure that the person_id added is local // Make sure that the person_id added is local
let added_local_user = LocalUserView::read_person(&mut context.pool(), data.person_id) let added_local_user = LocalUserView::read_person(&mut context.pool(), data.person_id)
.await? .await?

View File

@ -5,7 +5,7 @@ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
person::{BanPerson, BanPersonResponse}, person::{BanPerson, BanPersonResponse},
send_activity::{ActivityChannel, SendActivityData}, 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::{ use lemmy_db_schema::{
source::{ source::{
@ -31,6 +31,9 @@ pub async fn ban_from_site(
// Make sure user is an admin // Make sure user is an admin
is_admin(&local_user_view)?; 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 { if let Some(reason) = &data.reason {
is_valid_body_field(reason, false)?; is_valid_body_field(reason, false)?;
} }

View File

@ -4,7 +4,7 @@ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
site::PurgeComment, site::PurgeComment,
utils::is_admin, utils::{check_is_higher_admin, is_admin},
SuccessResponse, SuccessResponse,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -33,6 +33,14 @@ pub async fn purge_comment(
.await? .await?
.ok_or(LemmyErrorType::CouldntFindComment)?; .ok_or(LemmyErrorType::CouldntFindComment)?;
// Also check that you're a higher admin
check_is_higher_admin(
&mut context.pool(),
&local_user_view,
&[comment_view.creator.id],
)
.await?;
let post_id = comment_view.comment.post_id; let post_id = comment_view.comment.post_id;
// TODO read comments for pictrs images and purge them // TODO read comments for pictrs images and purge them

View File

@ -5,10 +5,11 @@ use lemmy_api_common::{
request::purge_image_from_pictrs, request::purge_image_from_pictrs,
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
site::PurgeCommunity, site::PurgeCommunity,
utils::{is_admin, purge_image_posts_for_community}, utils::{check_is_higher_admin, is_admin, purge_image_posts_for_community},
SuccessResponse, SuccessResponse,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::PersonId,
source::{ source::{
community::Community, community::Community,
moderator::{AdminPurgeCommunity, AdminPurgeCommunityForm}, moderator::{AdminPurgeCommunity, AdminPurgeCommunityForm},
@ -16,6 +17,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::{error::LemmyResult, LemmyErrorType}; use lemmy_utils::{error::LemmyResult, LemmyErrorType};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
@ -32,6 +34,21 @@ pub async fn purge_community(
.await? .await?
.ok_or(LemmyErrorType::CouldntFindCommunity)?; .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::<Vec<PersonId>>();
check_is_higher_admin(
&mut context.pool(),
&local_user_view,
&community_mod_person_ids,
)
.await?;
if let Some(banner) = &community.banner { if let Some(banner) = &community.banner {
purge_image_from_pictrs(banner, &context).await.ok(); purge_image_from_pictrs(banner, &context).await.ok();
} }

View File

@ -5,7 +5,7 @@ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
site::PurgePerson, site::PurgePerson,
utils::{is_admin, purge_user_account}, utils::{check_is_higher_admin, is_admin, purge_user_account},
SuccessResponse, SuccessResponse,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -27,9 +27,13 @@ pub async fn purge_person(
// Only let admin purge an item // Only let admin purge an item
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
// Also check that you're a higher admin
check_is_higher_admin(&mut context.pool(), &local_user_view, &[data.person_id]).await?;
let person = Person::read(&mut context.pool(), data.person_id) let person = Person::read(&mut context.pool(), data.person_id)
.await? .await?
.ok_or(LemmyErrorType::CouldntFindPerson)?; .ok_or(LemmyErrorType::CouldntFindPerson)?;
ban_nonlocal_user_from_local_communities( ban_nonlocal_user_from_local_communities(
&local_user_view, &local_user_view,
&person, &person,

View File

@ -5,7 +5,7 @@ use lemmy_api_common::{
request::purge_image_from_pictrs, request::purge_image_from_pictrs,
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
site::PurgePost, site::PurgePost,
utils::is_admin, utils::{check_is_higher_admin, is_admin},
SuccessResponse, SuccessResponse,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -32,6 +32,9 @@ pub async fn purge_post(
.await? .await?
.ok_or(LemmyErrorType::CouldntFindPost)?; .ok_or(LemmyErrorType::CouldntFindPost)?;
// Also check that you're a higher admin
check_is_higher_admin(&mut context.pool(), &local_user_view, &[post.creator_id]).await?;
// Purge image // Purge image
if let Some(url) = &post.url { if let Some(url) = &post.url {
purge_image_from_pictrs(url, &context).await.ok(); purge_image_from_pictrs(url, &context).await.ok();

View File

@ -116,10 +116,7 @@ mod tests {
let inserted_person = Person::create(pool, &new_person).await.unwrap(); let inserted_person = Person::create(pool, &new_person).await.unwrap();
let local_user_form = LocalUserInsertForm::builder() let local_user_form = LocalUserInsertForm::test_form(inserted_person.id);
.person_id(inserted_person.id)
.password_encrypted("123456".to_string())
.build();
let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![]) let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![])
.await .await

View File

@ -23,6 +23,7 @@ use lemmy_db_schema::{
local_site::LocalSite, local_site::LocalSite,
local_site_rate_limit::LocalSiteRateLimit, local_site_rate_limit::LocalSiteRateLimit,
local_site_url_blocklist::LocalSiteUrlBlocklist, local_site_url_blocklist::LocalSiteUrlBlocklist,
local_user::LocalUser,
password_reset_request::PasswordResetRequest, password_reset_request::PasswordResetRequest,
person::{Person, PersonUpdateForm}, person::{Person, PersonUpdateForm},
person_block::PersonBlock, person_block::PersonBlock,
@ -144,6 +145,58 @@ pub fn is_top_mod(
} }
} }
/// Checks to make sure the acting moderator is higher than the target moderator.
pub async fn check_is_higher_mod(
pool: &mut DbPool<'_>,
local_user_view: &LocalUserView,
community_id: CommunityId,
target_person_ids: &[PersonId],
) -> LemmyResult<()> {
CommunityModerator::is_higher_mod_check(
pool,
community_id,
local_user_view.person.id,
target_person_ids,
)
.await
.with_lemmy_type(LemmyErrorType::NotHigherMod)?;
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_admin(
pool: &mut DbPool<'_>,
local_user_view: &LocalUserView,
target_person_ids: &[PersonId],
) -> LemmyResult<()> {
LocalUser::is_higher_admin_check(pool, local_user_view.person.id, target_person_ids)
.await
.with_lemmy_type(LemmyErrorType::NotHigherAdmin)?;
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. /// Marks a post as read for a given person.
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn mark_post_as_read( pub async fn mark_post_as_read(

View File

@ -5,7 +5,7 @@ use lemmy_api_common::{
comment::{CommentResponse, RemoveComment}, comment::{CommentResponse, RemoveComment},
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
utils::check_community_mod_action, utils::{check_community_mod_action, check_is_higher_mod_or_admin},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
@ -37,6 +37,14 @@ pub async fn remove_comment(
) )
.await?; .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 // Don't allow removing or restoring comment which was deleted by user, as it would reveal
// the comment text in mod log. // the comment text in mod log.
if orig_comment.comment.deleted { if orig_comment.comment.deleted {

View File

@ -150,18 +150,18 @@ pub async fn register(
.unwrap_or(site_view.site.content_warning.is_some()); .unwrap_or(site_view.site.content_warning.is_some());
// Create the local user // Create the local user
let local_user_form = LocalUserInsertForm::builder() let local_user_form = LocalUserInsertForm {
.person_id(inserted_person.id) email: data.email.as_deref().map(str::to_lowercase),
.email(data.email.as_deref().map(str::to_lowercase)) password_encrypted: data.password.to_string(),
.password_encrypted(data.password.to_string()) show_nsfw: Some(show_nsfw),
.show_nsfw(Some(show_nsfw)) accepted_application,
.accepted_application(accepted_application) default_listing_type: Some(local_site.default_post_listing_type),
.default_listing_type(Some(local_site.default_post_listing_type)) post_listing_mode: Some(local_site.default_post_listing_mode),
.post_listing_mode(Some(local_site.default_post_listing_mode)) interface_language: language_tags.first().cloned(),
.interface_language(language_tags.first().cloned())
// If its the initial site setup, they are an admin // If its the initial site setup, they are an admin
.admin(Some(!local_site.site_setup)) admin: Some(!local_site.site_setup),
.build(); ..LocalUserInsertForm::new(inserted_person.id, data.password.to_string())
};
let all_languages = Language::read_all(&mut context.pool()).await?; let all_languages = Language::read_all(&mut context.pool()).await?;
// use hashset to avoid duplicates // use hashset to avoid duplicates

View File

@ -345,10 +345,7 @@ mod tests {
}; };
let person = Person::create(&mut context.pool(), &person_form).await?; let person = Person::create(&mut context.pool(), &person_form).await?;
let user_form = LocalUserInsertForm::builder() let user_form = LocalUserInsertForm::test_form(person.id);
.person_id(person.id)
.password_encrypted("pass".to_string())
.build();
let local_user = LocalUser::create(&mut context.pool(), &user_form, vec![]).await?; let local_user = LocalUser::create(&mut context.pool(), &user_form, vec![]).await?;
Ok( Ok(

View File

@ -533,10 +533,7 @@ mod tests {
let person_form = PersonInsertForm::test_form(instance.id, "my test person"); let person_form = PersonInsertForm::test_form(instance.id, "my test person");
let person = Person::create(pool, &person_form).await.unwrap(); let person = Person::create(pool, &person_form).await.unwrap();
let local_user_form = LocalUserInsertForm::builder() let local_user_form = LocalUserInsertForm::test_form(person.id);
.person_id(person.id)
.password_encrypted("my_pw".to_string())
.build();
let local_user = LocalUser::create(pool, &local_user_form, vec![]) let local_user = LocalUser::create(pool, &local_user_form, vec![])
.await .await
@ -645,10 +642,7 @@ mod tests {
let person_form = PersonInsertForm::test_form(instance.id, "my test person"); let person_form = PersonInsertForm::test_form(instance.id, "my test person");
let person = Person::create(pool, &person_form).await.unwrap(); let person = Person::create(pool, &person_form).await.unwrap();
let local_user_form = LocalUserInsertForm::builder() let local_user_form = LocalUserInsertForm::test_form(person.id);
.person_id(person.id)
.password_encrypted("my_pw".to_string())
.build();
let local_user = LocalUser::create(pool, &local_user_form, vec![]) let local_user = LocalUser::create(pool, &local_user_form, vec![])
.await .await
.unwrap(); .unwrap();

View File

@ -1,7 +1,14 @@
use crate::{ use crate::{
diesel::{DecoratableTarget, OptionalExtension}, diesel::{DecoratableTarget, OptionalExtension},
newtypes::{CommunityId, DbUrl, PersonId}, newtypes::{CommunityId, DbUrl, PersonId},
schema::{community, community_follower, instance}, schema::{
community,
community_follower,
community_moderator,
community_person_ban,
instance,
post,
},
source::{ source::{
actor_language::CommunityLanguage, actor_language::CommunityLanguage,
community::{ community::{
@ -83,9 +90,8 @@ impl Joinable for CommunityModerator {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
community_moderator_form: &CommunityModeratorForm, community_moderator_form: &CommunityModeratorForm,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
use crate::schema::community_moderator::dsl::community_moderator;
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(community_moderator) insert_into(community_moderator::table)
.values(community_moderator_form) .values(community_moderator_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
@ -95,9 +101,8 @@ impl Joinable for CommunityModerator {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
community_moderator_form: &CommunityModeratorForm, community_moderator_form: &CommunityModeratorForm,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
use crate::schema::community_moderator::dsl::community_moderator;
let conn = &mut get_conn(pool).await?; 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.person_id,
community_moderator_form.community_id, community_moderator_form.community_id,
))) )))
@ -147,25 +152,23 @@ impl Community {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
url: &DbUrl, url: &DbUrl,
) -> Result<Option<(Community, CollectionType)>, Error> { ) -> Result<Option<(Community, CollectionType)>, Error> {
use crate::schema::community::dsl::{featured_url, moderators_url};
use CollectionType::*;
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let res = community::table let res = community::table
.filter(moderators_url.eq(url)) .filter(community::moderators_url.eq(url))
.first(conn) .first(conn)
.await .await
.optional()?; .optional()?;
if let Some(c) = res { if let Some(c) = res {
Ok(Some((c, Moderators))) Ok(Some((c, CollectionType::Moderators)))
} else { } else {
let res = community::table let res = community::table
.filter(featured_url.eq(url)) .filter(community::featured_url.eq(url))
.first(conn) .first(conn)
.await .await
.optional()?; .optional()?;
if let Some(c) = res { if let Some(c) = res {
Ok(Some((c, Featured))) Ok(Some((c, CollectionType::Featured)))
} else { } else {
Ok(None) Ok(None)
} }
@ -177,7 +180,6 @@ impl Community {
posts: Vec<Post>, posts: Vec<Post>,
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
use crate::schema::post;
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
for p in &posts { for p in &posts {
debug_assert!(p.community_id == community_id); debug_assert!(p.community_id == community_id);
@ -185,10 +187,10 @@ impl Community {
// Mark the given posts as featured and all other posts as not featured. // Mark the given posts as featured and all other posts as not featured.
let post_ids = posts.iter().map(|p| p.id); let post_ids = posts.iter().map(|p| p.id);
update(post::table) update(post::table)
.filter(post::dsl::community_id.eq(community_id)) .filter(post::community_id.eq(community_id))
// This filter is just for performance // This filter is just for performance
.filter(post::dsl::featured_community.or(post::dsl::id.eq_any(post_ids.clone()))) .filter(post::featured_community.or(post::id.eq_any(post_ids.clone())))
.set(post::dsl::featured_community.eq(post::dsl::id.eq_any(post_ids))) .set(post::featured_community.eq(post::id.eq_any(post_ids)))
.execute(conn) .execute(conn)
.await?; .await?;
Ok(()) Ok(())
@ -200,37 +202,68 @@ impl CommunityModerator {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_community_id: CommunityId, for_community_id: CommunityId,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
use crate::schema::community_moderator::dsl::{community_id, community_moderator};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
diesel::delete(community_moderator.filter(community_id.eq(for_community_id))) diesel::delete(
.execute(conn) community_moderator::table.filter(community_moderator::community_id.eq(for_community_id)),
.await )
.execute(conn)
.await
} }
pub async fn leave_all_communities( pub async fn leave_all_communities(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_person_id: PersonId, for_person_id: PersonId,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
use crate::schema::community_moderator::dsl::{community_moderator, person_id};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
diesel::delete(community_moderator.filter(person_id.eq(for_person_id))) diesel::delete(
.execute(conn) community_moderator::table.filter(community_moderator::person_id.eq(for_person_id)),
.await )
.execute(conn)
.await
} }
pub async fn get_person_moderated_communities( pub async fn get_person_moderated_communities(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_person_id: PersonId, for_person_id: PersonId,
) -> Result<Vec<CommunityId>, Error> { ) -> Result<Vec<CommunityId>, Error> {
use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
community_moderator community_moderator::table
.filter(person_id.eq(for_person_id)) .filter(community_moderator::person_id.eq(for_person_id))
.select(community_id) .select(community_moderator::community_id)
.load::<CommunityId>(conn) .load::<CommunityId>(conn)
.await .await
} }
/// Checks to make sure the acting moderator is higher 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: &[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(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::<CommunityModerator>(conn)
.await?;
// If the first result sorted by published is the acting mod
if res.person_id == mod_person_id {
Ok(())
} else {
Err(diesel::result::Error::NotFound)
}
}
} }
#[async_trait] #[async_trait]
@ -240,11 +273,13 @@ impl Bannable for CommunityPersonBan {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
community_person_ban_form: &CommunityPersonBanForm, community_person_ban_form: &CommunityPersonBanForm,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(community_person_ban) insert_into(community_person_ban::table)
.values(community_person_ban_form) .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() .do_update()
.set(community_person_ban_form) .set(community_person_ban_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
@ -255,9 +290,8 @@ impl Bannable for CommunityPersonBan {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
community_person_ban_form: &CommunityPersonBanForm, community_person_ban_form: &CommunityPersonBanForm,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
use crate::schema::community_person_ban::dsl::community_person_ban;
let conn = &mut get_conn(pool).await?; 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.person_id,
community_person_ban_form.community_id, community_person_ban_form.community_id,
))) )))
@ -291,11 +325,10 @@ impl CommunityFollower {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
remote_community_id: CommunityId, remote_community_id: CommunityId,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
use crate::schema::community_follower::dsl::{community_follower, community_id};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(exists(community_follower::table.filter(
community_follower.filter(community_id.eq(remote_community_id)), community_follower::community_id.eq(remote_community_id),
)) )))
.get_result(conn) .get_result(conn)
.await .await
} }
@ -316,11 +349,13 @@ impl Queryable<sql_types::Nullable<sql_types::Bool>, Pg> for SubscribedType {
impl Followable for CommunityFollower { impl Followable for CommunityFollower {
type Form = CommunityFollowerForm; type Form = CommunityFollowerForm;
async fn follow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result<Self, Error> { async fn follow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result<Self, Error> {
use crate::schema::community_follower::dsl::{community_follower, community_id, person_id};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(community_follower) insert_into(community_follower::table)
.values(form) .values(form)
.on_conflict((community_id, person_id)) .on_conflict((
community_follower::community_id,
community_follower::person_id,
))
.do_update() .do_update()
.set(form) .set(form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
@ -331,17 +366,16 @@ impl Followable for CommunityFollower {
community_id: CommunityId, community_id: CommunityId,
person_id: PersonId, person_id: PersonId,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
use crate::schema::community_follower::dsl::{community_follower, pending};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
diesel::update(community_follower.find((person_id, community_id))) diesel::update(community_follower::table.find((person_id, community_id)))
.set(pending.eq(false)) .set(community_follower::pending.eq(false))
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
} }
async fn unfollow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result<usize, Error> { async fn unfollow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result<usize, Error> {
use crate::schema::community_follower::dsl::community_follower;
let conn = &mut get_conn(pool).await?; 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) .execute(conn)
.await .await
} }
@ -397,10 +431,8 @@ impl ApubActor for Community {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::indexing_slicing)] #[allow(clippy::indexing_slicing)]
mod tests { mod tests {
use crate::{ use crate::{
source::{ source::{
community::{ community::{
@ -421,22 +453,23 @@ mod tests {
utils::build_db_pool_for_tests, utils::build_db_pool_for_tests,
CommunityVisibility, CommunityVisibility,
}; };
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serial_test::serial; use serial_test::serial;
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_crud() { async fn test_crud() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests().await; let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into(); let pool = &mut pool.into();
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
.await
.unwrap();
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() let new_community = CommunityInsertForm::builder()
.name("TIL".into()) .name("TIL".into())
@ -445,7 +478,7 @@ mod tests {
.instance_id(inserted_instance.id) .instance_id(inserted_instance.id)
.build(); .build();
let inserted_community = Community::create(pool, &new_community).await.unwrap(); let inserted_community = Community::create(pool, &new_community).await?;
let expected_community = Community { let expected_community = Community {
id: inserted_community.id, id: inserted_community.id,
@ -477,91 +510,110 @@ mod tests {
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
community_id: inserted_community.id, community_id: inserted_community.id,
person_id: inserted_person.id, person_id: inserted_bobby.id,
pending: false, pending: false,
}; };
let inserted_community_follower = CommunityFollower::follow(pool, &community_follower_form) let inserted_community_follower =
.await CommunityFollower::follow(pool, &community_follower_form).await?;
.unwrap();
let expected_community_follower = CommunityFollower { let expected_community_follower = CommunityFollower {
community_id: inserted_community.id, community_id: inserted_community.id,
person_id: inserted_person.id, person_id: inserted_bobby.id,
pending: false, pending: false,
published: inserted_community_follower.published, published: inserted_community_follower.published,
}; };
let community_moderator_form = CommunityModeratorForm { let bobby_moderator_form = CommunityModeratorForm {
community_id: inserted_community.id, 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) let inserted_bobby_moderator = CommunityModerator::join(pool, &bobby_moderator_form).await?;
.await
.unwrap(); 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 { let expected_community_moderator = CommunityModerator {
community_id: inserted_community.id, community_id: inserted_community.id,
person_id: inserted_person.id, person_id: inserted_bobby.id,
published: inserted_community_moderator.published, 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,
)
.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(
pool,
inserted_community.id,
inserted_artemis.id,
&moderator_person_ids,
)
.await;
assert!(artemis_higher_check.is_err());
let community_person_ban_form = CommunityPersonBanForm { let community_person_ban_form = CommunityPersonBanForm {
community_id: inserted_community.id, community_id: inserted_community.id,
person_id: inserted_person.id, person_id: inserted_bobby.id,
expires: None, expires: None,
}; };
let inserted_community_person_ban = CommunityPersonBan::ban(pool, &community_person_ban_form) let inserted_community_person_ban =
.await CommunityPersonBan::ban(pool, &community_person_ban_form).await?;
.unwrap();
let expected_community_person_ban = CommunityPersonBan { let expected_community_person_ban = CommunityPersonBan {
community_id: inserted_community.id, community_id: inserted_community.id,
person_id: inserted_person.id, person_id: inserted_bobby.id,
published: inserted_community_person_ban.published, published: inserted_community_person_ban.published,
expires: None, expires: None,
}; };
let read_community = Community::read(pool, inserted_community.id) let read_community = Community::read(pool, inserted_community.id)
.await .await?
.unwrap() .ok_or(LemmyErrorType::CouldntFindCommunity)?;
.unwrap();
let update_community_form = CommunityUpdateForm { let update_community_form = CommunityUpdateForm {
title: Some("nada".to_owned()), title: Some("nada".to_owned()),
..Default::default() ..Default::default()
}; };
let updated_community = Community::update(pool, inserted_community.id, &update_community_form) let updated_community =
.await Community::update(pool, inserted_community.id, &update_community_form).await?;
.unwrap();
let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form) let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form).await?;
.await let left_community = CommunityModerator::leave(pool, &bobby_moderator_form).await?;
.unwrap(); let unban = CommunityPersonBan::unban(pool, &community_person_ban_form).await?;
let left_community = CommunityModerator::leave(pool, &community_moderator_form) let num_deleted = Community::delete(pool, inserted_community.id).await?;
.await Person::delete(pool, inserted_bobby.id).await?;
.unwrap(); Person::delete(pool, inserted_artemis.id).await?;
let unban = CommunityPersonBan::unban(pool, &community_person_ban_form) Instance::delete(pool, inserted_instance.id).await?;
.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();
assert_eq!(expected_community, read_community); assert_eq!(expected_community, read_community);
assert_eq!(expected_community, inserted_community); assert_eq!(expected_community, inserted_community);
assert_eq!(expected_community, updated_community); assert_eq!(expected_community, updated_community);
assert_eq!(expected_community_follower, inserted_community_follower); 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!(expected_community_person_ban, inserted_community_person_ban);
assert_eq!(1, ignored_community); assert_eq!(1, ignored_community);
assert_eq!(1, left_community); assert_eq!(1, left_community);
assert_eq!(1, unban); assert_eq!(1, unban);
// assert_eq!(2, loaded_count); // assert_eq!(2, loaded_count);
assert_eq!(1, num_deleted); assert_eq!(1, num_deleted);
Ok(())
} }
} }

View File

@ -215,6 +215,35 @@ impl LocalUser {
blocked_instances, blocked_instances,
}) })
} }
/// Checks to make sure the acting moderator is higher than the target moderator
pub async fn is_higher_admin_check(
pool: &mut DbPool<'_>,
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 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::<LocalUser>(conn)
.await?;
// If the first result sorted by published is the acting mod
if res.person_id == admin_person_id {
Ok(())
} else {
Err(diesel::result::Error::NotFound)
}
}
} }
/// Adds some helper functions for an optional LocalUser /// Adds some helper functions for an optional LocalUser
@ -257,10 +286,14 @@ impl LocalUserOptionHelper for Option<&LocalUser> {
impl LocalUserInsertForm { impl LocalUserInsertForm {
pub fn test_form(person_id: PersonId) -> Self { pub fn test_form(person_id: PersonId) -> Self {
Self::builder() Self::new(person_id, String::new())
.person_id(person_id) }
.password_encrypted(String::new())
.build() pub fn test_form_admin(person_id: PersonId) -> Self {
LocalUserInsertForm {
admin: Some(true),
..Self::test_form(person_id)
}
} }
} }
@ -272,3 +305,57 @@ pub struct UserBackupLists {
pub blocked_users: Vec<DbUrl>, pub blocked_users: Vec<DbUrl>,
pub blocked_instances: Vec<String>, pub blocked_instances: Vec<String>,
} }
#[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).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(())
}
}

View File

@ -72,10 +72,7 @@ mod tests {
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; 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 new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy prw");
let inserted_person = Person::create(pool, &new_person).await?; let inserted_person = Person::create(pool, &new_person).await?;
let new_local_user = LocalUserInsertForm::builder() let new_local_user = LocalUserInsertForm::test_form(inserted_person.id);
.person_id(inserted_person.id)
.password_encrypted("pass".to_string())
.build();
let inserted_local_user = LocalUser::create(pool, &new_local_user, vec![]).await?; let inserted_local_user = LocalUser::create(pool, &new_local_user, vec![]).await?;
// Create password reset token // Create password reset token

View File

@ -182,11 +182,10 @@ impl ApubActor for Person {
impl Followable for PersonFollower { impl Followable for PersonFollower {
type Form = PersonFollowerForm; type Form = PersonFollowerForm;
async fn follow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result<Self, Error> { async fn follow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result<Self, Error> {
use crate::schema::person_follower::dsl::{follower_id, person_follower, person_id};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(person_follower) insert_into(person_follower::table)
.values(form) .values(form)
.on_conflict((follower_id, person_id)) .on_conflict((person_follower::follower_id, person_follower::person_id))
.do_update() .do_update()
.set(form) .set(form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
@ -196,9 +195,8 @@ impl Followable for PersonFollower {
unimplemented!() unimplemented!()
} }
async fn unfollow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result<usize, Error> { async fn unfollow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result<usize, Error> {
use crate::schema::person_follower::dsl::person_follower;
let conn = &mut get_conn(pool).await?; 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) .execute(conn)
.await .await
} }
@ -220,7 +218,6 @@ impl PersonFollower {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::indexing_slicing)] #[allow(clippy::indexing_slicing)]
mod tests { mod tests {
@ -232,22 +229,21 @@ mod tests {
traits::{Crud, Followable}, traits::{Crud, Followable},
utils::build_db_pool_for_tests, utils::build_db_pool_for_tests,
}; };
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serial_test::serial; use serial_test::serial;
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_crud() { async fn test_crud() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests().await; let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into(); let pool = &mut pool.into();
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
.await
.unwrap();
let new_person = PersonInsertForm::test_form(inserted_instance.id, "holly"); 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 { let expected_person = Person {
id: inserted_person.id, id: inserted_person.id,
@ -274,57 +270,54 @@ mod tests {
}; };
let read_person = Person::read(pool, inserted_person.id) let read_person = Person::read(pool, inserted_person.id)
.await .await?
.unwrap() .ok_or(LemmyErrorType::CouldntFindPerson)?;
.unwrap();
let update_person_form = PersonUpdateForm { let update_person_form = PersonUpdateForm {
actor_id: Some(inserted_person.actor_id.clone()), actor_id: Some(inserted_person.actor_id.clone()),
..Default::default() ..Default::default()
}; };
let updated_person = Person::update(pool, inserted_person.id, &update_person_form) let updated_person = Person::update(pool, inserted_person.id, &update_person_form).await?;
.await
.unwrap();
let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap(); let num_deleted = Person::delete(pool, inserted_person.id).await?;
Instance::delete(pool, inserted_instance.id).await.unwrap(); Instance::delete(pool, inserted_instance.id).await?;
assert_eq!(expected_person, read_person); assert_eq!(expected_person, read_person);
assert_eq!(expected_person, inserted_person); assert_eq!(expected_person, inserted_person);
assert_eq!(expected_person, updated_person); assert_eq!(expected_person, updated_person);
assert_eq!(1, num_deleted); assert_eq!(1, num_deleted);
Ok(())
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn follow() { async fn follow() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests().await; let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into(); let pool = &mut pool.into();
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
.await
.unwrap();
let person_form_1 = PersonInsertForm::test_form(inserted_instance.id, "erich"); 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_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 { let follow_form = PersonFollowerForm {
person_id: person_1.id, person_id: person_1.id,
follower_id: person_2.id, follower_id: person_2.id,
pending: false, 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_1.id, person_follower.person_id);
assert_eq!(person_2.id, person_follower.follower_id); assert_eq!(person_2.id, person_follower.follower_id);
assert!(!person_follower.pending); assert!(!person_follower.pending);
let followers = PersonFollower::list_followers(pool, person_1.id) let followers = PersonFollower::list_followers(pool, person_1.id).await?;
.await
.unwrap();
assert_eq!(vec![person_2], followers); 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); assert_eq!(1, unfollow);
Ok(())
} }
} }

View File

@ -11,7 +11,6 @@ use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
#[cfg(feature = "full")] #[cfg(feature = "full")]
use ts_rs::TS; use ts_rs::TS;
use typed_builder::TypedBuilder;
#[skip_serializing_none] #[skip_serializing_none]
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
@ -69,38 +68,59 @@ pub struct LocalUser {
pub collapse_bot_comments: bool, pub collapse_bot_comments: bool,
} }
#[derive(Clone, TypedBuilder)] #[derive(Clone, derive_new::new)]
#[builder(field_defaults(default))]
#[cfg_attr(feature = "full", derive(Insertable))] #[cfg_attr(feature = "full", derive(Insertable))]
#[cfg_attr(feature = "full", diesel(table_name = local_user))] #[cfg_attr(feature = "full", diesel(table_name = local_user))]
pub struct LocalUserInsertForm { pub struct LocalUserInsertForm {
#[builder(!default)]
pub person_id: PersonId, pub person_id: PersonId,
#[builder(!default)]
pub password_encrypted: String, pub password_encrypted: String,
#[new(default)]
pub email: Option<String>, pub email: Option<String>,
#[new(default)]
pub show_nsfw: Option<bool>, pub show_nsfw: Option<bool>,
#[new(default)]
pub theme: Option<String>, pub theme: Option<String>,
#[new(default)]
pub default_sort_type: Option<SortType>, pub default_sort_type: Option<SortType>,
#[new(default)]
pub default_listing_type: Option<ListingType>, pub default_listing_type: Option<ListingType>,
#[new(default)]
pub interface_language: Option<String>, pub interface_language: Option<String>,
#[new(default)]
pub show_avatars: Option<bool>, pub show_avatars: Option<bool>,
#[new(default)]
pub send_notifications_to_email: Option<bool>, pub send_notifications_to_email: Option<bool>,
#[new(default)]
pub show_bot_accounts: Option<bool>, pub show_bot_accounts: Option<bool>,
#[new(default)]
pub show_scores: Option<bool>, pub show_scores: Option<bool>,
#[new(default)]
pub show_read_posts: Option<bool>, pub show_read_posts: Option<bool>,
#[new(default)]
pub email_verified: Option<bool>, pub email_verified: Option<bool>,
#[new(default)]
pub accepted_application: Option<bool>, pub accepted_application: Option<bool>,
#[new(default)]
pub totp_2fa_secret: Option<Option<String>>, pub totp_2fa_secret: Option<Option<String>>,
#[new(default)]
pub open_links_in_new_tab: Option<bool>, pub open_links_in_new_tab: Option<bool>,
#[new(default)]
pub blur_nsfw: Option<bool>, pub blur_nsfw: Option<bool>,
#[new(default)]
pub auto_expand: Option<bool>, pub auto_expand: Option<bool>,
#[new(default)]
pub infinite_scroll_enabled: Option<bool>, pub infinite_scroll_enabled: Option<bool>,
#[new(default)]
pub admin: Option<bool>, pub admin: Option<bool>,
#[new(default)]
pub post_listing_mode: Option<PostListingMode>, pub post_listing_mode: Option<PostListingMode>,
#[new(default)]
pub totp_2fa_enabled: Option<bool>, pub totp_2fa_enabled: Option<bool>,
#[new(default)]
pub enable_keyboard_navigation: Option<bool>, pub enable_keyboard_navigation: Option<bool>,
#[new(default)]
pub enable_animated_images: Option<bool>, pub enable_animated_images: Option<bool>,
#[new(default)]
pub collapse_bot_comments: Option<bool>, pub collapse_bot_comments: Option<bool>,
} }

View File

@ -301,10 +301,7 @@ mod tests {
let inserted_timmy = Person::create(pool, &new_person).await.unwrap(); let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
let new_local_user = LocalUserInsertForm::builder() let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id);
.person_id(inserted_timmy.id)
.password_encrypted("123".to_string())
.build();
let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]) let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![])
.await .await
.unwrap(); .unwrap();

View File

@ -490,11 +490,8 @@ mod tests {
let timmy_person_form = PersonInsertForm::test_form(inserted_instance.id, "timmy"); let timmy_person_form = PersonInsertForm::test_form(inserted_instance.id, "timmy");
let inserted_timmy_person = Person::create(pool, &timmy_person_form).await?; let inserted_timmy_person = Person::create(pool, &timmy_person_form).await?;
let timmy_local_user_form = LocalUserInsertForm::builder() let timmy_local_user_form = LocalUserInsertForm::test_form_admin(inserted_timmy_person.id);
.person_id(inserted_timmy_person.id)
.admin(Some(true))
.password_encrypted(String::new())
.build();
let inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]).await?; 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"); let sara_person_form = PersonInsertForm::test_form(inserted_instance.id, "sara");

View File

@ -323,10 +323,7 @@ mod tests {
let inserted_timmy = Person::create(pool, &new_person).await.unwrap(); let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
let new_local_user = LocalUserInsertForm::builder() let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id);
.person_id(inserted_timmy.id)
.password_encrypted("123".to_string())
.build();
let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]) let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![])
.await .await
.unwrap(); .unwrap();

View File

@ -167,11 +167,7 @@ mod tests {
let inserted_timmy_person = Person::create(pool, &timmy_person_form).await.unwrap(); let inserted_timmy_person = Person::create(pool, &timmy_person_form).await.unwrap();
let timmy_local_user_form = LocalUserInsertForm::builder() let timmy_local_user_form = LocalUserInsertForm::test_form_admin(inserted_timmy_person.id);
.person_id(inserted_timmy_person.id)
.password_encrypted("nada".to_string())
.admin(Some(true))
.build();
let _inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]) let _inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![])
.await .await
@ -181,10 +177,7 @@ mod tests {
let inserted_sara_person = Person::create(pool, &sara_person_form).await.unwrap(); let inserted_sara_person = Person::create(pool, &sara_person_form).await.unwrap();
let sara_local_user_form = LocalUserInsertForm::builder() let sara_local_user_form = LocalUserInsertForm::test_form(inserted_sara_person.id);
.person_id(inserted_sara_person.id)
.password_encrypted("nada".to_string())
.build();
let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form, vec![]) let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form, vec![])
.await .await
@ -209,10 +202,7 @@ mod tests {
let inserted_jess_person = Person::create(pool, &jess_person_form).await.unwrap(); let inserted_jess_person = Person::create(pool, &jess_person_form).await.unwrap();
let jess_local_user_form = LocalUserInsertForm::builder() let jess_local_user_form = LocalUserInsertForm::test_form(inserted_jess_person.id);
.person_id(inserted_jess_person.id)
.password_encrypted("nada".to_string())
.build();
let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form, vec![]) let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form, vec![])
.await .await

View File

@ -288,10 +288,7 @@ mod tests {
let inserted_person = Person::create(pool, &new_person).await.unwrap(); let inserted_person = Person::create(pool, &new_person).await.unwrap();
let local_user_form = LocalUserInsertForm::builder() let local_user_form = LocalUserInsertForm::test_form(inserted_person.id);
.person_id(inserted_person.id)
.password_encrypted(String::new())
.build();
let local_user = LocalUser::create(pool, &local_user_form, vec![]) let local_user = LocalUser::create(pool, &local_user_form, vec![])
.await .await
.unwrap(); .unwrap();

View File

@ -196,10 +196,7 @@ mod tests {
..PersonInsertForm::test_form(inserted_instance.id, "alice") ..PersonInsertForm::test_form(inserted_instance.id, "alice")
}; };
let alice = Person::create(pool, &alice_form).await?; let alice = Person::create(pool, &alice_form).await?;
let alice_local_user_form = LocalUserInsertForm::builder() let alice_local_user_form = LocalUserInsertForm::test_form(alice.id);
.person_id(alice.id)
.password_encrypted(String::new())
.build();
let alice_local_user = LocalUser::create(pool, &alice_local_user_form, vec![]).await?; let alice_local_user = LocalUser::create(pool, &alice_local_user_form, vec![]).await?;
let bob_form = PersonInsertForm { let bob_form = PersonInsertForm {
@ -208,10 +205,7 @@ mod tests {
..PersonInsertForm::test_form(inserted_instance.id, "bob") ..PersonInsertForm::test_form(inserted_instance.id, "bob")
}; };
let bob = Person::create(pool, &bob_form).await?; let bob = Person::create(pool, &bob_form).await?;
let bob_local_user_form = LocalUserInsertForm::builder() let bob_local_user_form = LocalUserInsertForm::test_form(bob.id);
.person_id(bob.id)
.password_encrypted(String::new())
.build();
let bob_local_user = LocalUser::create(pool, &bob_local_user_form, vec![]).await?; let bob_local_user = LocalUser::create(pool, &bob_local_user_form, vec![]).await?;
Ok(Data { Ok(Data {

View File

@ -38,6 +38,8 @@ pub enum LemmyErrorType {
NotTopAdmin, NotTopAdmin,
NotTopMod, NotTopMod,
NotLoggedIn, NotLoggedIn,
NotHigherMod,
NotHigherAdmin,
SiteBan, SiteBan,
Deleted, Deleted,
BannedFromCommunity, BannedFromCommunity,

View File

@ -468,12 +468,11 @@ async fn initialize_local_site_2022_10_10(
}; };
let person_inserted = Person::create(pool, &person_form).await?; let person_inserted = Person::create(pool, &person_form).await?;
let local_user_form = LocalUserInsertForm::builder() let local_user_form = LocalUserInsertForm {
.person_id(person_inserted.id) email: setup.admin_email.clone(),
.password_encrypted(setup.admin_password.clone()) admin: Some(true),
.email(setup.admin_email.clone()) ..LocalUserInsertForm::new(person_inserted.id, setup.admin_password.clone())
.admin(Some(true)) };
.build();
LocalUser::create(pool, &local_user_form, vec![]).await?; LocalUser::create(pool, &local_user_form, vec![]).await?;
}; };

View File

@ -146,10 +146,7 @@ mod tests {
let inserted_person = Person::create(pool, &new_person).await.unwrap(); let inserted_person = Person::create(pool, &new_person).await.unwrap();
let local_user_form = LocalUserInsertForm::builder() let local_user_form = LocalUserInsertForm::test_form(inserted_person.id);
.person_id(inserted_person.id)
.password_encrypted("123456".to_string())
.build();
let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![]) let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![])
.await .await