From 9e099726e6e36134032b21a568593d9227146a57 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Fri, 13 Oct 2023 15:48:18 +0200 Subject: [PATCH] Cleanup checks for community actions (fixes #2858, fixes #2868) (#4028) * Cleanup checks for community actions (fixes #2858, fixes #2868) * allow restoring deleted community * review changes * remove unneeded sql * remove joins * change mod log check --- crates/api/src/comment/distinguish.rs | 13 ++- crates/api/src/comment/like.rs | 6 +- crates/api/src/comment_report/create.rs | 9 +- crates/api/src/comment_report/list.rs | 3 + crates/api/src/comment_report/resolve.rs | 10 +- crates/api/src/community/add_mod.rs | 10 +- crates/api/src/community/ban.rs | 9 +- crates/api/src/community/follow.rs | 6 +- crates/api/src/community/transfer.rs | 5 +- crates/api/src/local_user/login.rs | 6 +- crates/api/src/local_user/report_count.rs | 3 + crates/api/src/post/feature.rs | 29 +++-- crates/api/src/post/like.rs | 14 +-- crates/api/src/post/lock.rs | 27 ++--- crates/api/src/post_report/create.rs | 9 +- crates/api/src/post_report/list.rs | 3 + crates/api/src/post_report/resolve.rs | 10 +- crates/api/src/site/mod_log.rs | 29 ++--- crates/api_common/src/build_response.rs | 15 ++- crates/api_common/src/utils.rs | 103 +++++++++++++----- crates/api_crud/src/comment/create.rs | 6 +- crates/api_crud/src/comment/delete.rs | 6 +- crates/api_crud/src/comment/remove.rs | 15 +-- crates/api_crud/src/comment/update.rs | 6 +- crates/api_crud/src/community/delete.rs | 10 +- crates/api_crud/src/community/remove.rs | 10 +- crates/api_crud/src/community/update.rs | 19 ++-- crates/api_crud/src/post/create.rs | 10 +- crates/api_crud/src/post/delete.rs | 18 +-- crates/api_crud/src/post/remove.rs | 26 ++--- crates/api_crud/src/post/update.rs | 8 +- .../activities/create_or_update/comment.rs | 2 +- crates/apub/src/activities/mod.rs | 4 +- crates/apub/src/objects/post.rs | 2 +- crates/db_schema/src/impls/person.rs | 9 -- .../src/community_person_ban_view.rs | 22 ++-- crates/routes/src/lib.rs | 6 +- src/session_middleware.rs | 6 +- 38 files changed, 273 insertions(+), 231 deletions(-) diff --git a/crates/api/src/comment/distinguish.rs b/crates/api/src/comment/distinguish.rs index 5059e2db7..f29e01f76 100644 --- a/crates/api/src/comment/distinguish.rs +++ b/crates/api/src/comment/distinguish.rs @@ -2,7 +2,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ comment::{CommentResponse, DistinguishComment}, context::LemmyContext, - utils::{check_community_ban, is_mod_or_admin}, + utils::{check_community_mod_action, check_community_user_action}, }; use lemmy_db_schema::{ source::comment::{Comment, CommentUpdateForm}, @@ -19,18 +19,19 @@ pub async fn distinguish_comment( ) -> Result, LemmyError> { let orig_comment = CommentView::read(&mut context.pool(), data.comment_id, None).await?; - check_community_ban( - local_user_view.person.id, + check_community_user_action( + &local_user_view.person, orig_comment.community.id, &mut context.pool(), ) .await?; // Verify that only a mod or admin can distinguish a comment - is_mod_or_admin( - &mut context.pool(), - local_user_view.person.id, + check_community_mod_action( + &local_user_view.person, orig_comment.community.id, + false, + &mut context.pool(), ) .await?; diff --git a/crates/api/src/comment/like.rs b/crates/api/src/comment/like.rs index ee0a0bd7e..e11a3e155 100644 --- a/crates/api/src/comment/like.rs +++ b/crates/api/src/comment/like.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ comment::{CommentResponse, CreateCommentLike}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, check_downvotes_enabled}, + utils::{check_community_user_action, check_downvotes_enabled}, }; use lemmy_db_schema::{ newtypes::LocalUserId, @@ -36,8 +36,8 @@ pub async fn like_comment( let comment_id = data.comment_id; let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; - check_community_ban( - local_user_view.person.id, + check_community_user_action( + &local_user_view.person, orig_comment.community.id, &mut context.pool(), ) diff --git a/crates/api/src/comment_report/create.rs b/crates/api/src/comment_report/create.rs index e1040fd5e..be892acfe 100644 --- a/crates/api/src/comment_report/create.rs +++ b/crates/api/src/comment_report/create.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ comment::{CommentReportResponse, CreateCommentReport}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, send_new_report_email_to_admins}, + utils::{check_community_user_action, send_new_report_email_to_admins}, }; use lemmy_db_schema::{ source::{ @@ -33,7 +33,12 @@ pub async fn create_comment_report( let comment_id = data.comment_id; let comment_view = CommentView::read(&mut context.pool(), comment_id, None).await?; - check_community_ban(person_id, comment_view.community.id, &mut context.pool()).await?; + check_community_user_action( + &local_user_view.person, + comment_view.community.id, + &mut context.pool(), + ) + .await?; let report_form = CommentReportForm { creator_id: person_id, diff --git a/crates/api/src/comment_report/list.rs b/crates/api/src/comment_report/list.rs index bc8dd2677..3d434deba 100644 --- a/crates/api/src/comment_report/list.rs +++ b/crates/api/src/comment_report/list.rs @@ -2,6 +2,7 @@ use actix_web::web::{Data, Json, Query}; use lemmy_api_common::{ comment::{ListCommentReports, ListCommentReportsResponse}, context::LemmyContext, + utils::check_community_mod_action_opt, }; use lemmy_db_views::{comment_report_view::CommentReportQuery, structs::LocalUserView}; use lemmy_utils::error::LemmyError; @@ -17,6 +18,8 @@ pub async fn list_comment_reports( let community_id = data.community_id; let unresolved_only = data.unresolved_only.unwrap_or_default(); + check_community_mod_action_opt(&local_user_view, community_id, &mut context.pool()).await?; + let page = data.page; let limit = data.limit; let comment_reports = CommentReportQuery { diff --git a/crates/api/src/comment_report/resolve.rs b/crates/api/src/comment_report/resolve.rs index 8296f068b..41ebe0d00 100644 --- a/crates/api/src/comment_report/resolve.rs +++ b/crates/api/src/comment_report/resolve.rs @@ -2,7 +2,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ comment::{CommentReportResponse, ResolveCommentReport}, context::LemmyContext, - utils::is_mod_or_admin, + utils::check_community_mod_action, }; use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable}; use lemmy_db_views::structs::{CommentReportView, LocalUserView}; @@ -20,7 +20,13 @@ pub async fn resolve_comment_report( let report = CommentReportView::read(&mut context.pool(), report_id, person_id).await?; let person_id = local_user_view.person.id; - is_mod_or_admin(&mut context.pool(), person_id, report.community.id).await?; + check_community_mod_action( + &local_user_view.person, + report.community.id, + false, + &mut context.pool(), + ) + .await?; if data.resolved { CommentReport::resolve(&mut context.pool(), report_id, person_id) diff --git a/crates/api/src/community/add_mod.rs b/crates/api/src/community/add_mod.rs index e08686361..9d055c654 100644 --- a/crates/api/src/community/add_mod.rs +++ b/crates/api/src/community/add_mod.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ community::{AddModToCommunity, AddModToCommunityResponse}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::is_mod_or_admin, + utils::check_community_mod_action, }; use lemmy_db_schema::{ source::{ @@ -26,7 +26,13 @@ pub async fn add_mod_to_community( let community_id = data.community_id; // Verify that only mods or admins can add mod - is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id).await?; + check_community_mod_action( + &local_user_view.person, + community_id, + false, + &mut context.pool(), + ) + .await?; let community = Community::read(&mut context.pool(), community_id).await?; if local_user_view.local_user.admin && !community.local { Err(LemmyErrorType::NotAModerator)? diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs index 2cc40cd1d..8e9aedbad 100644 --- a/crates/api/src/community/ban.rs +++ b/crates/api/src/community/ban.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ community::{BanFromCommunity, BanFromCommunityResponse}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{is_mod_or_admin, remove_user_data_in_community}, + utils::{check_community_mod_action, remove_user_data_in_community}, }; use lemmy_db_schema::{ source::{ @@ -36,10 +36,11 @@ pub async fn ban_from_community( let expires = data.expires.map(naive_from_unix); // Verify that only mods or admins can ban - is_mod_or_admin( - &mut context.pool(), - local_user_view.person.id, + check_community_mod_action( + &local_user_view.person, data.community_id, + false, + &mut context.pool(), ) .await?; is_valid_body_field(&data.reason, false)?; diff --git a/crates/api/src/community/follow.rs b/crates/api/src/community/follow.rs index 91ffce714..497aa83cf 100644 --- a/crates/api/src/community/follow.rs +++ b/crates/api/src/community/follow.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ community::{CommunityResponse, FollowCommunity}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, check_community_deleted_or_removed}, + utils::check_community_user_action, }; use lemmy_db_schema::{ source::{ @@ -32,8 +32,8 @@ pub async fn follow_community( if data.follow { if community.local { - check_community_ban(local_user_view.person.id, community.id, &mut context.pool()).await?; - check_community_deleted_or_removed(community.id, &mut context.pool()).await?; + check_community_user_action(&local_user_view.person, community.id, &mut context.pool()) + .await?; CommunityFollower::follow(&mut context.pool(), &community_follower_form) .await diff --git a/crates/api/src/community/transfer.rs b/crates/api/src/community/transfer.rs index fd2e293f8..340bb6b63 100644 --- a/crates/api/src/community/transfer.rs +++ b/crates/api/src/community/transfer.rs @@ -3,7 +3,7 @@ use anyhow::Context; use lemmy_api_common::{ community::{GetCommunityResponse, TransferCommunity}, context::LemmyContext, - utils::{is_admin, is_top_mod}, + utils::{check_community_user_action, is_admin, is_top_mod}, }; use lemmy_db_schema::{ source::{ @@ -27,11 +27,12 @@ pub async fn transfer_community( context: Data, local_user_view: LocalUserView, ) -> Result, LemmyError> { - // Fetch the community mods let community_id = data.community_id; let mut community_mods = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?; + check_community_user_action(&local_user_view.person, community_id, &mut context.pool()).await?; + // Make sure transferrer is either the top community mod, or an admin if !(is_top_mod(&local_user_view, &community_mods).is_ok() || is_admin(&local_user_view).is_ok()) { diff --git a/crates/api/src/local_user/login.rs b/crates/api/src/local_user/login.rs index 981b76a09..f57fd0a70 100644 --- a/crates/api/src/local_user/login.rs +++ b/crates/api/src/local_user/login.rs @@ -44,11 +44,7 @@ pub async fn login( if !valid { Err(LemmyErrorType::IncorrectLogin)? } - check_user_valid( - local_user_view.person.banned, - local_user_view.person.ban_expires, - local_user_view.person.deleted, - )?; + check_user_valid(&local_user_view.person)?; // Check if the user's email is verified if email verification is turned on // However, skip checking verification if the user is an admin diff --git a/crates/api/src/local_user/report_count.rs b/crates/api/src/local_user/report_count.rs index 3bfa1559b..666886432 100644 --- a/crates/api/src/local_user/report_count.rs +++ b/crates/api/src/local_user/report_count.rs @@ -2,6 +2,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, person::{GetReportCount, GetReportCountResponse}, + utils::check_community_mod_action_opt, }; use lemmy_db_views::structs::{ CommentReportView, @@ -21,6 +22,8 @@ pub async fn report_count( let admin = local_user_view.local_user.admin; let community_id = data.community_id; + check_community_mod_action_opt(&local_user_view, community_id, &mut context.pool()).await?; + let comment_reports = CommentReportView::get_report_count(&mut context.pool(), person_id, admin, community_id) .await?; diff --git a/crates/api/src/post/feature.rs b/crates/api/src/post/feature.rs index 8c2d2fced..8c4b4978f 100644 --- a/crates/api/src/post/feature.rs +++ b/crates/api/src/post/feature.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ context::LemmyContext, post::{FeaturePost, PostResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, check_community_deleted_or_removed, is_admin, is_mod_or_admin}, + utils::{check_community_mod_action, is_admin}, }; use lemmy_db_schema::{ source::{ @@ -27,23 +27,15 @@ pub async fn feature_post( let post_id = data.post_id; let orig_post = Post::read(&mut context.pool(), post_id).await?; - check_community_ban( - local_user_view.person.id, + check_community_mod_action( + &local_user_view.person, orig_post.community_id, + false, &mut context.pool(), ) .await?; - check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?; - if data.feature_type == PostFeatureType::Community { - // Verify that only the mods can feature in community - is_mod_or_admin( - &mut context.pool(), - local_user_view.person.id, - orig_post.community_id, - ) - .await?; - } else { + if data.feature_type == PostFeatureType::Local { is_admin(&local_user_view)?; } @@ -72,12 +64,17 @@ pub async fn feature_post( ModFeaturePost::create(&mut context.pool(), &form).await?; - let person_id = local_user_view.person.id; ActivityChannel::submit_activity( - SendActivityData::FeaturePost(post, local_user_view.person, data.featured), + SendActivityData::FeaturePost(post, local_user_view.person.clone(), data.featured), &context, ) .await?; - build_post_response(&context, orig_post.community_id, person_id, post_id).await + build_post_response( + &context, + orig_post.community_id, + &local_user_view.person, + post_id, + ) + .await } diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs index d4f9d644d..751d1b9e5 100644 --- a/crates/api/src/post/like.rs +++ b/crates/api/src/post/like.rs @@ -5,12 +5,7 @@ use lemmy_api_common::{ context::LemmyContext, post::{CreatePostLike, PostResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{ - check_community_ban, - check_community_deleted_or_removed, - check_downvotes_enabled, - mark_post_as_read, - }, + utils::{check_community_user_action, check_downvotes_enabled, mark_post_as_read}, }; use lemmy_db_schema::{ source::{ @@ -39,13 +34,12 @@ pub async fn like_post( let post_id = data.post_id; let post = Post::read(&mut context.pool(), post_id).await?; - check_community_ban( - local_user_view.person.id, + check_community_user_action( + &local_user_view.person, post.community_id, &mut context.pool(), ) .await?; - check_community_deleted_or_removed(post.community_id, &mut context.pool()).await?; let like_form = PostLikeForm { post_id: data.post_id, @@ -83,7 +77,7 @@ pub async fn like_post( build_post_response( context.deref(), post.community_id, - local_user_view.person.id, + &local_user_view.person, post_id, ) .await diff --git a/crates/api/src/post/lock.rs b/crates/api/src/post/lock.rs index ecd206156..b581f37a2 100644 --- a/crates/api/src/post/lock.rs +++ b/crates/api/src/post/lock.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ context::LemmyContext, post::{LockPost, PostResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, check_community_deleted_or_removed, is_mod_or_admin}, + utils::check_community_mod_action, }; use lemmy_db_schema::{ source::{ @@ -26,21 +26,13 @@ pub async fn lock_post( let post_id = data.post_id; let orig_post = Post::read(&mut context.pool(), post_id).await?; - check_community_ban( - local_user_view.person.id, + check_community_mod_action( + &local_user_view.person, orig_post.community_id, + false, &mut context.pool(), ) .await?; - check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?; - - // Verify that only the mods can lock - is_mod_or_admin( - &mut context.pool(), - local_user_view.person.id, - orig_post.community_id, - ) - .await?; // Update the post let post_id = data.post_id; @@ -63,12 +55,17 @@ pub async fn lock_post( }; ModLockPost::create(&mut context.pool(), &form).await?; - let person_id = local_user_view.person.id; ActivityChannel::submit_activity( - SendActivityData::LockPost(post, local_user_view.person, data.locked), + SendActivityData::LockPost(post, local_user_view.person.clone(), data.locked), &context, ) .await?; - build_post_response(&context, orig_post.community_id, person_id, post_id).await + build_post_response( + &context, + orig_post.community_id, + &local_user_view.person, + post_id, + ) + .await } diff --git a/crates/api/src/post_report/create.rs b/crates/api/src/post_report/create.rs index 126475c0c..e4ce2444a 100644 --- a/crates/api/src/post_report/create.rs +++ b/crates/api/src/post_report/create.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ context::LemmyContext, post::{CreatePostReport, PostReportResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, send_new_report_email_to_admins}, + utils::{check_community_user_action, send_new_report_email_to_admins}, }; use lemmy_db_schema::{ source::{ @@ -33,7 +33,12 @@ pub async fn create_post_report( let post_id = data.post_id; let post_view = PostView::read(&mut context.pool(), post_id, None, false).await?; - check_community_ban(person_id, post_view.community.id, &mut context.pool()).await?; + check_community_user_action( + &local_user_view.person, + post_view.community.id, + &mut context.pool(), + ) + .await?; let report_form = PostReportForm { creator_id: person_id, diff --git a/crates/api/src/post_report/list.rs b/crates/api/src/post_report/list.rs index 5cf2889ea..420052e35 100644 --- a/crates/api/src/post_report/list.rs +++ b/crates/api/src/post_report/list.rs @@ -2,6 +2,7 @@ use actix_web::web::{Data, Json, Query}; use lemmy_api_common::{ context::LemmyContext, post::{ListPostReports, ListPostReportsResponse}, + utils::check_community_mod_action_opt, }; use lemmy_db_views::{post_report_view::PostReportQuery, structs::LocalUserView}; use lemmy_utils::error::LemmyError; @@ -17,6 +18,8 @@ pub async fn list_post_reports( let community_id = data.community_id; let unresolved_only = data.unresolved_only.unwrap_or_default(); + check_community_mod_action_opt(&local_user_view, community_id, &mut context.pool()).await?; + let page = data.page; let limit = data.limit; let post_reports = PostReportQuery { diff --git a/crates/api/src/post_report/resolve.rs b/crates/api/src/post_report/resolve.rs index fe01c748c..3604055fd 100644 --- a/crates/api/src/post_report/resolve.rs +++ b/crates/api/src/post_report/resolve.rs @@ -2,7 +2,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, post::{PostReportResponse, ResolvePostReport}, - utils::is_mod_or_admin, + utils::check_community_mod_action, }; use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable}; use lemmy_db_views::structs::{LocalUserView, PostReportView}; @@ -20,7 +20,13 @@ pub async fn resolve_post_report( let report = PostReportView::read(&mut context.pool(), report_id, person_id).await?; let person_id = local_user_view.person.id; - is_mod_or_admin(&mut context.pool(), person_id, report.community.id).await?; + check_community_mod_action( + &local_user_view.person, + report.community.id, + false, + &mut context.pool(), + ) + .await?; if data.resolved { PostReport::resolve(&mut context.pool(), report_id, person_id) diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs index 9c66c72a1..133cce8d8 100644 --- a/crates/api/src/site/mod_log.rs +++ b/crates/api/src/site/mod_log.rs @@ -2,13 +2,9 @@ use actix_web::web::{Data, Json, Query}; use lemmy_api_common::{ context::LemmyContext, site::{GetModlog, GetModlogResponse}, - utils::{check_private_instance, is_admin, is_mod_or_admin}, -}; -use lemmy_db_schema::{ - newtypes::{CommunityId, PersonId}, - source::local_site::LocalSite, - ModlogActionType, + utils::{check_community_mod_action_opt, check_private_instance, is_admin}, }; +use lemmy_db_schema::{source::local_site::LocalSite, ModlogActionType}; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_moderator::structs::{ AdminPurgeCommentView, @@ -44,19 +40,16 @@ pub async fn get_mod_log( let type_ = data.type_.unwrap_or(All); let community_id = data.community_id; - let (local_person_id, is_admin) = match local_user_view { - Some(s) => (s.person.id, is_admin(&s).is_ok()), - None => (PersonId(-1), false), + let is_mod_or_admin = if let Some(local_user_view) = local_user_view { + let is_mod = community_id.is_some() + && check_community_mod_action_opt(&local_user_view, community_id, &mut context.pool()) + .await + .is_ok(); + is_mod || is_admin(&local_user_view).is_ok() + } else { + false }; - let community_id_value = match community_id { - Some(s) => s, - None => CommunityId(-1), - }; - let is_mod_of_community = data.community_id.is_some() - && is_mod_or_admin(&mut context.pool(), local_person_id, community_id_value) - .await - .is_ok(); - let hide_modlog_names = local_site.hide_modlog_mod_names && !is_mod_of_community && !is_admin; + let hide_modlog_names = local_site.hide_modlog_mod_names && !is_mod_or_admin; let mod_person_id = if hide_modlog_names { None diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs index 5083616c1..a85e29765 100644 --- a/crates/api_common/src/build_response.rs +++ b/crates/api_common/src/build_response.rs @@ -7,7 +7,7 @@ use crate::{ }; use actix_web::web::Json; use lemmy_db_schema::{ - newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId}, + newtypes::{CommentId, CommunityId, LocalUserId, PostId}, source::{ actor_language::CommunityLanguage, comment::Comment, @@ -44,10 +44,9 @@ pub async fn build_community_response( local_user_view: LocalUserView, community_id: CommunityId, ) -> Result, LemmyError> { - let is_mod_or_admin = - is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id) - .await - .is_ok(); + let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &local_user_view.person, community_id) + .await + .is_ok(); let person_id = local_user_view.person.id; let community_view = CommunityView::read( &mut context.pool(), @@ -67,16 +66,16 @@ pub async fn build_community_response( pub async fn build_post_response( context: &LemmyContext, community_id: CommunityId, - person_id: PersonId, + person: &Person, post_id: PostId, ) -> Result, LemmyError> { - let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), person_id, community_id) + let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), person, community_id) .await .is_ok(); let post_view = PostView::read( &mut context.pool(), post_id, - Some(person_id), + Some(person.id), is_mod_or_admin, ) .await?; diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 4dfad3645..139620b67 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -6,9 +6,7 @@ use crate::{ }; use actix_web::cookie::{Cookie, SameSite}; use anyhow::Context; -use chrono::{DateTime, Utc}; use lemmy_db_schema::{ - impls::person::is_banned, newtypes::{CommunityId, DbUrl, PersonId, PostId}, source::{ comment::{Comment, CommentUpdateForm}, @@ -33,7 +31,7 @@ use lemmy_db_views_actor::structs::{ }; use lemmy_utils::{ email::{send_email, translations::Lang}, - error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}, location_info, rate_limit::RateLimitConfig, settings::structs::Settings, @@ -49,10 +47,12 @@ pub static AUTH_COOKIE_NAME: &str = "auth"; #[tracing::instrument(skip_all)] pub async fn is_mod_or_admin( pool: &mut DbPool<'_>, - person_id: PersonId, + person: &Person, community_id: CommunityId, ) -> Result<(), LemmyError> { - let is_mod_or_admin = CommunityView::is_mod_or_admin(pool, person_id, community_id).await?; + check_user_valid(person)?; + + 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 { @@ -68,7 +68,7 @@ pub async fn is_mod_or_admin_opt( ) -> Result<(), LemmyError> { if let Some(local_user_view) = local_user_view { if let Some(community_id) = community_id { - is_mod_or_admin(pool, local_user_view.person.id, community_id).await + is_mod_or_admin(pool, &local_user_view.person, community_id).await } else { is_admin(local_user_view) } @@ -78,8 +78,11 @@ pub async fn is_mod_or_admin_opt( } pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> { + check_user_valid(&local_user_view.person)?; if !local_user_view.local_user.admin { Err(LemmyErrorType::NotAnAdmin)? + } else if local_user_view.person.banned { + Err(LemmyErrorType::Banned)? } else { Ok(()) } @@ -89,6 +92,7 @@ pub fn is_top_mod( local_user_view: &LocalUserView, community_mods: &[CommunityModeratorView], ) -> Result<(), LemmyError> { + check_user_valid(&local_user_view.person)?; if local_user_view.person.id != community_mods .first() @@ -134,52 +138,91 @@ pub async fn mark_post_as_unread( .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead) } -pub fn check_user_valid( - banned: bool, - ban_expires: Option>, - deleted: bool, -) -> Result<(), LemmyError> { +pub fn check_user_valid(person: &Person) -> Result<(), LemmyError> { // Check for a site ban - if is_banned(banned, ban_expires) { + if person.banned { Err(LemmyErrorType::SiteBan)? } // check for account deletion - else if deleted { + else if person.deleted { Err(LemmyErrorType::Deleted)? } else { Ok(()) } } -#[tracing::instrument(skip_all)] -pub async fn check_community_ban( - person_id: PersonId, +/// Checks that a normal user action (eg posting or voting) is allowed in a given community. +/// +/// In particular it checks that neither the user nor community are banned or deleted, and that +/// the user isn't banned. +pub async fn check_community_user_action( + person: &Person, community_id: CommunityId, pool: &mut DbPool<'_>, -) -> Result<(), LemmyError> { - let is_banned = CommunityPersonBanView::get(pool, person_id, community_id) - .await - .is_ok(); - if is_banned { - Err(LemmyErrorType::BannedFromCommunity)? - } else { - Ok(()) - } +) -> LemmyResult<()> { + check_user_valid(person)?; + check_community_deleted_removed(community_id, pool).await?; + check_community_ban(person, community_id, pool).await?; + Ok(()) } -#[tracing::instrument(skip_all)] -pub async fn check_community_deleted_or_removed( +async fn check_community_deleted_removed( community_id: CommunityId, pool: &mut DbPool<'_>, -) -> Result<(), LemmyError> { +) -> LemmyResult<()> { let community = Community::read(pool, community_id) .await .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?; if community.deleted || community.removed { Err(LemmyErrorType::Deleted)? - } else { - 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. +/// +/// In particular it checks that he is an admin or mod, wasn't banned and the community isn't +/// removed/deleted. +pub async fn check_community_mod_action( + person: &Person, + community_id: CommunityId, + allow_deleted: bool, + pool: &mut DbPool<'_>, +) -> LemmyResult<()> { + is_mod_or_admin(pool, person, community_id).await?; + check_community_ban(person, community_id, pool).await?; + + // it must be possible to restore deleted community + if !allow_deleted { + check_community_deleted_removed(community_id, pool).await?; + } + Ok(()) +} + +pub async fn check_community_mod_action_opt( + local_user_view: &LocalUserView, + community_id: Option, + pool: &mut DbPool<'_>, +) -> LemmyResult<()> { + if let Some(community_id) = community_id { + check_community_mod_action(&local_user_view.person, community_id, false, pool).await?; + } else { + is_admin(local_user_view)?; + } + Ok(()) } pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> { diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index dcf998bd7..2e719eda2 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -6,8 +6,7 @@ use lemmy_api_common::{ context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, utils::{ - check_community_ban, - check_community_deleted_or_removed, + check_community_user_action, check_post_deleted_or_removed, generate_local_apub_endpoint, get_post, @@ -57,8 +56,7 @@ pub async fn create_comment( let post = get_post(post_id, &mut context.pool()).await?; let community_id = post.community_id; - check_community_ban(local_user_view.person.id, community_id, &mut context.pool()).await?; - check_community_deleted_or_removed(community_id, &mut context.pool()).await?; + check_community_user_action(&local_user_view.person, community_id, &mut context.pool()).await?; check_post_deleted_or_removed(&post)?; // Check if post is locked, no new comments diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs index 1c986d03c..2de2a7955 100644 --- a/crates/api_crud/src/comment/delete.rs +++ b/crates/api_crud/src/comment/delete.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ comment::{CommentResponse, DeleteComment}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::check_community_ban, + utils::check_community_user_action, }; use lemmy_db_schema::{ source::{ @@ -31,8 +31,8 @@ pub async fn delete_comment( Err(LemmyErrorType::CouldntUpdateComment)? } - check_community_ban( - local_user_view.person.id, + check_community_user_action( + &local_user_view.person, orig_comment.community.id, &mut context.pool(), ) diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs index 601101bb8..cbfbcd22c 100644 --- a/crates/api_crud/src/comment/remove.rs +++ b/crates/api_crud/src/comment/remove.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ comment::{CommentResponse, RemoveComment}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, is_mod_or_admin}, + utils::check_community_mod_action, }; use lemmy_db_schema::{ source::{ @@ -27,21 +27,14 @@ pub async fn remove_comment( let comment_id = data.comment_id; let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; - check_community_ban( - local_user_view.person.id, + check_community_mod_action( + &local_user_view.person, orig_comment.community.id, + false, &mut context.pool(), ) .await?; - // Verify that only a mod or admin can remove - is_mod_or_admin( - &mut context.pool(), - local_user_view.person.id, - orig_comment.community.id, - ) - .await?; - // Do the remove let removed = data.removed; let updated_comment = Comment::update( diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs index d6c672262..21cf54cfa 100644 --- a/crates/api_crud/src/comment/update.rs +++ b/crates/api_crud/src/comment/update.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ comment::{CommentResponse, EditComment}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, local_site_to_slur_regex}, + utils::{check_community_user_action, local_site_to_slur_regex}, }; use lemmy_db_schema::{ source::{ @@ -37,8 +37,8 @@ pub async fn update_comment( let comment_id = data.comment_id; let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; - check_community_ban( - local_user_view.person.id, + check_community_user_action( + &local_user_view.person, orig_comment.community.id, &mut context.pool(), ) diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs index 7c94e0ccc..60b79fd79 100644 --- a/crates/api_crud/src/community/delete.rs +++ b/crates/api_crud/src/community/delete.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ community::{CommunityResponse, DeleteCommunity}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::is_top_mod, + utils::{check_community_mod_action, is_top_mod}, }; use lemmy_db_schema::{ source::community::{Community, CommunityUpdateForm}, @@ -26,6 +26,14 @@ pub async fn delete_community( let community_mods = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?; + check_community_mod_action( + &local_user_view.person, + community_id, + true, + &mut context.pool(), + ) + .await?; + // Make sure deleter is the top mod is_top_mod(&local_user_view, &community_mods)?; diff --git a/crates/api_crud/src/community/remove.rs b/crates/api_crud/src/community/remove.rs index 3a2cc654a..9604b0432 100644 --- a/crates/api_crud/src/community/remove.rs +++ b/crates/api_crud/src/community/remove.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ community::{CommunityResponse, RemoveCommunity}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::is_admin, + utils::{check_community_mod_action, is_admin}, }; use lemmy_db_schema::{ source::{ @@ -26,6 +26,14 @@ pub async fn remove_community( context: Data, local_user_view: LocalUserView, ) -> Result, LemmyError> { + check_community_mod_action( + &local_user_view.person, + data.community_id, + true, + &mut context.pool(), + ) + .await?; + // Verify its an admin (only an admin can remove a community) is_admin(&local_user_view)?; diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs index 8e5ec77c0..40ba1a2a1 100644 --- a/crates/api_crud/src/community/update.rs +++ b/crates/api_crud/src/community/update.rs @@ -5,10 +5,9 @@ use lemmy_api_common::{ community::{CommunityResponse, EditCommunity}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::local_site_to_slur_regex, + utils::{check_community_mod_action, local_site_to_slur_regex}, }; use lemmy_db_schema::{ - newtypes::PersonId, source::{ actor_language::{CommunityLanguage, SiteLanguage}, community::{Community, CommunityUpdateForm}, @@ -18,7 +17,6 @@ use lemmy_db_schema::{ utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now}, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_utils::{ error::{LemmyError, LemmyErrorExt, LemmyErrorType}, utils::{slurs::check_slurs_opt, validation::is_valid_body_field}, @@ -42,14 +40,13 @@ pub async fn update_community( let description = diesel_option_overwrite(data.description.clone()); // Verify its a mod (only mods can edit it) - let community_id = data.community_id; - let mods: Vec = - CommunityModeratorView::for_community(&mut context.pool(), community_id) - .await - .map(|v| v.into_iter().map(|m| m.moderator.id).collect())?; - if !mods.contains(&local_user_view.person.id) { - Err(LemmyErrorType::NotAModerator)? - } + check_community_mod_action( + &local_user_view.person, + data.community_id, + false, + &mut context.pool(), + ) + .await?; let community_id = data.community_id; if let Some(languages) = data.discussion_languages.clone() { diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 33408b774..e4af92916 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -7,8 +7,7 @@ use lemmy_api_common::{ request::fetch_site_data, send_activity::{ActivityChannel, SendActivityData}, utils::{ - check_community_ban, - check_community_deleted_or_removed, + check_community_user_action, generate_local_apub_endpoint, honeypot_check, local_site_to_slur_regex, @@ -60,13 +59,12 @@ pub async fn create_post( is_valid_body_field(&data.body, true)?; check_url_scheme(&data.url)?; - check_community_ban( - local_user_view.person.id, + check_community_user_action( + &local_user_view.person, data.community_id, &mut context.pool(), ) .await?; - check_community_deleted_or_removed(data.community_id, &mut context.pool()).await?; let community_id = data.community_id; let community = Community::read(&mut context.pool(), community_id).await?; @@ -184,5 +182,5 @@ pub async fn create_post( }); }; - build_post_response(&context, community_id, person_id, post_id).await + build_post_response(&context, community_id, &local_user_view.person, post_id).await } diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs index 90c95c6b2..630bfa357 100644 --- a/crates/api_crud/src/post/delete.rs +++ b/crates/api_crud/src/post/delete.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ context::LemmyContext, post::{DeletePost, PostResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, check_community_deleted_or_removed}, + utils::check_community_user_action, }; use lemmy_db_schema::{ source::post::{Post, PostUpdateForm}, @@ -28,13 +28,12 @@ pub async fn delete_post( Err(LemmyErrorType::CouldntUpdatePost)? } - check_community_ban( - local_user_view.person.id, + check_community_user_action( + &local_user_view.person, orig_post.community_id, &mut context.pool(), ) .await?; - check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?; // Verify that only the creator can delete if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) { @@ -52,12 +51,17 @@ pub async fn delete_post( ) .await?; - let person_id = local_user_view.person.id; ActivityChannel::submit_activity( - SendActivityData::DeletePost(post, local_user_view.person, data.0.clone()), + SendActivityData::DeletePost(post, local_user_view.person.clone(), data.0.clone()), &context, ) .await?; - build_post_response(&context, orig_post.community_id, person_id, data.post_id).await + build_post_response( + &context, + orig_post.community_id, + &local_user_view.person, + data.post_id, + ) + .await } diff --git a/crates/api_crud/src/post/remove.rs b/crates/api_crud/src/post/remove.rs index 52f380d88..2dd35d598 100644 --- a/crates/api_crud/src/post/remove.rs +++ b/crates/api_crud/src/post/remove.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ context::LemmyContext, post::{PostResponse, RemovePost}, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, is_mod_or_admin}, + utils::check_community_mod_action, }; use lemmy_db_schema::{ source::{ @@ -26,21 +26,14 @@ pub async fn remove_post( let post_id = data.post_id; let orig_post = Post::read(&mut context.pool(), post_id).await?; - check_community_ban( - local_user_view.person.id, + check_community_mod_action( + &local_user_view.person, orig_post.community_id, + false, &mut context.pool(), ) .await?; - // Verify that only the mods can remove - is_mod_or_admin( - &mut context.pool(), - local_user_view.person.id, - orig_post.community_id, - ) - .await?; - // Update the post let post_id = data.post_id; let removed = data.removed; @@ -63,12 +56,17 @@ pub async fn remove_post( }; ModRemovePost::create(&mut context.pool(), &form).await?; - let person_id = local_user_view.person.id; ActivityChannel::submit_activity( - SendActivityData::RemovePost(post, local_user_view.person, data.0), + SendActivityData::RemovePost(post, local_user_view.person.clone(), data.0), &context, ) .await?; - build_post_response(&context, orig_post.community_id, person_id, post_id).await + build_post_response( + &context, + orig_post.community_id, + &local_user_view.person, + post_id, + ) + .await } diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index dfd311f3f..b17981c55 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ post::{EditPost, PostResponse}, request::fetch_site_data, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, local_site_to_slur_regex}, + utils::{check_community_user_action, local_site_to_slur_regex}, }; use lemmy_db_schema::{ source::{ @@ -55,8 +55,8 @@ pub async fn update_post( let post_id = data.post_id; let orig_post = Post::read(&mut context.pool(), post_id).await?; - check_community_ban( - local_user_view.person.id, + check_community_user_action( + &local_user_view.person, orig_post.community_id, &mut context.pool(), ) @@ -107,7 +107,7 @@ pub async fn update_post( build_post_response( context.deref(), orig_post.community_id, - local_user_view.person.id, + &local_user_view.person, post_id, ) .await diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs index 502bc5e0c..e162709ba 100644 --- a/crates/apub/src/activities/create_or_update/comment.rs +++ b/crates/apub/src/activities/create_or_update/comment.rs @@ -140,7 +140,7 @@ impl ActivityHandler for CreateOrUpdateNote { if distinguished != existing_comment.distinguished { let creator = self.actor.dereference(context).await?; let (post, _) = self.object.get_parents(context).await?; - is_mod_or_admin(&mut context.pool(), creator.id, post.community_id).await?; + is_mod_or_admin(&mut context.pool(), &creator, post.community_id).await?; } } diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 1cb266c21..936af8ad7 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -92,9 +92,7 @@ pub(crate) async fn verify_person_in_community( } let person_id = person.id; let community_id = community.id; - let is_banned = CommunityPersonBanView::get(&mut context.pool(), person_id, community_id) - .await - .is_ok(); + let is_banned = CommunityPersonBanView::get(&mut context.pool(), person_id, community_id).await?; if is_banned { Err(LemmyErrorType::PersonIsBannedFromCommunity)? } else { diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 4aa398bc2..6aba17554 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -166,7 +166,7 @@ impl Object for ApubPost { let creator = page.creator()?.dereference(context).await?; let community = page.community(context).await?; if community.posting_restricted_to_mods { - is_mod_or_admin(&mut context.pool(), creator.id, community.id).await?; + is_mod_or_admin(&mut context.pool(), &creator, community.id).await?; } let mut name = page .name diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index 1a2974439..12ec0392f 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -11,7 +11,6 @@ use crate::{ traits::{ApubActor, Crud, Followable}, utils::{functions::lower, get_conn, naive_now, DbPool}, }; -use chrono::{DateTime, Utc}; use diesel::{dsl::insert_into, result::Error, ExpressionMethods, JoinOnDsl, QueryDsl}; use diesel_async::RunQueryDsl; @@ -87,14 +86,6 @@ impl Person { } } -pub fn is_banned(banned_: bool, expires: Option>) -> bool { - if let Some(expires) = expires { - banned_ && expires.gt(&naive_now()) - } else { - banned_ - } -} - #[async_trait] impl ApubActor for Person { async fn read_from_apub_id( diff --git a/crates/db_views_actor/src/community_person_ban_view.rs b/crates/db_views_actor/src/community_person_ban_view.rs index ba0b4cd7f..712bb2d3a 100644 --- a/crates/db_views_actor/src/community_person_ban_view.rs +++ b/crates/db_views_actor/src/community_person_ban_view.rs @@ -1,9 +1,9 @@ use crate::structs::CommunityPersonBanView; -use diesel::{result::Error, ExpressionMethods, QueryDsl}; +use diesel::{dsl::exists, result::Error, select, ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ newtypes::{CommunityId, PersonId}, - schema::{community, community_person_ban, person}, + schema::community_person_ban, utils::{get_conn, DbPool}, }; @@ -12,16 +12,14 @@ impl CommunityPersonBanView { pool: &mut DbPool<'_>, from_person_id: PersonId, from_community_id: CommunityId, - ) -> Result { + ) -> Result { let conn = &mut get_conn(pool).await?; - community_person_ban::table - .inner_join(community::table) - .inner_join(person::table) - .select((community::all_columns, person::all_columns)) - .filter(community_person_ban::community_id.eq(from_community_id)) - .filter(community_person_ban::person_id.eq(from_person_id)) - .order_by(community_person_ban::published) - .first::(conn) - .await + select(exists( + community_person_ban::table + .filter(community_person_ban::community_id.eq(from_community_id)) + .filter(community_person_ban::person_id.eq(from_person_id)), + )) + .get_result::(conn) + .await } } diff --git a/crates/routes/src/lib.rs b/crates/routes/src/lib.rs index 28da113ef..ec28fda45 100644 --- a/crates/routes/src/lib.rs +++ b/crates/routes/src/lib.rs @@ -14,11 +14,7 @@ async fn local_user_view_from_jwt( ) -> Result { let local_user_id = Claims::validate(jwt, context).await?; let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?; - check_user_valid( - local_user_view.person.banned, - local_user_view.person.ban_expires, - local_user_view.person.deleted, - )?; + check_user_valid(&local_user_view.person)?; Ok(local_user_view) } diff --git a/src/session_middleware.rs b/src/session_middleware.rs index 1d19cec9c..80b79f917 100644 --- a/src/session_middleware.rs +++ b/src/session_middleware.rs @@ -109,11 +109,7 @@ async fn local_user_view_from_jwt( .await .with_lemmy_type(LemmyErrorType::NotLoggedIn)?; let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?; - check_user_valid( - local_user_view.person.banned, - local_user_view.person.ban_expires, - local_user_view.person.deleted, - )?; + check_user_valid(&local_user_view.person)?; Ok(local_user_view) }