Removing a few more Result<bool> . (#4977)

* Removing a few more Result<bool> .

* Running taplo fmt.

* Running fmt.

* Adding email taken test.

* Fixing tests.

* Adding back in missing admin check.

* Rename check_has_local_followers function.
This commit is contained in:
Dessalines 2024-09-23 20:55:35 -04:00 committed by GitHub
parent 62e1790ae7
commit d476d32200
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 182 additions and 220 deletions

View file

@ -21,16 +21,16 @@
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/node": "^22.0.2", "@types/node": "^22.3.0",
"@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.0.0", "@typescript-eslint/parser": "^8.1.0",
"eslint": "^9.8.0", "eslint": "^9.9.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"jest": "^29.5.0", "jest": "^29.5.0",
"lemmy-js-client": "0.20.0-alpha.11", "lemmy-js-client": "0.20.0-alpha.11",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"ts-jest": "^29.1.0", "ts-jest": "^29.1.0",
"typescript": "^5.5.4", "typescript": "^5.5.4",
"typescript-eslint": "^8.0.0" "typescript-eslint": "^8.1.0"
} }
} }

View file

@ -12,16 +12,16 @@ importers:
specifier: ^29.5.12 specifier: ^29.5.12
version: 29.5.12 version: 29.5.12
'@types/node': '@types/node':
specifier: ^22.0.2 specifier: ^22.3.0
version: 22.3.0 version: 22.3.0
'@typescript-eslint/eslint-plugin': '@typescript-eslint/eslint-plugin':
specifier: ^8.0.0 specifier: ^8.1.0
version: 8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4) version: 8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^8.0.0 specifier: ^8.1.0
version: 8.1.0(eslint@9.9.0)(typescript@5.5.4) version: 8.1.0(eslint@9.9.0)(typescript@5.5.4)
eslint: eslint:
specifier: ^9.8.0 specifier: ^9.9.0
version: 9.9.0 version: 9.9.0
eslint-plugin-prettier: eslint-plugin-prettier:
specifier: ^5.1.3 specifier: ^5.1.3
@ -42,7 +42,7 @@ importers:
specifier: ^5.5.4 specifier: ^5.5.4
version: 5.5.4 version: 5.5.4
typescript-eslint: typescript-eslint:
specifier: ^8.0.0 specifier: ^8.1.0
version: 8.1.0(eslint@9.9.0)(typescript@5.5.4) version: 8.1.0(eslint@9.9.0)(typescript@5.5.4)
packages: packages:

View file

@ -628,7 +628,7 @@ test("Enforce community ban for federated user", async () => {
// Alpha tries to make post on beta, but it fails because of ban // Alpha tries to make post on beta, but it fails because of ban
await expect( await expect(
createPost(alpha, betaCommunity.community.id), createPost(alpha, betaCommunity.community.id),
).rejects.toStrictEqual(Error("banned_from_community")); ).rejects.toStrictEqual(Error("person_is_banned_from_community"));
// Unban alpha // Unban alpha
let unBanAlpha = await banPersonFromCommunity( let unBanAlpha = await banPersonFromCommunity(

View file

@ -52,15 +52,12 @@ pub async fn add_mod_to_community(
// moderator. This is necessary because otherwise the action would be rejected // moderator. This is necessary because otherwise the action would be rejected
// by the community's home instance. // by the community's home instance.
if local_user_view.local_user.admin && !community.local { if local_user_view.local_user.admin && !community.local {
let is_mod = CommunityModeratorView::is_community_moderator( CommunityModeratorView::check_is_community_moderator(
&mut context.pool(), &mut context.pool(),
community.id, community.id,
local_user_view.person.id, local_user_view.person.id,
) )
.await?; .await?;
if !is_mod {
Err(LemmyErrorType::NotAModerator)?
}
} }
// Update in local database // Update in local database

View file

@ -63,9 +63,7 @@ pub async fn save_user_settings(
let previous_email = local_user_view.local_user.email.clone().unwrap_or_default(); let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
// if email was changed, check that it is not taken and send verification mail // if email was changed, check that it is not taken and send verification mail
if previous_email.deref() != email { if previous_email.deref() != email {
if LocalUser::is_email_taken(&mut context.pool(), email).await? { LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
return Err(LemmyErrorType::EmailAlreadyExists)?;
}
send_verification_email( send_verification_email(
&local_user_view, &local_user_view,
email, email,

View file

@ -29,13 +29,9 @@ impl Claims {
let claims = let claims =
decode::<Claims>(jwt, &key, &validation).with_lemmy_type(LemmyErrorType::NotLoggedIn)?; decode::<Claims>(jwt, &key, &validation).with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let user_id = LocalUserId(claims.claims.sub.parse()?); let user_id = LocalUserId(claims.claims.sub.parse()?);
let is_valid = LoginToken::validate(&mut context.pool(), user_id, jwt).await?; LoginToken::validate(&mut context.pool(), user_id, jwt).await?;
if !is_valid {
Err(LemmyErrorType::NotLoggedIn)?
} else {
Ok(user_id) Ok(user_id)
} }
}
pub async fn generate( pub async fn generate(
user_id: LocalUserId, user_id: LocalUserId,

View file

@ -73,13 +73,7 @@ pub async fn is_mod_or_admin(
community_id: CommunityId, community_id: CommunityId,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
check_user_valid(person)?; check_user_valid(person)?;
CommunityView::check_is_mod_or_admin(pool, person.id, community_id).await
let is_mod_or_admin = CommunityView::is_mod_or_admin(pool, person.id, community_id).await?;
if !is_mod_or_admin {
Err(LemmyErrorType::NotAModOrAdmin)?
} else {
Ok(())
}
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
@ -110,13 +104,7 @@ pub async fn check_community_mod_of_any_or_admin_action(
let person = &local_user_view.person; let person = &local_user_view.person;
check_user_valid(person)?; check_user_valid(person)?;
CommunityView::check_is_mod_of_any_or_admin(pool, person.id).await
let is_mod_of_any_or_admin = CommunityView::is_mod_of_any_or_admin(pool, person.id).await?;
if !is_mod_of_any_or_admin {
Err(LemmyErrorType::NotAModOrAdmin)?
} else {
Ok(())
}
} }
pub fn is_admin(local_user_view: &LocalUserView) -> LemmyResult<()> { pub fn is_admin(local_user_view: &LocalUserView) -> LemmyResult<()> {
@ -242,7 +230,7 @@ pub async fn check_community_user_action(
) -> LemmyResult<()> { ) -> LemmyResult<()> {
check_user_valid(person)?; check_user_valid(person)?;
check_community_deleted_removed(community_id, pool).await?; check_community_deleted_removed(community_id, pool).await?;
check_community_ban(person, community_id, pool).await?; CommunityPersonBanView::check(pool, person.id, community_id).await?;
Ok(()) Ok(())
} }
@ -257,19 +245,6 @@ async fn check_community_deleted_removed(
Ok(()) Ok(())
} }
async fn check_community_ban(
person: &Person,
community_id: CommunityId,
pool: &mut DbPool<'_>,
) -> LemmyResult<()> {
// check if user was banned from site or community
let is_banned = CommunityPersonBanView::get(pool, person.id, community_id).await?;
if is_banned {
Err(LemmyErrorType::BannedFromCommunity)?
}
Ok(())
}
/// Check that the given user can perform a mod action in the community. /// Check that the given user can perform a mod action in the community.
/// ///
/// In particular it checks that he is an admin or mod, wasn't banned and the community isn't /// In particular it checks that he is an admin or mod, wasn't banned and the community isn't
@ -281,7 +256,7 @@ pub async fn check_community_mod_action(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
is_mod_or_admin(pool, person, community_id).await?; is_mod_or_admin(pool, person, community_id).await?;
check_community_ban(person, community_id, pool).await?; CommunityPersonBanView::check(pool, person.id, community_id).await?;
// it must be possible to restore deleted community // it must be possible to restore deleted community
if !allow_deleted { if !allow_deleted {
@ -307,51 +282,6 @@ pub fn check_comment_deleted_or_removed(comment: &Comment) -> LemmyResult<()> {
} }
} }
/// Throws an error if a recipient has blocked a person.
#[tracing::instrument(skip_all)]
pub async fn check_person_block(
my_id: PersonId,
potential_blocker_id: PersonId,
pool: &mut DbPool<'_>,
) -> LemmyResult<()> {
let is_blocked = PersonBlock::read(pool, potential_blocker_id, my_id).await?;
if is_blocked {
Err(LemmyErrorType::PersonIsBlocked)?
} else {
Ok(())
}
}
/// Throws an error if a recipient has blocked a community.
#[tracing::instrument(skip_all)]
async fn check_community_block(
community_id: CommunityId,
person_id: PersonId,
pool: &mut DbPool<'_>,
) -> LemmyResult<()> {
let is_blocked = CommunityBlock::read(pool, person_id, community_id).await?;
if is_blocked {
Err(LemmyErrorType::CommunityIsBlocked)?
} else {
Ok(())
}
}
/// Throws an error if a recipient has blocked an instance.
#[tracing::instrument(skip_all)]
async fn check_instance_block(
instance_id: InstanceId,
person_id: PersonId,
pool: &mut DbPool<'_>,
) -> LemmyResult<()> {
let is_blocked = InstanceBlock::read(pool, person_id, instance_id).await?;
if is_blocked {
Err(LemmyErrorType::InstanceIsBlocked)?
} else {
Ok(())
}
}
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn check_person_instance_community_block( pub async fn check_person_instance_community_block(
my_id: PersonId, my_id: PersonId,
@ -360,9 +290,9 @@ pub async fn check_person_instance_community_block(
community_id: CommunityId, community_id: CommunityId,
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
check_person_block(my_id, potential_blocker_id, pool).await?; PersonBlock::read(pool, potential_blocker_id, my_id).await?;
check_instance_block(community_instance_id, potential_blocker_id, pool).await?; InstanceBlock::read(pool, potential_blocker_id, community_instance_id).await?;
check_community_block(community_id, potential_blocker_id, pool).await?; CommunityBlock::read(pool, potential_blocker_id, community_id).await?;
Ok(()) Ok(())
} }

View file

@ -95,15 +95,12 @@ pub async fn create_post(
let community = Community::read(&mut context.pool(), community_id).await?; let community = Community::read(&mut context.pool(), community_id).await?;
if community.posting_restricted_to_mods { if community.posting_restricted_to_mods {
let community_id = data.community_id; let community_id = data.community_id;
let is_mod = CommunityModeratorView::is_community_moderator( CommunityModeratorView::check_is_community_moderator(
&mut context.pool(), &mut context.pool(),
community_id, community_id,
local_user_view.local_user.person_id, local_user_view.local_user.person_id,
) )
.await?; .await?;
if !is_mod {
Err(LemmyErrorType::OnlyModsCanPostInCommunity)?
}
} }
// Only need to check if language is allowed in case user set it explicitly. When using default // Only need to check if language is allowed in case user set it explicitly. When using default

View file

@ -5,7 +5,6 @@ use lemmy_api_common::{
private_message::{CreatePrivateMessage, PrivateMessageResponse}, private_message::{CreatePrivateMessage, PrivateMessageResponse},
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
utils::{ utils::{
check_person_block,
get_interface_language, get_interface_language,
get_url_blocklist, get_url_blocklist,
local_site_to_slur_regex, local_site_to_slur_regex,
@ -16,6 +15,7 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
local_site::LocalSite, local_site::LocalSite,
person_block::PersonBlock,
private_message::{PrivateMessage, PrivateMessageInsertForm}, private_message::{PrivateMessage, PrivateMessageInsertForm},
}, },
traits::Crud, traits::Crud,
@ -39,10 +39,10 @@ pub async fn create_private_message(
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?; let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
is_valid_body_field(&content, false)?; is_valid_body_field(&content, false)?;
check_person_block( PersonBlock::read(
local_user_view.person.id,
data.recipient_id,
&mut context.pool(), &mut context.pool(),
data.recipient_id,
local_user_view.person.id,
) )
.await?; .await?;

View file

@ -92,9 +92,8 @@ pub async fn register(
} }
if local_site.site_setup && local_site.captcha_enabled { if local_site.site_setup && local_site.captcha_enabled {
if let Some(captcha_uuid) = &data.captcha_uuid { let uuid = uuid::Uuid::parse_str(&data.captcha_uuid.clone().unwrap_or_default())?;
let uuid = uuid::Uuid::parse_str(captcha_uuid)?; CaptchaAnswer::check_captcha(
let check = CaptchaAnswer::check_captcha(
&mut context.pool(), &mut context.pool(),
CheckCaptchaAnswer { CheckCaptchaAnswer {
uuid, uuid,
@ -102,12 +101,6 @@ pub async fn register(
}, },
) )
.await?; .await?;
if !check {
Err(LemmyErrorType::CaptchaIncorrect)?
}
} else {
Err(LemmyErrorType::CaptchaIncorrect)?
}
} }
let slur_regex = local_site_to_slur_regex(&local_site); let slur_regex = local_site_to_slur_regex(&local_site);
@ -119,9 +112,7 @@ pub async fn register(
} }
if let Some(email) = &data.email { if let Some(email) = &data.email {
if LocalUser::is_email_taken(&mut context.pool(), email).await? { LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
Err(LemmyErrorType::EmailAlreadyExists)?
}
} }
// We have to create both a person, and local_user // We have to create both a person, and local_user

View file

@ -213,15 +213,13 @@ async fn can_accept_activity_in_community(
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
if let Some(community) = community { if let Some(community) = community {
if !community.local
&& !CommunityFollower::has_local_followers(&mut context.pool(), community.id).await?
{
Err(LemmyErrorType::CommunityHasNoFollowers)?
}
// Local only community can't federate // Local only community can't federate
if community.visibility != CommunityVisibility::Public { if community.visibility != CommunityVisibility::Public {
return Err(LemmyErrorType::NotFound.into()); return Err(LemmyErrorType::NotFound.into());
} }
if !community.local {
CommunityFollower::check_has_local_followers(&mut context.pool(), community.id).await?
}
} }
Ok(()) Ok(())
} }

View file

@ -87,12 +87,7 @@ pub(crate) async fn verify_person_in_community(
} }
let person_id = person.id; let person_id = person.id;
let community_id = community.id; let community_id = community.id;
let is_banned = CommunityPersonBanView::get(&mut context.pool(), person_id, community_id).await?; CommunityPersonBanView::check(&mut context.pool(), person_id, community_id).await
if is_banned {
Err(LemmyErrorType::PersonIsBannedFromCommunity)?
} else {
Ok(())
}
} }
/// Verify that mod action in community was performed by a moderator. /// Verify that mod action in community was performed by a moderator.
@ -106,14 +101,6 @@ pub(crate) async fn verify_mod_action(
community: &Community, community: &Community,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let mod_ = mod_id.dereference(context).await?;
let is_mod_or_admin =
CommunityView::is_mod_or_admin(&mut context.pool(), mod_.id, community.id).await?;
if is_mod_or_admin {
return Ok(());
}
// mod action comes from the same instance as the community, so it was presumably done // mod action comes from the same instance as the community, so it was presumably done
// by an instance admin. // by an instance admin.
// TODO: federate instance admin status and check it here // TODO: federate instance admin status and check it here
@ -121,7 +108,8 @@ pub(crate) async fn verify_mod_action(
return Ok(()); return Ok(());
} }
Err(LemmyErrorType::NotAModerator)? let mod_ = mod_id.dereference(context).await?;
CommunityView::check_is_mod_or_admin(&mut context.pool(), mod_.id, community.id).await
} }
pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> LemmyResult<()> { pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> LemmyResult<()> {

View file

@ -39,7 +39,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyErrorType, LemmyResult}, error::{LemmyError, LemmyResult},
spawn_try_task, spawn_try_task,
utils::{ utils::{
markdown::markdown_to_html, markdown::markdown_to_html,
@ -180,15 +180,12 @@ impl Object for ApubPost {
let creator = page.creator()?.dereference(context).await?; let creator = page.creator()?.dereference(context).await?;
let community = page.community(context).await?; let community = page.community(context).await?;
if community.posting_restricted_to_mods { if community.posting_restricted_to_mods {
let is_mod = CommunityModeratorView::is_community_moderator( CommunityModeratorView::check_is_community_moderator(
&mut context.pool(), &mut context.pool(),
community.id, community.id,
creator.id, creator.id,
) )
.await?; .await?;
if !is_mod {
Err(LemmyErrorType::OnlyModsCanPostInCommunity)?
}
} }
let mut name = page let mut name = page
.name .name

View file

@ -15,12 +15,13 @@ use activitypub_federation::{
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
utils::{check_person_block, get_url_blocklist, local_site_opt_to_slur_regex, process_markdown}, utils::{get_url_blocklist, local_site_opt_to_slur_regex, process_markdown},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
local_site::LocalSite, local_site::LocalSite,
person::Person, person::Person,
person_block::PersonBlock,
private_message::{PrivateMessage, PrivateMessageInsertForm}, private_message::{PrivateMessage, PrivateMessageInsertForm},
}, },
traits::Crud, traits::Crud,
@ -126,7 +127,7 @@ impl Object for ApubPrivateMessage {
) -> LemmyResult<ApubPrivateMessage> { ) -> LemmyResult<ApubPrivateMessage> {
let creator = note.attributed_to.dereference(context).await?; let creator = note.attributed_to.dereference(context).await?;
let recipient = note.to[0].dereference(context).await?; let recipient = note.to[0].dereference(context).await?;
check_person_block(creator.id, recipient.id, &mut context.pool()).await?; PersonBlock::read(&mut context.pool(), recipient.id, creator.id).await?;
let local_site = LocalSite::read(&mut context.pool()).await.ok(); let local_site = LocalSite::read(&mut context.pool()).await.ok();
let slur_regex = &local_site_opt_to_slur_regex(&local_site); let slur_regex = &local_site_opt_to_slur_regex(&local_site);

View file

@ -13,6 +13,7 @@ use diesel::{
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
impl CaptchaAnswer { impl CaptchaAnswer {
pub async fn insert(pool: &mut DbPool<'_>, captcha: &CaptchaAnswerForm) -> Result<Self, Error> { pub async fn insert(pool: &mut DbPool<'_>, captcha: &CaptchaAnswerForm) -> Result<Self, Error> {
@ -27,7 +28,7 @@ impl CaptchaAnswer {
pub async fn check_captcha( pub async fn check_captcha(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
to_check: CheckCaptchaAnswer, to_check: CheckCaptchaAnswer,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
// fetch requested captcha // fetch requested captcha
@ -43,7 +44,9 @@ impl CaptchaAnswer {
.execute(conn) .execute(conn)
.await?; .await?;
Ok(captcha_exists) captcha_exists
.then_some(())
.ok_or(LemmyErrorType::CaptchaIncorrect.into())
} }
} }
@ -83,7 +86,6 @@ mod tests {
.await; .await;
assert!(result.is_ok()); assert!(result.is_ok());
assert!(result.unwrap());
} }
#[tokio::test] #[tokio::test]
@ -119,7 +121,6 @@ mod tests {
) )
.await; .await;
assert!(result_repeat.is_ok()); assert!(result_repeat.is_err());
assert!(!result_repeat.unwrap());
} }
} }

View file

@ -35,8 +35,7 @@ use crate::{
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use diesel::{ use diesel::{
deserialize, deserialize,
dsl, dsl::{self, exists, insert_into},
dsl::{exists, insert_into},
pg::Pg, pg::Pg,
result::Error, result::Error,
select, select,
@ -320,16 +319,18 @@ impl CommunityFollower {
/// Check if a remote instance has any followers on local instance. For this it is enough to check /// Check if a remote instance has any followers on local instance. For this it is enough to check
/// if any follow relation is stored. Dont use this for local community. /// if any follow relation is stored. Dont use this for local community.
pub async fn has_local_followers( pub async fn check_has_local_followers(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
remote_community_id: CommunityId, remote_community_id: CommunityId,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists(community_follower::table.filter( select(exists(community_follower::table.filter(
community_follower::community_id.eq(remote_community_id), community_follower::community_id.eq(remote_community_id),
))) )))
.get_result(conn) .get_result::<bool>(conn)
.await .await?
.then_some(())
.ok_or(LemmyErrorType::CommunityHasNoFollowers.into())
} }
} }

View file

@ -9,26 +9,29 @@ use crate::{
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
use diesel::{ use diesel::{
dsl::{exists, insert_into}, dsl::{exists, insert_into, not},
result::Error, result::Error,
select, select,
ExpressionMethods, ExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
impl CommunityBlock { impl CommunityBlock {
pub async fn read( pub async fn read(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_person_id: PersonId, for_person_id: PersonId,
for_community_id: CommunityId, for_community_id: CommunityId,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(not(exists(
community_block::table.find((for_person_id, for_community_id)), community_block::table.find((for_person_id, for_community_id)),
)) )))
.get_result(conn) .get_result::<bool>(conn)
.await .await?
.then_some(())
.ok_or(LemmyErrorType::CommunityIsBlocked.into())
} }
pub async fn for_person( pub async fn for_person(

View file

@ -9,26 +9,29 @@ use crate::{
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
use diesel::{ use diesel::{
dsl::{exists, insert_into}, dsl::{exists, insert_into, not},
result::Error, result::Error,
select, select,
ExpressionMethods, ExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
impl InstanceBlock { impl InstanceBlock {
pub async fn read( pub async fn read(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_person_id: PersonId, for_person_id: PersonId,
for_instance_id: InstanceId, for_instance_id: InstanceId,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(not(exists(
instance_block::table.find((for_person_id, for_instance_id)), instance_block::table.find((for_person_id, for_instance_id)),
)) )))
.get_result(conn) .get_result::<bool>(conn)
.await .await?
.then_some(())
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
} }
pub async fn for_person( pub async fn for_person(

View file

@ -136,14 +136,16 @@ impl LocalUser {
diesel::delete(persons).execute(conn).await diesel::delete(persons).execute(conn).await
} }
pub async fn is_email_taken(pool: &mut DbPool<'_>, email: &str) -> Result<bool, Error> { pub async fn check_is_email_taken(pool: &mut DbPool<'_>, email: &str) -> LemmyResult<()> {
use diesel::dsl::{exists, select}; use diesel::dsl::{exists, select};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists(local_user::table.filter( select(not(exists(local_user::table.filter(
lower(coalesce(local_user::email, "")).eq(email.to_lowercase()), lower(coalesce(local_user::email, "")).eq(email.to_lowercase()),
))) ))))
.get_result(conn) .get_result::<bool>(conn)
.await .await?
.then_some(())
.ok_or(LemmyErrorType::EmailAlreadyExists.into())
} }
// TODO: maybe move this and pass in LocalUserView // TODO: maybe move this and pass in LocalUserView
@ -419,4 +421,32 @@ mod tests {
Ok(()) Ok(())
} }
#[tokio::test]
#[serial]
async fn test_email_taken() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into();
let darwin_email = "charles.darwin@gmail.com";
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
let darwin_person = PersonInsertForm::test_form(inserted_instance.id, "darwin");
let inserted_darwin_person = Person::create(pool, &darwin_person).await?;
let mut darwin_local_user_form =
LocalUserInsertForm::test_form_admin(inserted_darwin_person.id);
darwin_local_user_form.email = Some(darwin_email.into());
let _inserted_darwin_local_user =
LocalUser::create(pool, &darwin_local_user_form, vec![]).await?;
let check = LocalUser::check_is_email_taken(pool, darwin_email).await;
assert!(check.is_err());
let passed_check = LocalUser::check_is_email_taken(pool, "not_charles@gmail.com").await;
assert!(passed_check.is_ok());
Ok(())
}
} }

View file

@ -7,6 +7,7 @@ use crate::{
}; };
use diesel::{delete, dsl::exists, insert_into, result::Error, select}; use diesel::{delete, dsl::exists, insert_into, result::Error, select};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
impl LoginToken { impl LoginToken {
pub async fn create(pool: &mut DbPool<'_>, form: LoginTokenCreateForm) -> Result<Self, Error> { pub async fn create(pool: &mut DbPool<'_>, form: LoginTokenCreateForm) -> Result<Self, Error> {
@ -22,13 +23,15 @@ impl LoginToken {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
user_id_: LocalUserId, user_id_: LocalUserId,
token_: &str, token_: &str,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(exists(
login_token.find(token_).filter(user_id.eq(user_id_)), login_token.find(token_).filter(user_id.eq(user_id_)),
)) ))
.get_result(conn) .get_result::<bool>(conn)
.await .await?
.then_some(())
.ok_or(LemmyErrorType::NotLoggedIn.into())
} }
pub async fn list( pub async fn list(

View file

@ -9,7 +9,7 @@ use crate::{
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
use diesel::{ use diesel::{
dsl::{exists, insert_into}, dsl::{exists, insert_into, not},
result::Error, result::Error,
select, select,
ExpressionMethods, ExpressionMethods,
@ -17,19 +17,22 @@ use diesel::{
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
impl PersonBlock { impl PersonBlock {
pub async fn read( pub async fn read(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_person_id: PersonId, for_person_id: PersonId,
for_recipient_id: PersonId, for_recipient_id: PersonId,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(not(exists(
person_block::table.find((for_person_id, for_recipient_id)), person_block::table.find((for_person_id, for_recipient_id)),
)) )))
.get_result(conn) .get_result::<bool>(conn)
.await .await?
.then_some(())
.ok_or(LemmyErrorType::PersonIsBlocked.into())
} }
pub async fn for_person( pub async fn for_person(

View file

@ -15,7 +15,13 @@ doctest = false
workspace = true workspace = true
[features] [features]
full = ["lemmy_db_schema/full", "diesel", "diesel-async", "ts-rs"] full = [
"lemmy_db_schema/full",
"lemmy_utils/full",
"diesel",
"diesel-async",
"ts-rs",
]
[dependencies] [dependencies]
lemmy_db_schema = { workspace = true } lemmy_db_schema = { workspace = true }
@ -33,6 +39,7 @@ serde_with = { workspace = true }
ts-rs = { workspace = true, optional = true } ts-rs = { workspace = true, optional = true }
chrono.workspace = true chrono.workspace = true
strum = { workspace = true } strum = { workspace = true }
lemmy_utils = { workspace = true, optional = true }
[dev-dependencies] [dev-dependencies]
serial_test = { workspace = true } serial_test = { workspace = true }

View file

@ -8,13 +8,14 @@ use lemmy_db_schema::{
source::local_user::LocalUser, source::local_user::LocalUser,
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
impl CommunityModeratorView { impl CommunityModeratorView {
pub async fn is_community_moderator( pub async fn check_is_community_moderator(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
find_community_id: CommunityId, find_community_id: CommunityId,
find_person_id: PersonId, find_person_id: PersonId,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
use lemmy_db_schema::schema::community_moderator::dsl::{ use lemmy_db_schema::schema::community_moderator::dsl::{
community_id, community_id,
community_moderator, community_moderator,
@ -27,20 +28,24 @@ impl CommunityModeratorView {
.filter(person_id.eq(find_person_id)), .filter(person_id.eq(find_person_id)),
)) ))
.get_result::<bool>(conn) .get_result::<bool>(conn)
.await .await?
.then_some(())
.ok_or(LemmyErrorType::NotAModerator.into())
} }
pub(crate) async fn is_community_moderator_of_any( pub(crate) async fn is_community_moderator_of_any(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
find_person_id: PersonId, find_person_id: PersonId,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
use lemmy_db_schema::schema::community_moderator::dsl::{community_moderator, person_id}; use lemmy_db_schema::schema::community_moderator::dsl::{community_moderator, person_id};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(exists(
community_moderator.filter(person_id.eq(find_person_id)), community_moderator.filter(person_id.eq(find_person_id)),
)) ))
.get_result::<bool>(conn) .get_result::<bool>(conn)
.await .await?
.then_some(())
.ok_or(LemmyErrorType::NotAModerator.into())
} }
pub async fn for_community( pub async fn for_community(

View file

@ -1,25 +1,33 @@
use crate::structs::CommunityPersonBanView; use crate::structs::CommunityPersonBanView;
use diesel::{dsl::exists, result::Error, select, ExpressionMethods, QueryDsl}; use diesel::{
dsl::{exists, not},
select,
ExpressionMethods,
QueryDsl,
};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::{CommunityId, PersonId}, newtypes::{CommunityId, PersonId},
schema::community_person_ban, schema::community_person_ban,
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
impl CommunityPersonBanView { impl CommunityPersonBanView {
pub async fn get( pub async fn check(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
from_person_id: PersonId, from_person_id: PersonId,
from_community_id: CommunityId, from_community_id: CommunityId,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(not(exists(
community_person_ban::table community_person_ban::table
.filter(community_person_ban::community_id.eq(from_community_id)) .filter(community_person_ban::community_id.eq(from_community_id))
.filter(community_person_ban::person_id.eq(from_person_id)), .filter(community_person_ban::person_id.eq(from_person_id)),
)) )))
.get_result::<bool>(conn) .get_result::<bool>(conn)
.await .await?
.then_some(())
.ok_or(LemmyErrorType::PersonIsBannedFromCommunity.into())
} }
} }

View file

@ -26,6 +26,7 @@ use lemmy_db_schema::{
ListingType, ListingType,
PostSortType, PostSortType,
}; };
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
fn queries<'a>() -> Queries< fn queries<'a>() -> Queries<
impl ReadFn<'a, CommunityView, (CommunityId, Option<&'a LocalUser>, bool)>, impl ReadFn<'a, CommunityView, (CommunityId, Option<&'a LocalUser>, bool)>,
@ -185,35 +186,39 @@ impl CommunityView {
.await .await
} }
pub async fn is_mod_or_admin( pub async fn check_is_mod_or_admin(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
person_id: PersonId, person_id: PersonId,
community_id: CommunityId, community_id: CommunityId,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
let is_mod = let is_mod =
CommunityModeratorView::is_community_moderator(pool, community_id, person_id).await?; CommunityModeratorView::check_is_community_moderator(pool, community_id, person_id).await;
if is_mod { if is_mod.is_ok()
Ok(true) || PersonView::read(pool, person_id)
} else if let Ok(person_view) = PersonView::read(pool, person_id).await { .await
Ok(person_view.is_admin) .is_ok_and(|t| t.is_admin)
{
Ok(())
} else { } else {
Ok(false) Err(LemmyErrorType::NotAModOrAdmin)?
} }
} }
/// Checks if a person is an admin, or moderator of any community. /// Checks if a person is an admin, or moderator of any community.
pub async fn is_mod_of_any_or_admin( pub async fn check_is_mod_of_any_or_admin(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
person_id: PersonId, person_id: PersonId,
) -> Result<bool, Error> { ) -> LemmyResult<()> {
let is_mod_of_any = let is_mod_of_any =
CommunityModeratorView::is_community_moderator_of_any(pool, person_id).await?; CommunityModeratorView::is_community_moderator_of_any(pool, person_id).await;
if is_mod_of_any { if is_mod_of_any.is_ok()
Ok(true) || PersonView::read(pool, person_id)
} else if let Ok(person_view) = PersonView::read(pool, person_id).await { .await
Ok(person_view.is_admin) .is_ok_and(|t| t.is_admin)
{
Ok(())
} else { } else {
Ok(false) Err(LemmyErrorType::NotAModOrAdmin)?
} }
} }
} }