Receive and store remote site bans (fixes #3399) (#5515)

* Receive and store remote site bans (fixes #3399)

* db schema changes, handly expiration

* partial federation changes

* add column `mod_ban.instance_id`

* remove unused

* wip: remove person.banned

* fix down migration

* fmt, keep banned status

* fixes

* simplify

* update post removed

* fix api tests

* ban from local instance

* banned() helpers

* cleanup undo_block_user

* remove order by

* cache SiteView::read_local, add instance

* use local instance id for PersonView

* add helper function person_with_instance_actions

* use exists

* comments update removed

* remove method is_mod_or_admin

* fix tests

* no unwrap

* change local_instance_person_join

* remove LocalSite::read

* wip: home_instance_actions

* add home_instance_actions to all structs

* fixes

* fix tests

* fmt

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
This commit is contained in:
Nutomic 2025-03-28 12:47:24 +00:00 committed by GitHub
parent e079a41308
commit 636811eb8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
102 changed files with 2822 additions and 4104 deletions

View file

@ -29,7 +29,7 @@
"eslint": "^9.20.0",
"eslint-plugin-prettier": "^5.2.3",
"jest": "^29.5.0",
"lemmy-js-client": "1.0.0-action-structs.1",
"lemmy-js-client": "1.0.0-site-person-ban.1",
"prettier": "^3.5.0",
"ts-jest": "^29.1.0",
"tsoa": "^6.6.0",

File diff suppressed because it is too large Load diff

View file

@ -40,6 +40,7 @@ import {
getMyUser,
listInbox,
getModlog,
getCommunity,
} from "./shared";
import { PostView } from "lemmy-js-client/dist/types/PostView";
import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams";
@ -461,7 +462,7 @@ test("Search for a post", async () => {
expect(betaPost?.post.name).toBeDefined();
});
test("Enforce site ban federation for local user", async () => {
test.only("Enforce site ban federation for local user", async () => {
if (!betaCommunity) {
throw "Missing beta community";
}
@ -499,9 +500,11 @@ test("Enforce site ban federation for local user", async () => {
// alpha ban should be federated to beta
let alphaUserOnBeta1 = await waitUntil(
() => resolvePerson(beta, alphaUserActorId!),
res => res.person?.person.banned ?? false,
res => res.person?.home_instance_actions?.received_ban != null,
);
expect(alphaUserOnBeta1.person?.person.banned).toBe(true);
expect(
alphaUserOnBeta1.person?.home_instance_actions?.received_ban,
).toBeDefined();
// existing alpha post should be removed on beta
let betaBanRes = await waitUntil(
@ -557,7 +560,9 @@ test("Enforce site ban federation for federated user", async () => {
await followBeta(alphaUserHttp);
let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId!);
expect(alphaUserOnBeta2.person?.person.banned).toBe(false);
expect(
alphaUserOnBeta2.person?.instance_actions?.received_ban,
).toBeUndefined();
if (!alphaUserOnBeta2.person) {
throw "Missing alpha person";
@ -577,29 +582,35 @@ test("Enforce site ban federation for federated user", async () => {
);
expect(banAlphaOnBeta.banned).toBe(true);
// The beta site ban should NOT be federated to alpha
let alphaPerson2 = (await getMyUser(alphaUserHttp)).local_user_view.person;
expect(alphaPerson2.banned).toBe(false);
// existing alpha post should be removed on beta
let betaBanRes = await waitUntil(
() => getPost(beta, searchBeta1.post.id),
s => s.post_view.post.removed,
);
expect(betaBanRes.post_view.post.removed).toBe(true);
let betaRemovedPost = await getPost(beta, searchBeta1.post.id);
expect(betaRemovedPost.post_view.post.removed).toBe(true);
// existing alpha's post to the beta community should be removed on alpha
let alphaPostAfterRemoveOnBeta = await waitUntil(
// post should also be removed on alpha
let alphaRemovedPost = await waitUntil(
() => getPost(alpha, postRes1.post_view.post.id),
s => s.post_view.post.removed,
);
expect(betaBanRes.post_view.post.removed).toBe(true);
expect(alphaPostAfterRemoveOnBeta.post_view.post.removed).toBe(true);
expect(alphaRemovedPost.post_view.post.removed).toBe(true);
// User should not be shown to be banned from alpha
let alphaPerson2 = (await getMyUser(alphaUserHttp)).local_user_view;
expect(alphaPerson2.instance_actions?.received_ban).toBeUndefined();
// but the ban should be indicated by beta community on alpha
let communityWithBan = await getCommunity(
alphaUserHttp,
betaCommunity.community.id,
);
expect(
alphaPostAfterRemoveOnBeta.post_view.creator_community_actions
?.received_ban,
communityWithBan.community_view.instance_actions?.received_ban,
).toBeDefined();
// post to beta community is rejected
await expect(
createPost(alphaUserHttp, betaCommunity.community.id),
).rejects.toStrictEqual(Error("site_ban"));
await unfollowRemotes(alpha);
});

View file

@ -24,7 +24,7 @@ pub async fn distinguish_comment(
.await?;
check_community_user_action(
&local_user_view.person,
&local_user_view,
&orig_comment.community,
&mut context.pool(),
)
@ -37,7 +37,7 @@ pub async fn distinguish_comment(
// Verify that only a mod or admin can distinguish a comment
check_community_mod_action(
&local_user_view.person,
&local_user_view,
&orig_comment.community,
false,
&mut context.pool(),

View file

@ -13,11 +13,10 @@ use lemmy_db_schema::{
source::{
comment::{CommentActions, CommentLikeForm},
comment_reply::CommentReply,
local_site::LocalSite,
},
traits::Likeable,
};
use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_db_views::structs::{CommentView, LocalUserView, SiteView};
use lemmy_utils::error::LemmyResult;
use std::ops::Deref;
@ -26,7 +25,7 @@ pub async fn like_comment(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<CommentResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
let comment_id = data.comment_id;
let mut recipient_ids = Vec::<LocalUserId>::new();
@ -49,7 +48,7 @@ pub async fn like_comment(
.await?;
check_community_user_action(
&local_user_view.person,
&local_user_view,
&orig_comment.community,
&mut context.pool(),
)

View file

@ -22,7 +22,7 @@ pub async fn list_comment_likes(
is_mod_or_admin(
&mut context.pool(),
&local_user_view.person,
&local_user_view,
comment_view.community.id,
)
.await?;

View file

@ -24,13 +24,7 @@ pub async fn add_mod_to_community(
) -> LemmyResult<Json<AddModToCommunityResponse>> {
let community = Community::read(&mut context.pool(), data.community_id).await?;
// Verify that only mods or admins can add mod
check_community_mod_action(
&local_user_view.person,
&community,
false,
&mut context.pool(),
)
.await?;
check_community_mod_action(&local_user_view, &community, false, &mut context.pool()).await?;
// If its a mod removal, also check that you're a higher mod.
if !data.added {

View file

@ -31,13 +31,7 @@ pub async fn ban_from_community(
let community = Community::read(&mut context.pool(), data.community_id).await?;
// Verify that only mods or admins can ban
check_community_mod_action(
&local_user_view.person,
&community,
false,
&mut context.pool(),
)
.await?;
check_community_mod_action(&local_user_view, &community, false, &mut context.pool()).await?;
LocalUser::is_higher_mod_or_admin_check(
&mut context.pool(),

View file

@ -4,7 +4,7 @@ use lemmy_api_common::{
community::{CommunityResponse, FollowCommunity},
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_deleted_removed, check_user_valid},
utils::{check_community_deleted_removed, check_local_user_valid},
};
use lemmy_db_schema::{
source::{
@ -22,7 +22,7 @@ pub async fn follow_community(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<CommunityResponse>> {
check_user_valid(&local_user_view.person)?;
check_local_user_valid(&local_user_view)?;
let community = Community::read(&mut context.pool(), data.community_id).await?;
let person_id = local_user_view.person.id;

View file

@ -16,12 +16,7 @@ pub async fn post_pending_follows_approve(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<SuccessResponse>> {
is_mod_or_admin(
&mut context.pool(),
&local_user_view.person,
data.community_id,
)
.await?;
is_mod_or_admin(&mut context.pool(), &local_user_view, data.community_id).await?;
let activity_data = if data.approve {
CommunityActions::approve_follower(

View file

@ -12,12 +12,7 @@ pub async fn get_pending_follows_count(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<GetCommunityPendingFollowsCountResponse>> {
is_mod_or_admin(
&mut context.pool(),
&local_user_view.person,
data.community_id,
)
.await?;
is_mod_or_admin(&mut context.pool(), &local_user_view, data.community_id).await?;
let count =
CommunityFollowerView::count_approval_required(&mut context.pool(), data.community_id).await?;
Ok(Json(GetCommunityPendingFollowsCountResponse { count }))

View file

@ -5,12 +5,8 @@ use lemmy_api_common::{
context::LemmyContext,
utils::{check_private_instance, is_mod_or_admin_opt},
};
use lemmy_db_schema::source::{
actor_language::CommunityLanguage,
community::Community,
local_site::LocalSite,
};
use lemmy_db_views::structs::{CommunityView, LocalUserView};
use lemmy_db_schema::source::{actor_language::CommunityLanguage, community::Community};
use lemmy_db_views::structs::{CommunityView, LocalUserView, SiteView};
use lemmy_utils::error::LemmyResult;
pub async fn get_random_community(
@ -18,7 +14,7 @@ pub async fn get_random_community(
context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>,
) -> LemmyResult<Json<CommunityResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
check_private_instance(&local_user_view, &local_site)?;

View file

@ -30,7 +30,7 @@ pub async fn transfer_community(
let mut community_mods =
CommunityModeratorView::for_community(&mut context.pool(), community.id).await?;
check_community_user_action(&local_user_view.person, &community, &mut context.pool()).await?;
check_community_user_action(&local_user_view, &community, &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())

View file

@ -1,20 +1,5 @@
use activitypub_federation::config::Data;
use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
use captcha::Captcha;
use lemmy_api_common::{
community::BanFromCommunity,
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::check_expire_time,
};
use lemmy_db_schema::{
source::{
community::{CommunityActions, CommunityPersonBanForm},
mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
person::Person,
},
traits::{Bannable, Crud, Followable},
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
@ -132,89 +117,6 @@ fn build_totp_2fa(hostname: &str, username: &str, secret: &str) -> LemmyResult<T
.with_lemmy_type(LemmyErrorType::CouldntGenerateTotp)
}
/// Site bans are only federated for local users.
/// This is a problem, because site-banning non-local users will still leave content
/// they've posted to our local communities, on other servers.
///
/// So when doing a site ban for a non-local user, you need to federate/send a
/// community ban for every local community they've participated in.
/// See https://github.com/LemmyNet/lemmy/issues/4118
pub(crate) async fn ban_nonlocal_user_from_local_communities(
local_user_view: &LocalUserView,
target: &Person,
ban: bool,
reason: &Option<String>,
remove_or_restore_data: &Option<bool>,
expires: &Option<i64>,
context: &Data<LemmyContext>,
) -> LemmyResult<()> {
// Only run this code for federated users
if !target.local {
let ids = Person::list_local_community_ids(&mut context.pool(), target.id).await?;
for community_id in ids {
let expires_dt = check_expire_time(*expires)?;
// Ban / unban them from our local communities
let community_user_ban_form = CommunityPersonBanForm {
ban_expires: Some(expires_dt),
..CommunityPersonBanForm::new(community_id, target.id)
};
if ban {
// Ignore all errors for these
CommunityActions::ban(&mut context.pool(), &community_user_ban_form)
.await
.ok();
// Also unsubscribe them from the community, if they are subscribed
CommunityActions::unfollow(&mut context.pool(), target.id, community_id)
.await
.ok();
} else {
CommunityActions::unban(&mut context.pool(), &community_user_ban_form)
.await
.ok();
}
// Mod tables
let form = ModBanFromCommunityForm {
mod_person_id: local_user_view.person.id,
other_person_id: target.id,
community_id,
reason: reason.clone(),
banned: Some(ban),
expires: expires_dt,
};
ModBanFromCommunity::create(&mut context.pool(), &form).await?;
// Federate the ban from community
let ban_from_community = BanFromCommunity {
community_id,
person_id: target.id,
ban,
reason: reason.clone(),
remove_or_restore_data: *remove_or_restore_data,
expires: *expires,
};
ActivityChannel::submit_activity(
SendActivityData::BanFromCommunity {
moderator: local_user_view.person.clone(),
community_id,
target: target.clone(),
data: ban_from_community,
},
context,
)?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {

View file

@ -12,7 +12,7 @@ use lemmy_db_schema::{
traits::Crud,
};
use lemmy_db_views::{person::person_view::PersonQuery, structs::LocalUserView};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult};
pub async fn add_admin(
data: Json<AddAdmin>,
@ -61,7 +61,7 @@ pub async fn add_admin(
admins_only: Some(true),
..Default::default()
}
.list(&mut context.pool())
.list(local_user_view.person.instance_id, &mut context.pool())
.await?;
Ok(Json(AddAdminResponse { admins }))

View file

@ -1,4 +1,3 @@
use crate::ban_nonlocal_user_from_local_communities;
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
@ -9,16 +8,16 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{
source::{
instance::{InstanceActions, InstanceBanForm},
local_user::LocalUser,
login_token::LoginToken,
mod_log::moderator::{ModBan, ModBanForm},
person::{Person, PersonUpdateForm},
},
traits::Crud,
traits::{Bannable, Crud},
};
use lemmy_db_views::structs::{LocalUserView, PersonView};
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
error::{LemmyErrorExt2, LemmyErrorType, LemmyResult},
utils::validation::is_valid_body_field,
};
@ -44,20 +43,19 @@ pub async fn ban_from_site(
let expires = check_expire_time(data.expires)?;
let person = Person::update(
&mut context.pool(),
data.person_id,
&PersonUpdateForm {
banned: Some(data.ban),
ban_expires: Some(expires),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
let form = InstanceBanForm::new(data.person_id, local_user_view.person.instance_id, expires);
if data.ban {
InstanceActions::ban(&mut context.pool(), &form)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
} else {
InstanceActions::unban(&mut context.pool(), &form)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
}
// if its a local user, invalidate logins
let local_user = LocalUserView::read_person(&mut context.pool(), person.id).await;
let local_user = LocalUserView::read_person(&mut context.pool(), data.person_id).await;
if let Ok(local_user) = local_user {
LoginToken::invalidate_all(&mut context.pool(), local_user.local_user.id).await?;
}
@ -67,7 +65,7 @@ pub async fn ban_from_site(
let removed = data.ban;
remove_or_restore_user_data(
local_user_view.person.id,
person.id,
data.person_id,
removed,
&data.reason,
&context,
@ -78,26 +76,16 @@ pub async fn ban_from_site(
// Mod tables
let form = ModBanForm {
mod_person_id: local_user_view.person.id,
other_person_id: person.id,
other_person_id: data.person_id,
reason: data.reason.clone(),
banned: Some(data.ban),
expires,
instance_id: local_user_view.person.instance_id,
};
ModBan::create(&mut context.pool(), &form).await?;
let person_view = PersonView::read(&mut context.pool(), person.id, false).await?;
ban_nonlocal_user_from_local_communities(
&local_user_view,
&person,
data.ban,
&data.reason,
&data.remove_or_restore_data,
&data.expires,
&context,
)
.await?;
let person_view = PersonView::read(&mut context.pool(), data.person_id, false).await?;
ActivityChannel::submit_activity(
SendActivityData::BanFromSite {

View file

@ -14,14 +14,12 @@ use lemmy_api_common::{
person::{CaptchaResponse, GetCaptchaResponse},
LemmyErrorType,
};
use lemmy_db_schema::source::{
captcha_answer::{CaptchaAnswer, CaptchaAnswerForm},
local_site::LocalSite,
};
use lemmy_db_schema::source::captcha_answer::{CaptchaAnswer, CaptchaAnswerForm};
use lemmy_db_views::structs::SiteView;
use lemmy_utils::error::LemmyResult;
pub async fn get_captcha(context: Data<LemmyContext>) -> LemmyResult<HttpResponse> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
let mut res = HttpResponseBuilder::new(StatusCode::OK);
res.insert_header(CacheControl(vec![CacheDirective::NoStore]));

View file

@ -31,7 +31,7 @@ pub async fn list_banned_users(
limit: data.limit,
..Default::default()
}
.list(&mut context.pool())
.list(local_user_view.person.instance_id, &mut context.pool())
.await?;
let next_page = banned.last().map(PaginationCursorBuilder::to_cursor);

View file

@ -8,7 +8,7 @@ use lemmy_api_common::{
claims::Claims,
context::LemmyContext,
person::{Login, LoginResponse},
utils::{check_email_verified, check_registration_application, check_user_valid},
utils::{check_email_verified, check_local_user_valid, check_registration_application},
};
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
@ -35,7 +35,7 @@ pub async fn login(
if !valid {
Err(LemmyErrorType::IncorrectLogin)?
}
check_user_valid(&local_user_view.person)?;
check_local_user_valid(&local_user_view)?;
check_email_verified(&local_user_view, &site_view)?;
check_registration_application(&local_user_view, &site_view.local_site, &mut context.pool())

View file

@ -28,13 +28,7 @@ pub async fn feature_post(
let orig_post = Post::read(&mut context.pool(), post_id).await?;
let community = Community::read(&mut context.pool(), orig_post.community_id).await?;
check_community_mod_action(
&local_user_view.person,
&community,
false,
&mut context.pool(),
)
.await?;
check_community_mod_action(&local_user_view, &community, false, &mut context.pool()).await?;
if data.feature_type == PostFeatureType::Local {
is_admin(&local_user_view)?;

View file

@ -10,13 +10,10 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{
newtypes::PostOrCommentId,
source::{
local_site::LocalSite,
post::{PostActions, PostLikeForm, PostReadForm},
},
source::post::{PostActions, PostLikeForm, PostReadForm},
traits::{Likeable, Readable},
};
use lemmy_db_views::structs::{LocalUserView, PostView};
use lemmy_db_views::structs::{LocalUserView, PostView, SiteView};
use lemmy_utils::error::LemmyResult;
use std::ops::Deref;
@ -25,7 +22,7 @@ pub async fn like_post(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<PostResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
let post_id = data.post_id;
check_local_vote_mode(
@ -41,12 +38,7 @@ pub async fn like_post(
// Check for a community ban
let post = PostView::read(&mut context.pool(), post_id, None, false).await?;
check_community_user_action(
&local_user_view.person,
&post.community,
&mut context.pool(),
)
.await?;
check_community_user_action(&local_user_view, &post.community, &mut context.pool()).await?;
let mut like_form = PostLikeForm::new(data.post_id, local_user_view.person.id, data.score);

View file

@ -15,12 +15,7 @@ pub async fn list_post_likes(
local_user_view: LocalUserView,
) -> LemmyResult<Json<ListPostLikesResponse>> {
let post = Post::read(&mut context.pool(), data.post_id).await?;
is_mod_or_admin(
&mut context.pool(),
&local_user_view.person,
post.community_id,
)
.await?;
is_mod_or_admin(&mut context.pool(), &local_user_view, post.community_id).await?;
let post_likes =
VoteView::list_for_post(&mut context.pool(), data.post_id, data.page, data.limit).await?;

View file

@ -26,7 +26,7 @@ pub async fn lock_post(
let orig_post = PostView::read(&mut context.pool(), post_id, None, false).await?;
check_community_mod_action(
&local_user_view.person,
&local_user_view,
&orig_post.community,
false,
&mut context.pool(),

View file

@ -13,13 +13,10 @@ use lemmy_api_common::{
},
};
use lemmy_db_schema::{
source::{
comment_report::{CommentReport, CommentReportForm},
local_site::LocalSite,
},
source::comment_report::{CommentReport, CommentReportForm},
traits::Reportable,
};
use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView};
use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView, SiteView};
use lemmy_utils::error::LemmyResult;
/// Creates a comment report and notifies the moderators of the community
@ -42,7 +39,7 @@ pub async fn create_comment_report(
.await?;
check_community_user_action(
&local_user_view.person,
&local_user_view,
&comment_view.community,
&mut context.pool(),
)
@ -65,7 +62,7 @@ pub async fn create_comment_report(
CommentReportView::read(&mut context.pool(), report.id, person_id).await?;
// Email the admins
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
if local_site.reports_email_admins {
send_new_report_email_to_admins(
&comment_report_view.creator.name,

View file

@ -22,7 +22,7 @@ pub async fn resolve_comment_report(
let person_id = local_user_view.person.id;
check_community_mod_action(
&local_user_view.person,
&local_user_view,
&report.community,
true,
&mut context.pool(),

View file

@ -9,11 +9,10 @@ use lemmy_db_schema::{
source::{
community::Community,
community_report::{CommunityReport, CommunityReportForm},
local_site::LocalSite,
},
traits::{Crud, Reportable},
};
use lemmy_db_views::structs::{CommunityReportView, LocalUserView};
use lemmy_db_views::structs::{CommunityReportView, LocalUserView, SiteView};
use lemmy_utils::error::LemmyResult;
pub async fn create_community_report(
@ -47,7 +46,7 @@ pub async fn create_community_report(
CommunityReportView::read(&mut context.pool(), report.id, person_id).await?;
// Email the admins
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
if local_site.reports_email_admins {
send_new_report_email_to_admins(
&community_report_view.creator.name,

View file

@ -13,13 +13,10 @@ use lemmy_api_common::{
},
};
use lemmy_db_schema::{
source::{
local_site::LocalSite,
post_report::{PostReport, PostReportForm},
},
source::post_report::{PostReport, PostReportForm},
traits::Reportable,
};
use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView};
use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView, SiteView};
use lemmy_utils::error::LemmyResult;
/// Creates a post report and notifies the moderators of the community
@ -36,12 +33,7 @@ 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_user_action(
&local_user_view.person,
&post_view.community,
&mut context.pool(),
)
.await?;
check_community_user_action(&local_user_view, &post_view.community, &mut context.pool()).await?;
check_post_deleted_or_removed(&post_view.post)?;
@ -60,7 +52,7 @@ pub async fn create_post_report(
let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?;
// Email the admins
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
if local_site.reports_email_admins {
send_new_report_email_to_admins(
&post_report_view.creator.name,

View file

@ -22,7 +22,7 @@ pub async fn resolve_post_report(
let person_id = local_user_view.person.id;
check_community_mod_action(
&local_user_view.person,
&local_user_view,
&report.community,
true,
&mut context.pool(),

View file

@ -7,13 +7,12 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{
source::{
local_site::LocalSite,
private_message::PrivateMessage,
private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
},
traits::{Crud, Reportable},
};
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView};
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView, SiteView};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
pub async fn create_pm_report(
@ -47,7 +46,7 @@ pub async fn create_pm_report(
PrivateMessageReportView::read(&mut context.pool(), report.id).await?;
// Email the admins
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
if local_site.reports_email_admins {
send_new_report_email_to_admins(
&private_message_report_view.creator.name,

View file

@ -32,7 +32,7 @@ pub async fn leave_admin(
admins_only: Some(true),
..Default::default()
}
.list(&mut context.pool())
.list(local_user_view.person.instance_id, &mut context.pool())
.await?;
if admins.len() == 1 {
Err(LemmyErrorType::CannotLeaveAdmin)?
@ -67,7 +67,7 @@ pub async fn leave_admin(
admins_only: Some(true),
..Default::default()
}
.list(&mut context.pool())
.list(site_view.instance.id, &mut context.pool())
.await?;
let all_languages = Language::read_all(&mut context.pool()).await?;

View file

@ -4,10 +4,10 @@ use lemmy_api_common::{
site::{GetModlog, GetModlogResponse},
utils::{check_community_mod_of_any_or_admin_action, check_private_instance},
};
use lemmy_db_schema::{source::local_site::LocalSite, traits::PaginationCursorBuilder};
use lemmy_db_schema::traits::PaginationCursorBuilder;
use lemmy_db_views::{
combined::modlog_combined_view::ModlogCombinedQuery,
structs::{LocalUserView, ModlogCombinedView},
structs::{LocalUserView, ModlogCombinedView, SiteView},
};
use lemmy_utils::error::LemmyResult;
@ -16,7 +16,7 @@ pub async fn get_mod_log(
context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>,
) -> LemmyResult<Json<GetModlogResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
check_private_instance(&local_user_view, &local_site)?;

View file

@ -1,4 +1,3 @@
use crate::ban_nonlocal_user_from_local_communities;
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
@ -10,11 +9,12 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{
source::{
instance::{InstanceActions, InstanceBanForm},
local_user::LocalUser,
mod_log::admin::{AdminPurgePerson, AdminPurgePersonForm},
person::{Person, PersonUpdateForm},
person::Person,
},
traits::Crud,
traits::{Bannable, Crud},
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyResult;
@ -37,28 +37,25 @@ pub async fn purge_person(
let person = Person::read(&mut context.pool(), data.person_id).await?;
ban_nonlocal_user_from_local_communities(
&local_user_view,
&person,
true,
&data.reason,
&Some(true),
&None,
ActivityChannel::submit_activity(
SendActivityData::BanFromSite {
moderator: local_user_view.person.clone(),
banned_user: person.clone(),
reason: data.reason.clone(),
remove_or_restore_data: Some(true),
ban: true,
expires: None,
},
&context,
)
.await?;
)?;
// Clear profile data.
purge_user_account(data.person_id, &context).await?;
// Keep person record, but mark as banned to prevent login or refetching from home instance.
let person = Person::update(
InstanceActions::ban(
&mut context.pool(),
data.person_id,
&PersonUpdateForm {
banned: Some(true),
..Default::default()
},
&InstanceBanForm::new(data.person_id, local_user_view.person.instance_id, None),
)
.await?;

View file

@ -5,10 +5,9 @@ use lemmy_api_common::{
site::{ListRegistrationApplications, ListRegistrationApplicationsResponse},
utils::is_admin,
};
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views::{
registration_applications::registration_application_view::RegistrationApplicationQuery,
structs::LocalUserView,
structs::{LocalUserView, SiteView},
};
use lemmy_utils::error::LemmyResult;
@ -18,7 +17,7 @@ pub async fn list_registration_applications(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<ListRegistrationApplicationsResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
// Make sure user is an admin
is_admin(&local_user_view)?;

View file

@ -54,8 +54,6 @@ async fn create_test_site(context: &Data<LemmyContext>) -> LemmyResult<(Instance
)
.await?;
let admin_local_user_view = LocalUserView::read_person(pool, admin_person.id).await?;
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
let site = Site::create(pool, &site_form).await?;
@ -75,6 +73,8 @@ async fn create_test_site(context: &Data<LemmyContext>) -> LemmyResult<(Instance
let rate_limit_form = LocalSiteRateLimitInsertForm::new(local_site.id);
LocalSiteRateLimit::create(pool, &rate_limit_form).await?;
let admin_local_user_view = LocalUserView::read_person(pool, admin_person.id).await?;
Ok((inserted_instance, admin_local_user_view))
}

View file

@ -5,15 +5,14 @@ use lemmy_api_common::{
site::GetUnreadRegistrationApplicationCountResponse,
utils::is_admin,
};
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView, SiteView};
use lemmy_utils::error::LemmyResult;
pub async fn get_unread_registration_application_count(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<GetUnreadRegistrationApplicationCountResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
// Only let admins do this
is_admin(&local_user_view)?;

View file

@ -46,7 +46,7 @@ pub async fn build_community_response(
local_user_view: LocalUserView,
community_id: CommunityId,
) -> LemmyResult<Json<CommunityResponse>> {
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &local_user_view.person, community_id)
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &local_user_view, community_id)
.await
.is_ok();
let local_user = local_user_view.local_user;
@ -71,10 +71,10 @@ pub async fn build_post_response(
local_user_view: LocalUserView,
post_id: PostId,
) -> LemmyResult<Json<PostResponse>> {
let local_user = local_user_view.local_user;
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &local_user_view.person, community_id)
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &local_user_view, community_id)
.await
.is_ok();
let local_user = local_user_view.local_user;
let post_view = PostView::read(
&mut context.pool(),
post_id,

View file

@ -52,6 +52,7 @@ use lemmy_db_views::{
CommunityView,
LocalImageView,
LocalUserView,
PersonView,
SiteView,
},
};
@ -84,11 +85,11 @@ pub const AUTH_COOKIE_NAME: &str = "jwt";
pub async fn is_mod_or_admin(
pool: &mut DbPool<'_>,
person: &Person,
local_user_view: &LocalUserView,
community_id: CommunityId,
) -> LemmyResult<()> {
check_user_valid(person)?;
CommunityView::check_is_mod_or_admin(pool, person.id, community_id).await
check_local_user_valid(local_user_view)?;
CommunityView::check_is_mod_or_admin(pool, local_user_view.person.id, community_id).await
}
pub async fn is_mod_or_admin_opt(
@ -98,7 +99,7 @@ pub async fn is_mod_or_admin_opt(
) -> LemmyResult<()> {
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, community_id).await
is_mod_or_admin(pool, local_user_view, community_id).await
} else {
is_admin(local_user_view)
}
@ -116,12 +117,12 @@ pub async fn check_community_mod_of_any_or_admin_action(
) -> LemmyResult<()> {
let person = &local_user_view.person;
check_user_valid(person)?;
check_local_user_valid(local_user_view)?;
CommunityView::check_is_mod_of_any_or_admin(pool, person.id).await
}
pub fn is_admin(local_user_view: &LocalUserView) -> LemmyResult<()> {
check_user_valid(&local_user_view.person)?;
check_local_user_valid(local_user_view)?;
if !local_user_view.local_user.admin {
Err(LemmyErrorType::NotAnAdmin)?
} else {
@ -133,7 +134,7 @@ pub fn is_top_mod(
local_user_view: &LocalUserView,
community_mods: &[CommunityModeratorView],
) -> LemmyResult<()> {
check_user_valid(&local_user_view.person)?;
check_local_user_valid(local_user_view)?;
if local_user_view.person.id
!= community_mods
.first()
@ -159,13 +160,25 @@ pub async fn update_read_comments(
Ok(())
}
pub fn check_user_valid(person: &Person) -> LemmyResult<()> {
pub fn check_local_user_valid(local_user_view: &LocalUserView) -> LemmyResult<()> {
// Check for a site ban
if person.banned {
if local_user_view.banned() {
Err(LemmyErrorType::SiteBan)?
}
// check for account deletion
else if person.deleted {
else if local_user_view.person.deleted {
Err(LemmyErrorType::Deleted)?
} else {
Ok(())
}
}
pub fn check_person_valid(person_view: &PersonView) -> LemmyResult<()> {
// Check for a site ban
if person_view.banned() {
Err(LemmyErrorType::SiteBan)?
}
// check for account deletion
else if person_view.person.deleted {
Err(LemmyErrorType::Deleted)?
} else {
Ok(())
@ -217,14 +230,16 @@ pub async fn check_registration_application(
/// 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,
local_user_view: &LocalUserView,
community: &Community,
pool: &mut DbPool<'_>,
) -> LemmyResult<()> {
check_user_valid(person)?;
check_local_user_valid(local_user_view)?;
check_community_deleted_removed(community)?;
CommunityPersonBanView::check(pool, person.id, community.id).await?;
CommunityFollowerView::check_private_community_action(pool, person.id, community).await?;
CommunityPersonBanView::check(pool, local_user_view.person.id, community.id).await?;
CommunityFollowerView::check_private_community_action(pool, local_user_view.person.id, community)
.await?;
InstanceActions::check_ban(pool, local_user_view.person.id, community.instance_id).await?;
Ok(())
}
@ -240,13 +255,13 @@ pub fn check_community_deleted_removed(community: &Community) -> LemmyResult<()>
/// 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,
local_user_view: &LocalUserView,
community: &Community,
allow_deleted: bool,
pool: &mut DbPool<'_>,
) -> LemmyResult<()> {
is_mod_or_admin(pool, person, community.id).await?;
CommunityPersonBanView::check(pool, person.id, community.id).await?;
is_mod_or_admin(pool, local_user_view, community.id).await?;
CommunityPersonBanView::check(pool, local_user_view.person.id, community.id).await?;
// it must be possible to restore deleted community
if !allow_deleted {
@ -403,7 +418,7 @@ pub async fn send_email_to_user(
body: &str,
settings: &Settings,
) {
if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
if local_user_view.banned() || !local_user_view.local_user.send_notifications_to_email {
return;
}
@ -540,7 +555,10 @@ pub async fn slur_regex(context: &LemmyContext) -> LemmyResult<Regex> {
Ok(
CACHE
.try_get_with((), async {
let local_site = LocalSite::read(&mut context.pool()).await.ok();
let local_site = SiteView::read_local(&mut context.pool())
.await
.ok()
.map(|s| s.local_site);
build_and_check_regex(local_site.and_then(|s| s.slur_filter_regex).as_deref())
})
.await
@ -807,7 +825,7 @@ pub async fn remove_or_restore_user_data(
// Posts
let removed_or_restored_posts =
Post::update_removed_for_creator(pool, banned_person_id, None, removed).await?;
Post::update_removed_for_creator(pool, banned_person_id, None, None, removed).await?;
create_modlog_entries_for_removed_or_restored_posts(
pool,
mod_person_id,
@ -891,7 +909,8 @@ pub async fn remove_or_restore_user_data_in_community(
) -> LemmyResult<()> {
// Posts
let posts =
Post::update_removed_for_creator(pool, banned_person_id, Some(community_id), remove).await?;
Post::update_removed_for_creator(pool, banned_person_id, Some(community_id), None, remove)
.await?;
create_modlog_entries_for_removed_or_restored_posts(
pool,
mod_person_id,
@ -1150,7 +1169,7 @@ pub 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)?;
check_local_user_valid(&local_user_view)?;
Ok(local_user_view)
}

View file

@ -59,16 +59,11 @@ pub async fn create_comment(
let post = post_view.post;
let community_id = post_view.community.id;
check_community_user_action(
&local_user_view.person,
&post_view.community,
&mut context.pool(),
)
.await?;
check_community_user_action(&local_user_view, &post_view.community, &mut context.pool()).await?;
check_post_deleted_or_removed(&post)?;
// Check if post is locked, no new comments
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &local_user_view.person, community_id)
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &local_user_view, community_id)
.await
.is_ok();
if post.locked && !is_mod_or_admin {

View file

@ -34,7 +34,7 @@ pub async fn delete_comment(
}
check_community_user_action(
&local_user_view.person,
&local_user_view,
&orig_comment.community,
&mut context.pool(),
)

View file

@ -5,8 +5,7 @@ use lemmy_api_common::{
context::LemmyContext,
utils::check_private_instance,
};
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::error::LemmyResult;
pub async fn get_comment(
@ -14,7 +13,7 @@ pub async fn get_comment(
context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>,
) -> LemmyResult<Json<CommentResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
check_private_instance(&local_user_view, &local_site)?;

View file

@ -34,7 +34,7 @@ pub async fn remove_comment(
.await?;
check_community_mod_action(
&local_user_view.person,
&local_user_view,
&orig_comment.community,
false,
&mut context.pool(),

View file

@ -35,7 +35,7 @@ pub async fn update_comment(
.await?;
check_community_user_action(
&local_user_view.person,
&local_user_view,
&orig_comment.community,
&mut context.pool(),
)

View file

@ -24,13 +24,7 @@ pub async fn delete_community(
CommunityModeratorView::for_community(&mut context.pool(), data.community_id).await?;
let community = Community::read(&mut context.pool(), data.community_id).await?;
check_community_mod_action(
&local_user_view.person,
&community,
true,
&mut context.pool(),
)
.await?;
check_community_mod_action(&local_user_view, &community, true, &mut context.pool()).await?;
// Make sure deleter is the top mod
is_top_mod(&local_user_view, &community_mods)?;

View file

@ -24,13 +24,7 @@ pub async fn remove_community(
local_user_view: LocalUserView,
) -> LemmyResult<Json<CommunityResponse>> {
let community = Community::read(&mut context.pool(), data.community_id).await?;
check_community_mod_action(
&local_user_view.person,
&community,
true,
&mut context.pool(),
)
.await?;
check_community_mod_action(&local_user_view, &community, true, &mut context.pool()).await?;
// Verify its an admin (only an admin can remove a community)
is_admin(&local_user_view)?;

View file

@ -19,12 +19,11 @@ use lemmy_db_schema::{
source::{
actor_language::{CommunityLanguage, SiteLanguage},
community::{Community, CommunityUpdateForm},
local_site::LocalSite,
},
traits::Crud,
utils::diesel_string_update,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::{slurs::check_slurs_opt, validation::is_valid_body_field},
@ -35,7 +34,7 @@ pub async fn update_community(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<CommunityResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
let slur_regex = slur_regex(&context).await?;
let url_blocklist = get_url_blocklist(&context).await?;
@ -58,13 +57,7 @@ pub async fn update_community(
let old_community = Community::read(&mut context.pool(), data.community_id).await?;
// Verify its a mod (only mods can edit it)
check_community_mod_action(
&local_user_view.person,
&old_community,
false,
&mut context.pool(),
)
.await?;
check_community_mod_action(&local_user_view, &old_community, false, &mut context.pool()).await?;
let community_id = data.community_id;
if let Some(languages) = data.discussion_languages.clone() {

View file

@ -24,13 +24,12 @@ use lemmy_db_schema::{
newtypes::PostOrCommentId,
source::{
community::Community,
local_site::LocalSite,
post::{Post, PostActions, PostInsertForm, PostLikeForm, PostReadForm},
},
traits::{Crud, Likeable, Readable},
utils::diesel_url_create,
};
use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView};
use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView, SiteView};
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::{
@ -52,7 +51,7 @@ pub async fn create_post(
local_user_view: LocalUserView,
) -> LemmyResult<Json<PostResponse>> {
honeypot_check(&data.honeypot)?;
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
let slur_regex = slur_regex(&context).await?;
check_slurs(&data.name, &slur_regex)?;
@ -83,7 +82,7 @@ pub async fn create_post(
}
let community = Community::read(&mut context.pool(), data.community_id).await?;
check_community_user_action(&local_user_view.person, &community, &mut context.pool()).await?;
check_community_user_action(&local_user_view, &community, &mut context.pool()).await?;
// If its an NSFW community, then use that as a default
let nsfw = data.nsfw.or(Some(community.nsfw));

View file

@ -31,7 +31,7 @@ pub async fn delete_post(
}
let community = Community::read(&mut context.pool(), orig_post.community_id).await?;
check_community_user_action(&local_user_view.person, &community, &mut context.pool()).await?;
check_community_user_action(&local_user_view, &community, &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) {

View file

@ -33,13 +33,7 @@ pub async fn remove_post(
let orig_post = Post::read(&mut context.pool(), post_id).await?;
let community = Community::read(&mut context.pool(), orig_post.community_id).await?;
check_community_mod_action(
&local_user_view.person,
&community,
false,
&mut context.pool(),
)
.await?;
check_community_mod_action(&local_user_view, &community, false, &mut context.pool()).await?;
LocalUser::is_higher_mod_or_admin_check(
&mut context.pool(),

View file

@ -23,13 +23,12 @@ use lemmy_db_schema::{
newtypes::PostOrCommentId,
source::{
community::Community,
local_site::LocalSite,
post::{Post, PostUpdateForm},
},
traits::Crud,
utils::{diesel_string_update, diesel_url_update},
};
use lemmy_db_views::structs::{LocalUserView, PostView};
use lemmy_db_views::structs::{LocalUserView, PostView, SiteView};
use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::{
@ -51,7 +50,7 @@ pub async fn update_post(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<PostResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
let url = diesel_url_update(data.url.as_deref())?;
let custom_thumbnail = diesel_url_update(data.custom_thumbnail.as_deref())?;
@ -95,12 +94,7 @@ pub async fn update_post(
let post_id = data.post_id;
let orig_post = PostView::read(&mut context.pool(), post_id, None, false).await?;
check_community_user_action(
&local_user_view.person,
&orig_post.community,
&mut context.pool(),
)
.await?;
check_community_user_action(&local_user_view, &orig_post.community, &mut context.pool()).await?;
// Verify that only the creator can edit
if !Post::is_post_creator(local_user_view.person.id, orig_post.post.creator_id) {

View file

@ -45,7 +45,7 @@ pub async fn create_site(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<SiteResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
// Make sure user is an admin; other types of users should not create site data...
is_admin(&local_user_view)?;

View file

@ -54,7 +54,7 @@ async fn read_site(context: &LemmyContext) -> LemmyResult<GetSiteResponse> {
admins_only: Some(true),
..Default::default()
}
.list(&mut context.pool())
.list(site_view.instance.id, &mut context.pool())
.await?;
let all_languages = Language::read_all(&mut context.pool()).await?;
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;

View file

@ -8,8 +8,8 @@ use lemmy_api_common::{
person::{LoginResponse, Register},
utils::{
check_email_verified,
check_local_user_valid,
check_registration_application,
check_user_valid,
generate_inbox_url,
honeypot_check,
password_length_check,
@ -281,7 +281,7 @@ pub async fn authenticate_with_oauth(
// user found by oauth_user_id => Login user
let local_user = user_view.clone().local_user;
check_user_valid(&user_view.person)?;
check_local_user_valid(&user_view)?;
check_email_verified(&user_view, &site_view)?;
check_registration_application(&user_view, &site_view.local_site, pool).await?;
local_user
@ -318,7 +318,7 @@ pub async fn authenticate_with_oauth(
// users who signed up before the switch could have accounts with unverified emails falsely
// marked as verified.
check_user_valid(&user_view.person)?;
check_local_user_valid(&user_view)?;
check_email_verified(&user_view, &site_view)?;
check_registration_application(&user_view, &site_view.local_site, pool).await?;

View file

@ -1,5 +1,5 @@
use actix_web::web::{Data, Json};
use lemmy_api_common::{context::LemmyContext, site::MyUserInfo, utils::check_user_valid};
use lemmy_api_common::{context::LemmyContext, site::MyUserInfo, utils::check_local_user_valid};
use lemmy_db_schema::{
source::{
actor_language::LocalUserLanguage,
@ -16,7 +16,7 @@ pub async fn get_my_user(
local_user_view: LocalUserView,
context: Data<LemmyContext>,
) -> LemmyResult<Json<MyUserInfo>> {
check_user_valid(&local_user_view.person)?;
check_local_user_valid(&local_user_view)?;
// Build the local user with parallel queries and add it to site response
let person_id = local_user_view.person.id;

View file

@ -1,4 +1,4 @@
use super::to;
use super::{to, update_removed_for_instance};
use crate::{
activities::{
block::{generate_cc, SiteOrCommunity},
@ -18,10 +18,8 @@ use crate::{
use activitypub_federation::{
config::Data,
kinds::activity::BlockType,
protocol::verification::verify_domains_match,
traits::{ActivityHandler, Actor},
};
use anyhow::anyhow;
use chrono::{DateTime, Utc};
use lemmy_api_common::{
context::LemmyContext,
@ -31,12 +29,12 @@ use lemmy_db_schema::{
source::{
activity::ActivitySendTargets,
community::{CommunityActions, CommunityPersonBanForm},
instance::{InstanceActions, InstanceBanForm},
mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
person::{Person, PersonUpdateForm},
},
traits::{Bannable, Crud},
};
use lemmy_utils::error::{FederationError, LemmyError, LemmyResult};
use lemmy_utils::error::{LemmyError, LemmyResult};
use url::Url;
impl BlockUser {
@ -116,21 +114,8 @@ impl ActivityHandler for BlockUser {
async fn verify(&self, context: &Data<LemmyContext>) -> LemmyResult<()> {
match self.target.dereference(context).await? {
SiteOrCommunity::Site(site) => {
SiteOrCommunity::Site(_site) => {
verify_is_public(&self.to, &self.cc)?;
let domain = self
.object
.inner()
.domain()
.ok_or(FederationError::UrlWithoutDomain)?;
if context.settings().hostname == domain {
return Err(
anyhow!("Site bans from remote instance can't affect user's home instance").into(),
);
}
// site ban can only target a user who is on the same instance as the actor (admin)
verify_domains_match(&site.id(), self.actor.inner())?;
verify_domains_match(&site.id(), self.object.inner())?;
}
SiteOrCommunity::Community(community) => {
verify_visibility(&self.to, &self.cc, &community)?;
@ -148,21 +133,20 @@ impl ActivityHandler for BlockUser {
let blocked_person = self.object.dereference(context).await?;
let target = self.target.dereference(context).await?;
let reason = self.summary;
let pool = &mut context.pool();
match target {
SiteOrCommunity::Site(_site) => {
let blocked_person = Person::update(
&mut context.pool(),
blocked_person.id,
&PersonUpdateForm {
banned: Some(true),
ban_expires: Some(expires),
..Default::default()
},
)
.await?;
SiteOrCommunity::Site(site) => {
let form = InstanceBanForm::new(blocked_person.id, site.instance_id, expires);
InstanceActions::ban(pool, &form).await?;
if self.remove_data.unwrap_or(false) {
remove_or_restore_user_data(mod_person.id, blocked_person.id, true, &reason, context)
.await?;
if blocked_person.instance_id == site.instance_id {
// user banned from home instance, remove all content
remove_or_restore_user_data(mod_person.id, blocked_person.id, true, &reason, context)
.await?;
} else {
update_removed_for_instance(&blocked_person, &site, true, pool).await?;
}
}
// write mod log
@ -172,6 +156,7 @@ impl ActivityHandler for BlockUser {
reason,
banned: Some(true),
expires,
instance_id: site.instance_id,
};
ModBan::create(&mut context.pool(), &form).await?;
}

View file

@ -20,7 +20,7 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{
newtypes::CommunityId,
source::{community::Community, person::Person, site::Site},
source::{comment::Comment, community::Community, person::Person, post::Post, site::Site},
traits::Crud,
utils::DbPool,
};
@ -139,32 +139,27 @@ pub(crate) async fn send_ban_from_site(
let site = SiteOrCommunity::Site(Site::read_local(&mut context.pool()).await?.into());
let expires = check_expire_time(expires)?;
// if the action affects a local user, federate to other instances
if banned_user.local {
if ban {
BlockUser::send(
&site,
&banned_user.into(),
&moderator.into(),
remove_or_restore_data.unwrap_or(false),
reason.clone(),
expires,
&context,
)
.await
} else {
UndoBlockUser::send(
&site,
&banned_user.into(),
&moderator.into(),
remove_or_restore_data.unwrap_or(false),
reason.clone(),
&context,
)
.await
}
if ban {
BlockUser::send(
&site,
&banned_user.into(),
&moderator.into(),
remove_or_restore_data.unwrap_or(false),
reason.clone(),
expires,
&context,
)
.await
} else {
Ok(())
UndoBlockUser::send(
&site,
&banned_user.into(),
&moderator.into(),
remove_or_restore_data.unwrap_or(false),
reason.clone(),
&context,
)
.await
}
}
@ -211,3 +206,29 @@ fn to(target: &SiteOrCommunity) -> LemmyResult<Vec<Url>> {
vec![public()]
})
}
// user banned from remote instance, remove content only in communities from that
// instance
async fn update_removed_for_instance(
blocked_person: &Person,
site: &ApubSite,
removed: bool,
pool: &mut DbPool<'_>,
) -> LemmyResult<()> {
Post::update_removed_for_creator(
pool,
blocked_person.id,
None,
Some(site.instance_id),
removed,
)
.await?;
Comment::update_removed_for_creator_and_instance(
pool,
blocked_person.id,
site.instance_id,
removed,
)
.await?;
Ok(())
}

View file

@ -1,4 +1,4 @@
use super::to;
use super::{to, update_removed_for_instance};
use crate::{
activities::{
block::{generate_cc, SiteOrCommunity},
@ -27,8 +27,8 @@ use lemmy_db_schema::{
source::{
activity::ActivitySendTargets,
community::{CommunityActions, CommunityPersonBanForm},
instance::{InstanceActions, InstanceBanForm},
mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
person::{Person, PersonUpdateForm},
},
traits::{Bannable, Crud},
};
@ -99,23 +99,21 @@ impl ActivityHandler for UndoBlockUser {
let expires = self.object.end_time;
let mod_person = self.actor.dereference(context).await?;
let blocked_person = self.object.object.dereference(context).await?;
let pool = &mut context.pool();
match self.object.target.dereference(context).await? {
SiteOrCommunity::Site(_site) => {
SiteOrCommunity::Site(site) => {
verify_is_public(&self.to, &self.cc)?;
let blocked_person = Person::update(
&mut context.pool(),
blocked_person.id,
&PersonUpdateForm {
banned: Some(false),
ban_expires: Some(expires),
..Default::default()
},
)
.await?;
let form = InstanceBanForm::new(blocked_person.id, site.instance_id, expires);
InstanceActions::unban(pool, &form).await?;
if self.restore_data.unwrap_or(false) {
remove_or_restore_user_data(mod_person.id, blocked_person.id, false, &None, context)
.await?;
if blocked_person.instance_id == site.instance_id {
// user unbanned from home instance, restore all content
remove_or_restore_user_data(mod_person.id, blocked_person.id, false, &None, context)
.await?;
} else {
update_removed_for_instance(&blocked_person, &site, false, pool).await?;
}
}
// write mod log
@ -125,6 +123,7 @@ impl ActivityHandler for UndoBlockUser {
reason: self.object.summary,
banned: Some(false),
expires,
instance_id: site.instance_id,
};
ModBan::create(&mut context.pool(), &form).await?;
}

View file

@ -25,7 +25,7 @@ use activitypub_federation::{
use lemmy_api_common::{
build_response::send_local_notifs,
context::LemmyContext,
utils::{check_post_deleted_or_removed, is_mod_or_admin},
utils::check_post_deleted_or_removed,
};
use lemmy_db_schema::{
newtypes::{PersonId, PostOrCommentId},
@ -38,6 +38,7 @@ use lemmy_db_schema::{
},
traits::{Crud, Likeable},
};
use lemmy_db_views::structs::CommunityView;
use lemmy_utils::{
error::{LemmyError, LemmyResult},
utils::mention::scrape_text_for_mentions,
@ -144,7 +145,8 @@ 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, post.community_id).await?;
CommunityView::check_is_mod_or_admin(&mut context.pool(), creator.id, post.community_id)
.await?;
}
}

View file

@ -38,6 +38,7 @@ use lemmy_db_schema::{
source::{
activity::{ActivitySendTargets, ActorType, SentActivity, SentActivityForm},
community::Community,
instance::InstanceActions,
},
traits::Crud,
CommunityVisibility,
@ -63,11 +64,8 @@ async fn verify_person(
context: &Data<LemmyContext>,
) -> LemmyResult<()> {
let person = person_id.dereference(context).await?;
if person.banned {
Err(FederationError::PersonIsBannedFromSite(person.ap_id.to_string()).into())
} else {
Ok(())
}
InstanceActions::check_ban(&mut context.pool(), person.id, person.instance_id).await?;
Ok(())
}
/// Fetches the person and community to verify their type, then checks if person is banned from site
@ -78,11 +76,7 @@ pub(crate) async fn verify_person_in_community(
context: &Data<LemmyContext>,
) -> LemmyResult<()> {
let person = person_id.dereference(context).await?;
if person.banned {
Err(FederationError::PersonIsBannedFromSite(
person.ap_id.to_string(),
))?
}
InstanceActions::check_ban(&mut context.pool(), person.id, person.instance_id).await?;
let person_id = person.id;
let community_id = community.id;
CommunityPersonBanView::check(&mut context.pool(), person_id, community_id).await

View file

@ -18,7 +18,8 @@ use activitypub_federation::{
traits::{ActivityHandler, Actor},
};
use lemmy_api_common::{context::LemmyContext, utils::check_bot_account};
use lemmy_db_schema::{source::local_site::LocalSite, FederationMode};
use lemmy_db_schema::FederationMode;
use lemmy_db_views::structs::SiteView;
use lemmy_utils::error::{LemmyError, LemmyResult};
use url::Url;
@ -65,8 +66,9 @@ impl ActivityHandler for Vote {
check_bot_account(&actor.0)?;
// Check for enabled federation votes
let local_site = LocalSite::read(&mut context.pool())
let local_site = SiteView::read_local(&mut context.pool())
.await
.map(|s| s.local_site)
.unwrap_or_default();
let (downvote_setting, upvote_setting) = match object {

View file

@ -6,12 +6,8 @@ use lemmy_api_common::{
context::LemmyContext,
utils::{check_private_instance, is_mod_or_admin_opt, read_site_for_actor},
};
use lemmy_db_schema::source::{
actor_language::CommunityLanguage,
community::Community,
local_site::LocalSite,
};
use lemmy_db_views::structs::{CommunityModeratorView, CommunityView, LocalUserView};
use lemmy_db_schema::source::{actor_language::CommunityLanguage, community::Community};
use lemmy_db_views::structs::{CommunityModeratorView, CommunityView, LocalUserView, SiteView};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
pub async fn get_community(
@ -19,7 +15,7 @@ pub async fn get_community(
context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>,
) -> LemmyResult<Json<GetCommunityResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
if data.name.is_none() && data.id.is_none() {
Err(LemmyErrorType::NoIdGiven)?

View file

@ -10,8 +10,15 @@ use lemmy_api_common::{
site::{ResolveObject, ResolveObjectResponse},
utils::check_private_instance,
};
use lemmy_db_schema::{source::local_site::LocalSite, utils::DbPool};
use lemmy_db_views::structs::{CommentView, CommunityView, LocalUserView, PersonView, PostView};
use lemmy_db_schema::utils::DbPool;
use lemmy_db_views::structs::{
CommentView,
CommunityView,
LocalUserView,
PersonView,
PostView,
SiteView,
};
use lemmy_utils::error::{LemmyErrorExt2, LemmyErrorType, LemmyResult};
pub async fn resolve_object(
@ -19,7 +26,7 @@ pub async fn resolve_object(
context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>,
) -> LemmyResult<Json<ResolveObjectResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
check_private_instance(&local_user_view, &local_site)?;
// If we get a valid personId back we can safely assume that the user is authenticated,
// if there's no personId then the JWT was missing or invalid.
@ -77,13 +84,12 @@ mod tests {
source::{
community::{Community, CommunityInsertForm},
instance::Instance,
local_site::{LocalSite, LocalSiteInsertForm},
local_site::LocalSite,
post::{Post, PostInsertForm, PostUpdateForm},
site::{Site, SiteInsertForm},
},
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views::{site::site_view::create_test_instance, structs::LocalUserView};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use serial_test::serial;
@ -93,6 +99,7 @@ mod tests {
async fn test_object_visibility() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let pool = &mut context.pool();
let instance = create_test_instance(pool).await?;
let name = "test_local_user_name";
let bio = "test_local_user_bio";
@ -101,21 +108,10 @@ mod tests {
let regular_user = LocalUserView::create_test_user(pool, name, bio, false).await?;
let admin_user = LocalUserView::create_test_user(pool, name, bio, true).await?;
let instance_id = creator.person.instance_id;
let site_form = SiteInsertForm::new("test site".to_string(), instance_id);
let site = Site::create(pool, &site_form).await?;
let local_site_form = LocalSiteInsertForm {
site_setup: Some(true),
private_instance: Some(false),
..LocalSiteInsertForm::new(site.id)
};
LocalSite::create(pool, &local_site_form).await?;
let community = Community::create(
pool,
&CommunityInsertForm::new(
instance_id,
instance.id,
"test".to_string(),
"test".to_string(),
"pubkey".to_string(),
@ -180,8 +176,7 @@ mod tests {
assert_eq!(res.post.as_ref().unwrap().post.ap_id, post.ap_id);
LocalSite::delete(pool).await?;
Site::delete(pool, site.id).await?;
Instance::delete(pool, instance_id).await?;
Instance::delete(pool, instance.id).await?;
Ok(())
}

View file

@ -288,11 +288,15 @@ pub(crate) mod tests {
CommunityFollowerState,
CommunityInsertForm,
},
instance::Instance,
person::Person,
},
traits::{Crud, Followable},
};
use lemmy_db_views::structs::{CommunityFollowerView, LocalUserView};
use lemmy_db_views::{
site::site_view::create_test_instance,
structs::{CommunityFollowerView, LocalUserView},
};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use serial_test::serial;
use std::time::Duration;
@ -303,6 +307,7 @@ pub(crate) mod tests {
async fn test_settings_export_import() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let pool = &mut context.pool();
let instance = create_test_instance(pool).await?;
let export_user = LocalUserView::create_test_user(pool, "hanna", "my bio", false).await?;
@ -344,6 +349,7 @@ pub(crate) mod tests {
Person::delete(pool, export_user.person.id).await?;
Person::delete(pool, import_user.person.id).await?;
Instance::delete(&mut context.pool(), instance.id).await?;
Ok(())
}
@ -352,6 +358,7 @@ pub(crate) mod tests {
async fn disallow_large_backup() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let pool = &mut context.pool();
let instance = create_test_instance(pool).await?;
let export_user = LocalUserView::create_test_user(pool, "harry", "harry bio", false).await?;
@ -380,6 +387,7 @@ pub(crate) mod tests {
Person::delete(pool, export_user.person.id).await?;
Person::delete(pool, import_user.person.id).await?;
Instance::delete(&mut context.pool(), instance.id).await?;
Ok(())
}
@ -388,6 +396,7 @@ pub(crate) mod tests {
async fn import_partial_backup() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let pool = &mut context.pool();
let instance = create_test_instance(pool).await?;
let import_user = LocalUserView::create_test_user(pool, "larry", "larry bio", false).await?;
@ -408,6 +417,7 @@ pub(crate) mod tests {
// local_user can be deserialized without id/person_id fields
assert_eq!("my_theme", import_user_updated.local_user.theme);
Instance::delete(&mut context.pool(), instance.id).await?;
Ok(())
}
}

View file

@ -105,7 +105,7 @@ mod tests {
},
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views::{site::site_view::create_test_instance, structs::LocalUserView};
use pretty_assertions::assert_eq;
use serial_test::serial;
@ -113,7 +113,7 @@ mod tests {
#[tokio::test]
async fn test_markdown_rewrite_remote_links() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let instance = Instance::read_or_create(&mut context.pool(), "example.com".to_string()).await?;
let instance = create_test_instance(&mut context.pool()).await?;
let community = Community::create(
&mut context.pool(),
&CommunityInsertForm::new(

View file

@ -9,6 +9,7 @@ use lemmy_db_schema::{
source::{activity::ReceivedActivity, instance::Instance, local_site::LocalSite},
utils::{ActualDbPool, DbPool},
};
use lemmy_db_views::structs::SiteView;
use lemmy_utils::{
error::{FederationError, LemmyError, LemmyErrorType, LemmyResult},
CacheLock,
@ -153,7 +154,7 @@ pub(crate) async fn local_site_data_cached(
lemmy_db_schema::try_join_with_pool!(pool => (
// LocalSite may be missing
|pool| async {
Ok(LocalSite::read(pool).await.ok())
Ok(SiteView::read_local(pool).await.ok().map(|s| s.local_site))
},
Instance::allowlist,
Instance::blocklist

View file

@ -23,7 +23,7 @@ use chrono::{DateTime, Utc};
use lemmy_api_common::{
context::LemmyContext,
plugins::{plugin_hook_after, plugin_hook_before},
utils::{get_url_blocklist, is_mod_or_admin, process_markdown, slur_regex},
utils::{get_url_blocklist, process_markdown, slur_regex},
};
use lemmy_db_schema::{
source::{
@ -34,6 +34,7 @@ use lemmy_db_schema::{
},
traits::Crud,
};
use lemmy_db_views::structs::CommunityView;
use lemmy_utils::{
error::{FederationError, LemmyError, LemmyResult},
utils::markdown::markdown_to_html,
@ -161,9 +162,10 @@ impl Object for ApubComment {
let (post, _) = Box::pin(note.get_parents(context)).await?;
let creator = Box::pin(note.attributed_to.dereference(context)).await?;
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &creator, community.id)
.await
.is_ok();
let is_mod_or_admin =
CommunityView::check_is_mod_or_admin(&mut context.pool(), creator.id, community.id)
.await
.is_ok();
if post.locked && !is_mod_or_admin {
Err(FederationError::PostIsLocked)?
} else {
@ -233,7 +235,8 @@ pub(crate) mod tests {
};
use assert_json_diff::assert_json_include;
use html2md::parse_html;
use lemmy_db_schema::source::{local_site::LocalSite, site::Site};
use lemmy_db_schema::source::{instance::Instance, local_site::LocalSite, site::Site};
use lemmy_db_views::site::site_view::create_test_instance;
use pretty_assertions::assert_eq;
use serial_test::serial;
@ -267,6 +270,7 @@ pub(crate) mod tests {
#[serial]
pub(crate) async fn test_parse_lemmy_comment() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let instance = create_test_instance(&mut context.pool()).await?;
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741")?;
let data = prepare_comment_test(&url, &context).await?;
@ -285,6 +289,7 @@ pub(crate) mod tests {
Comment::delete(&mut context.pool(), comment_id).await?;
cleanup(data, &context).await?;
Instance::delete(&mut context.pool(), instance.id).await?;
Ok(())
}
@ -292,6 +297,7 @@ pub(crate) mod tests {
#[serial]
async fn test_parse_pleroma_comment() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let instance = create_test_instance(&mut context.pool()).await?;
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741")?;
let data = prepare_comment_test(&url, &context).await?;
@ -311,6 +317,7 @@ pub(crate) mod tests {
Comment::delete(&mut context.pool(), comment.id).await?;
cleanup(data, &context).await?;
Instance::delete(&mut context.pool(), instance.id).await?;
Ok(())
}

View file

@ -39,11 +39,11 @@ use lemmy_db_schema::{
activity::ActorType,
actor_language::CommunityLanguage,
community::{Community, CommunityInsertForm, CommunityUpdateForm},
local_site::LocalSite,
},
traits::{ApubActor, Crud},
CommunityVisibility,
};
use lemmy_db_views::structs::SiteView;
use lemmy_utils::{
error::{LemmyError, LemmyResult},
spawn_try_task,
@ -144,7 +144,10 @@ impl Object for ApubCommunity {
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
async fn from_json(group: Group, context: &Data<Self::DataType>) -> LemmyResult<ApubCommunity> {
let local_site = LocalSite::read(&mut context.pool()).await.ok();
let local_site = SiteView::read_local(&mut context.pool())
.await
.ok()
.map(|s| s.local_site);
let instance_id = fetch_instance_actor_for_object(&group.id, context).await?;
let slur_regex = slur_regex(context).await?;

View file

@ -152,8 +152,6 @@ impl Object for ApubPerson {
let person_form = PersonInsertForm {
name: person.preferred_username,
display_name,
banned: None,
ban_expires: None,
deleted: Some(false),
avatar,
banner,

View file

@ -40,13 +40,12 @@ use lemmy_api_common::{
use lemmy_db_schema::{
source::{
community::Community,
local_site::LocalSite,
person::Person,
post::{Post, PostInsertForm, PostUpdateForm},
},
traits::Crud,
};
use lemmy_db_views::structs::CommunityModeratorView;
use lemmy_db_views::structs::{CommunityModeratorView, SiteView};
use lemmy_utils::{
error::{LemmyError, LemmyResult},
spawn_try_task,
@ -185,7 +184,10 @@ impl Object for ApubPost {
}
async fn from_json(page: Page, context: &Data<Self::DataType>) -> LemmyResult<ApubPost> {
let local_site = LocalSite::read(&mut context.pool()).await.ok();
let local_site = SiteView::read_local(&mut context.pool())
.await
.ok()
.map(|s| s.local_site);
let creator = page.creator()?.dereference(context).await?;
let community = page.community(context).await?;

View file

@ -23,7 +23,7 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{
source::{
instance::Instance,
instance::{Instance, InstanceActions},
person::{Person, PersonActions},
private_message::{PrivateMessage as DbPrivateMessage, PrivateMessageInsertForm},
},
@ -31,7 +31,7 @@ use lemmy_db_schema::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{
error::{FederationError, LemmyError, LemmyErrorType, LemmyResult},
error::{LemmyError, LemmyErrorType, LemmyResult},
utils::markdown::markdown_to_html,
};
use semver::{Version, VersionReq};
@ -123,13 +123,8 @@ impl Object for ApubPrivateMessage {
check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?;
let person = note.attributed_to.dereference(context).await?;
if person.banned {
Err(FederationError::PersonIsBannedFromSite(
person.ap_id.to_string(),
))?
} else {
Ok(())
}
InstanceActions::check_ban(&mut context.pool(), person.id, person.instance_id).await?;
Ok(())
}
async fn from_json(
@ -184,6 +179,7 @@ mod tests {
};
use assert_json_diff::assert_json_include;
use lemmy_db_schema::source::site::Site;
use lemmy_db_views::site::site_view::create_test_instance;
use pretty_assertions::assert_eq;
use serial_test::serial;
@ -217,6 +213,7 @@ mod tests {
#[serial]
async fn test_parse_lemmy_pm() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let instance = create_test_instance(&mut context.pool()).await?;
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621")?;
let data = prepare_comment_test(&url, &context).await?;
let json: PrivateMessage = file_to_json_object("assets/lemmy/objects/private_message.json")?;
@ -233,6 +230,7 @@ mod tests {
DbPrivateMessage::delete(&mut context.pool(), pm_id).await?;
cleanup(data, &context).await?;
Instance::delete(&mut context.pool(), instance.id).await?;
Ok(())
}
@ -240,6 +238,7 @@ mod tests {
#[serial]
async fn test_parse_pleroma_pm() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let instance = create_test_instance(&mut context.pool()).await?;
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621")?;
let data = prepare_comment_test(&url, &context).await?;
let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2")?;
@ -253,6 +252,7 @@ mod tests {
DbPrivateMessage::delete(&mut context.pool(), pm.id).await?;
cleanup(data, &context).await?;
Instance::delete(&mut context.pool(), instance.id).await?;
Ok(())
}
}

View file

@ -1,7 +1,7 @@
use crate::{
diesel::{DecoratableTarget, OptionalExtension},
newtypes::{CommentId, DbUrl, PersonId},
schema::{comment, comment_actions},
newtypes::{CommentId, DbUrl, InstanceId, PersonId},
schema::{comment, comment_actions, post},
source::comment::{
Comment,
CommentActions,
@ -24,6 +24,7 @@ use diesel::{
dsl::insert_into,
expression::SelectableHelper,
result::Error,
update,
ExpressionMethods,
QueryDsl,
};
@ -67,6 +68,35 @@ impl Comment {
.await
}
pub async fn update_removed_for_creator_and_instance(
pool: &mut DbPool<'_>,
creator_id: PersonId,
instance_id: InstanceId,
removed: bool,
) -> Result<Vec<CommentId>, Error> {
let conn = &mut get_conn(pool).await?;
// Diesel can't update from join unfortunately, so you'll need to loop over these
let comment_ids = comment::table
.inner_join(post::table)
.filter(comment::creator_id.eq(creator_id))
.filter(post::instance_id.eq(instance_id))
.select(comment::id)
.load::<CommentId>(conn)
.await?;
let form = &CommentUpdateForm {
removed: Some(removed),
..Default::default()
};
update(comment::table)
.filter(comment::id.eq_any(comment_ids.clone()))
.set(form)
.execute(conn)
.await?;
Ok(comment_ids)
}
pub async fn create(
pool: &mut DbPool<'_>,
comment_form: &CommentInsertForm,

View file

@ -12,9 +12,9 @@ use crate::{
},
source::{
federation_queue_state::FederationQueueState,
instance::{Instance, InstanceActions, InstanceBlockForm, InstanceForm},
instance::{Instance, InstanceActions, InstanceBanForm, InstanceBlockForm, InstanceForm},
},
traits::Blockable,
traits::{Bannable, Blockable},
utils::{
functions::{coalesce, lower},
get_conn,
@ -249,3 +249,53 @@ impl Blockable for InstanceActions {
.await
}
}
impl InstanceActions {
pub async fn check_ban(
pool: &mut DbPool<'_>,
person_id: PersonId,
instance_id: InstanceId,
) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?;
let ban_exists = select(exists(
instance_actions::table
.filter(instance_actions::person_id.eq(person_id))
.filter(instance_actions::instance_id.eq(instance_id))
.filter(instance_actions::received_ban.is_not_null()),
))
.get_result::<bool>(conn)
.await?;
if ban_exists {
return Err(LemmyErrorType::SiteBan.into());
}
Ok(())
}
}
impl Bannable for InstanceActions {
type Form = InstanceBanForm;
async fn ban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
Ok(
insert_into(instance_actions::table)
.values(form)
.on_conflict((instance_actions::person_id, instance_actions::instance_id))
.do_update()
.set(form)
.returning(Self::as_select())
.get_result::<Self>(conn)
.await?,
)
}
async fn unban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
let conn = &mut get_conn(pool).await?;
Ok(
uplete::new(instance_actions::table.find((form.person_id, form.instance_id)))
.set_null(instance_actions::received_ban)
.set_null(instance_actions::ban_expires)
.get_result(conn)
.await?,
)
}
}

View file

@ -5,8 +5,6 @@ use crate::{
};
use diesel::{dsl::insert_into, result::Error};
use diesel_async::RunQueryDsl;
use lemmy_utils::{build_cache, error::LemmyResult, CacheLock};
use std::sync::LazyLock;
impl LocalSite {
pub async fn create(pool: &mut DbPool<'_>, form: &LocalSiteInsertForm) -> Result<Self, Error> {
@ -16,17 +14,14 @@ impl LocalSite {
.get_result::<Self>(conn)
.await
}
pub async fn read(pool: &mut DbPool<'_>) -> LemmyResult<Self> {
static CACHE: CacheLock<LocalSite> = LazyLock::new(build_cache);
Ok(
CACHE
.try_get_with((), async {
let conn = &mut get_conn(pool).await?;
local_site::table.first(conn).await
})
.await?,
)
/// Only used for tests
#[cfg(test)]
async fn read(pool: &mut DbPool<'_>) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
local_site::table.first(conn).await
}
pub async fn update(pool: &mut DbPool<'_>, form: &LocalSiteUpdateForm) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
diesel::update(local_site::table)
@ -56,6 +51,7 @@ mod tests {
traits::Crud,
utils::{build_db_pool_for_tests, DbPool},
};
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;

View file

@ -554,6 +554,7 @@ mod tests {
reason: None,
banned: None,
expires: None,
instance_id: inserted_instance.id,
};
let inserted_mod_ban = ModBan::create(pool, &mod_ban_form).await?;
let read_mod_ban = ModBan::read(pool, inserted_mod_ban.id).await?;
@ -565,6 +566,7 @@ mod tests {
banned: true,
expires: None,
published: inserted_mod_ban.published,
instance_id: inserted_instance.id,
};
// mod add community

View file

@ -1,7 +1,7 @@
use crate::{
diesel::OptionalExtension,
newtypes::{CommunityId, DbUrl, InstanceId, PersonId},
schema::{comment, community, instance, local_user, person, person_actions, post},
schema::{instance, local_user, person, person_actions},
source::person::{
Person,
PersonActions,
@ -18,7 +18,6 @@ use diesel::{
dsl::{exists, insert_into, not, select},
expression::SelectableHelper,
result::Error,
CombineDsl,
ExpressionMethods,
JoinOnDsl,
QueryDsl,
@ -103,31 +102,6 @@ impl Person {
.await
}
/// Lists local community ids for all posts and comments for a given creator.
pub async fn list_local_community_ids(
pool: &mut DbPool<'_>,
for_creator_id: PersonId,
) -> Result<Vec<CommunityId>, Error> {
let conn = &mut get_conn(pool).await?;
comment::table
.inner_join(post::table)
.inner_join(community::table.on(post::community_id.eq(community::id)))
.filter(community::local.eq(true))
.filter(not(community::deleted))
.filter(not(community::removed))
.filter(comment::creator_id.eq(for_creator_id))
.select(community::id)
.union(
post::table
.inner_join(community::table)
.filter(community::local.eq(true))
.filter(post::creator_id.eq(for_creator_id))
.select(community::id),
)
.load::<CommunityId>(conn)
.await
}
pub async fn check_username_taken(pool: &mut DbPool<'_>, username: &str) -> LemmyResult<()> {
use diesel::dsl::{exists, select};
let conn = &mut get_conn(pool).await?;
@ -357,7 +331,6 @@ mod tests {
display_name: None,
avatar: None,
banner: None,
banned: false,
deleted: false,
published: inserted_person.published,
updated: None,
@ -370,7 +343,6 @@ mod tests {
last_refreshed_at: inserted_person.published,
inbox_url: inserted_person.inbox_url.clone(),
matrix_user_id: None,
ban_expires: None,
instance_id: inserted_instance.id,
post_count: 0,
post_score: 0,

View file

@ -1,5 +1,5 @@
use crate::{
newtypes::{CommunityId, DbUrl, PersonId, PostId},
newtypes::{CommunityId, DbUrl, InstanceId, PersonId, PostId},
schema::{community, person, post, post_actions},
source::post::{
Post,
@ -149,6 +149,7 @@ impl Post {
pool: &mut DbPool<'_>,
for_creator_id: PersonId,
for_community_id: Option<CommunityId>,
for_instance_id: Option<InstanceId>,
removed: bool,
) -> Result<Vec<Self>, Error> {
let conn = &mut get_conn(pool).await?;
@ -160,6 +161,10 @@ impl Post {
update = update.filter(post::community_id.eq(for_community_id));
}
if let Some(for_instance_id) = for_instance_id {
update = update.filter(post::instance_id.eq(for_instance_id));
}
update
.set((post::removed.eq(removed), post::updated.eq(Utc::now())))
.get_results::<Self>(conn)

View file

@ -18,9 +18,10 @@ pub mod sensitive;
pub mod schema;
#[cfg(feature = "full")]
pub mod aliases {
use crate::schema::{community_actions, local_user, person};
use crate::schema::{community_actions, instance_actions, local_user, person};
diesel::alias!(
community_actions as creator_community_actions: CreatorCommunityActions,
instance_actions as home_instance_actions: HomeInstanceActions,
local_user as creator_local_user: CreatorLocalUser,
person as person1: Person1,
person as person2: Person2,
@ -40,7 +41,7 @@ use strum::{Display, EnumString};
#[cfg(feature = "full")]
use {
diesel::query_source::AliasedField,
schema::{community_actions, person},
schema::{community_actions, instance_actions, person},
ts_rs::TS,
};
@ -329,7 +330,6 @@ pub type Person1AliasAllColumnsTuple = (
AliasedField<aliases::Person1, person::name>,
AliasedField<aliases::Person1, person::display_name>,
AliasedField<aliases::Person1, person::avatar>,
AliasedField<aliases::Person1, person::banned>,
AliasedField<aliases::Person1, person::published>,
AliasedField<aliases::Person1, person::updated>,
AliasedField<aliases::Person1, person::ap_id>,
@ -343,7 +343,6 @@ pub type Person1AliasAllColumnsTuple = (
AliasedField<aliases::Person1, person::inbox_url>,
AliasedField<aliases::Person1, person::matrix_user_id>,
AliasedField<aliases::Person1, person::bot_account>,
AliasedField<aliases::Person1, person::ban_expires>,
AliasedField<aliases::Person1, person::instance_id>,
AliasedField<aliases::Person1, person::post_count>,
AliasedField<aliases::Person1, person::post_score>,
@ -358,7 +357,6 @@ pub type Person2AliasAllColumnsTuple = (
AliasedField<aliases::Person2, person::name>,
AliasedField<aliases::Person2, person::display_name>,
AliasedField<aliases::Person2, person::avatar>,
AliasedField<aliases::Person2, person::banned>,
AliasedField<aliases::Person2, person::published>,
AliasedField<aliases::Person2, person::updated>,
AliasedField<aliases::Person2, person::ap_id>,
@ -372,7 +370,6 @@ pub type Person2AliasAllColumnsTuple = (
AliasedField<aliases::Person2, person::inbox_url>,
AliasedField<aliases::Person2, person::matrix_user_id>,
AliasedField<aliases::Person2, person::bot_account>,
AliasedField<aliases::Person2, person::ban_expires>,
AliasedField<aliases::Person2, person::instance_id>,
AliasedField<aliases::Person2, person::post_count>,
AliasedField<aliases::Person2, person::post_score>,
@ -393,3 +390,13 @@ pub type CreatorCommunityActionsAllColumnsTuple = (
AliasedField<aliases::CreatorCommunityActions, community_actions::received_ban>,
AliasedField<aliases::CreatorCommunityActions, community_actions::ban_expires>,
);
#[cfg(feature = "full")]
/// A helper tuple for creator community actions
pub type HomeInstanceActionsAllColumnsTuple = (
AliasedField<aliases::HomeInstanceActions, instance_actions::person_id>,
AliasedField<aliases::HomeInstanceActions, instance_actions::instance_id>,
AliasedField<aliases::HomeInstanceActions, instance_actions::blocked>,
AliasedField<aliases::HomeInstanceActions, instance_actions::received_ban>,
AliasedField<aliases::HomeInstanceActions, instance_actions::ban_expires>,
);

View file

@ -373,6 +373,8 @@ diesel::table! {
person_id -> Int4,
instance_id -> Int4,
blocked -> Nullable<Timestamptz>,
received_ban -> Nullable<Timestamptz>,
ban_expires -> Nullable<Timestamptz>,
}
}
@ -573,6 +575,7 @@ diesel::table! {
banned -> Bool,
expires -> Nullable<Timestamptz>,
published -> Timestamptz,
instance_id -> Int4,
}
}
@ -739,7 +742,6 @@ diesel::table! {
#[max_length = 255]
display_name -> Nullable<Varchar>,
avatar -> Nullable<Text>,
banned -> Bool,
published -> Timestamptz,
updated -> Nullable<Timestamptz>,
#[max_length = 255]
@ -755,7 +757,6 @@ diesel::table! {
inbox_url -> Varchar,
matrix_user_id -> Nullable<Text>,
bot_account -> Bool,
ban_expires -> Nullable<Timestamptz>,
instance_id -> Int4,
post_count -> Int8,
post_score -> Int8,
@ -1180,77 +1181,77 @@ diesel::joinable!(site_language -> site (site_id));
diesel::joinable!(tag -> community (community_id));
diesel::allow_tables_to_appear_in_same_query!(
admin_allow_instance,
admin_block_instance,
admin_purge_comment,
admin_purge_community,
admin_purge_person,
admin_purge_post,
captcha_answer,
comment,
comment_actions,
comment_reply,
comment_report,
community,
community_actions,
community_language,
community_report,
custom_emoji,
custom_emoji_keyword,
email_verification,
federation_allowlist,
federation_blocklist,
federation_queue_state,
image_details,
inbox_combined,
instance,
instance_actions,
language,
local_image,
local_site,
local_site_rate_limit,
local_site_url_blocklist,
local_user,
local_user_language,
login_token,
mod_add,
mod_add_community,
mod_ban,
mod_ban_from_community,
mod_change_community_visibility,
mod_feature_post,
mod_lock_post,
mod_remove_comment,
mod_remove_community,
mod_remove_post,
mod_transfer_community,
modlog_combined,
oauth_account,
oauth_provider,
password_reset_request,
person,
person_actions,
person_ban,
person_comment_mention,
person_content_combined,
person_post_mention,
person_saved_combined,
post,
post_actions,
post_report,
post_tag,
previously_run_sql,
private_message,
private_message_report,
received_activity,
registration_application,
remote_image,
report_combined,
search_combined,
secret,
sent_activity,
site,
site_language,
tag,
tagline,
admin_allow_instance,
admin_block_instance,
admin_purge_comment,
admin_purge_community,
admin_purge_person,
admin_purge_post,
captcha_answer,
comment,
comment_actions,
comment_reply,
comment_report,
community,
community_actions,
community_language,
community_report,
custom_emoji,
custom_emoji_keyword,
email_verification,
federation_allowlist,
federation_blocklist,
federation_queue_state,
image_details,
inbox_combined,
instance,
instance_actions,
language,
local_image,
local_site,
local_site_rate_limit,
local_site_url_blocklist,
local_user,
local_user_language,
login_token,
mod_add,
mod_add_community,
mod_ban,
mod_ban_from_community,
mod_change_community_visibility,
mod_feature_post,
mod_lock_post,
mod_remove_comment,
mod_remove_community,
mod_remove_post,
mod_transfer_community,
modlog_combined,
oauth_account,
oauth_provider,
password_reset_request,
person,
person_actions,
person_ban,
person_comment_mention,
person_content_combined,
person_post_mention,
person_saved_combined,
post,
post_actions,
post_report,
post_tag,
previously_run_sql,
private_message,
private_message_report,
received_activity,
registration_application,
remote_image,
report_combined,
search_combined,
secret,
sent_activity,
site,
site_language,
tag,
tagline,
);

View file

@ -62,6 +62,12 @@ pub struct InstanceActions {
#[cfg_attr(feature = "full", ts(optional))]
/// When the instance was blocked.
pub blocked: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))]
/// When this user received a site ban.
pub received_ban: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))]
/// When their ban expires.
pub ban_expires: Option<DateTime<Utc>>,
}
#[derive(derive_new::new)]
@ -73,3 +79,14 @@ pub struct InstanceBlockForm {
#[new(value = "Utc::now()")]
pub blocked: DateTime<Utc>,
}
#[derive(derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = instance_actions))]
pub struct InstanceBanForm {
pub person_id: PersonId,
pub instance_id: InstanceId,
#[new(value = "Utc::now()")]
pub received_ban: DateTime<Utc>,
pub ban_expires: Option<DateTime<Utc>>,
}

View file

@ -16,6 +16,7 @@ use crate::{
newtypes::{
CommentId,
CommunityId,
InstanceId,
ModAddCommunityId,
ModAddId,
ModBanFromCommunityId,
@ -210,6 +211,7 @@ pub struct ModBan {
#[cfg_attr(feature = "full", ts(optional))]
pub expires: Option<DateTime<Utc>>,
pub published: DateTime<Utc>,
pub instance_id: InstanceId,
}
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
@ -245,6 +247,7 @@ pub struct ModBanForm {
pub reason: Option<String>,
pub banned: Option<bool>,
pub expires: Option<DateTime<Utc>>,
pub instance_id: InstanceId,
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]

View file

@ -33,8 +33,6 @@ pub struct Person {
/// A URL for an avatar.
#[cfg_attr(feature = "full", ts(optional))]
pub avatar: Option<DbUrl>,
/// Whether the person is banned.
pub banned: bool,
pub published: DateTime<Utc>,
#[cfg_attr(feature = "full", ts(optional))]
pub updated: Option<DateTime<Utc>>,
@ -64,9 +62,6 @@ pub struct Person {
pub matrix_user_id: Option<String>,
/// Whether the person is a bot account.
pub bot_account: bool,
/// When their ban, if it exists, expires, if at all.
#[cfg_attr(feature = "full", ts(optional))]
pub ban_expires: Option<DateTime<Utc>>,
pub instance_id: InstanceId,
pub post_count: i64,
#[serde(skip)]
@ -88,8 +83,6 @@ pub struct PersonInsertForm {
#[new(default)]
pub avatar: Option<DbUrl>,
#[new(default)]
pub banned: Option<bool>,
#[new(default)]
pub published: Option<DateTime<Utc>>,
#[new(default)]
pub updated: Option<DateTime<Utc>>,
@ -113,8 +106,6 @@ pub struct PersonInsertForm {
pub matrix_user_id: Option<String>,
#[new(default)]
pub bot_account: Option<bool>,
#[new(default)]
pub ban_expires: Option<DateTime<Utc>>,
}
#[derive(Clone, Default)]
@ -123,7 +114,6 @@ pub struct PersonInsertForm {
pub struct PersonUpdateForm {
pub display_name: Option<Option<String>>,
pub avatar: Option<Option<DbUrl>>,
pub banned: Option<bool>,
pub updated: Option<Option<DateTime<Utc>>>,
pub ap_id: Option<DbUrl>,
pub bio: Option<Option<String>>,
@ -136,7 +126,6 @@ pub struct PersonUpdateForm {
pub inbox_url: Option<DbUrl>,
pub matrix_user_id: Option<Option<String>>,
pub bot_account: Option<bool>,
pub ban_expires: Option<Option<DateTime<Utc>>>,
}
#[skip_serializing_none]

View file

@ -1,10 +1,13 @@
use crate::structs::{
CommentReplyView,
InboxCombinedView,
InboxCombinedViewInternal,
PersonCommentMentionView,
PersonPostMentionView,
PrivateMessageView,
use crate::{
structs::{
CommentReplyView,
InboxCombinedView,
InboxCombinedViewInternal,
PersonCommentMentionView,
PersonPostMentionView,
PrivateMessageView,
},
utils::home_instance_person_join,
};
use diesel::{
dsl::not,
@ -52,16 +55,18 @@ impl InboxCombinedViewInternal {
let item_creator = person::id;
let recipient_person = aliases::person1.field(person::id);
let item_creator_join = person::table.on(
comment::creator_id
.eq(item_creator)
.or(
inbox_combined::person_post_mention_id
.is_not_null()
.and(post::creator_id.eq(item_creator)),
)
.or(private_message::creator_id.eq(item_creator)),
);
let item_creator_join = person::table
.on(
comment::creator_id
.eq(item_creator)
.or(
inbox_combined::person_post_mention_id
.is_not_null()
.and(post::creator_id.eq(item_creator)),
)
.or(private_message::creator_id.eq(item_creator)),
)
.left_join(home_instance_person_join());
let recipient_join = aliases::person1.on(
comment_reply::recipient_id
@ -390,6 +395,7 @@ impl InternalToCombinedView for InboxCombinedViewInternal {
comment_actions: v.comment_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
creator_community_actions: v.creator_community_actions,
creator_is_admin: v.item_creator_is_admin,
can_mod: v.can_mod,
@ -412,6 +418,7 @@ impl InternalToCombinedView for InboxCombinedViewInternal {
comment_actions: v.comment_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
creator_community_actions: v.creator_community_actions,
creator_is_admin: v.item_creator_is_admin,
can_mod: v.can_mod,
@ -429,6 +436,7 @@ impl InternalToCombinedView for InboxCombinedViewInternal {
community_actions: v.community_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
post_actions: v.post_actions,
image_details: v.image_details,
creator_community_actions: v.creator_community_actions,

View file

@ -990,6 +990,7 @@ mod tests {
banned: Some(true),
reason: None,
expires: None,
instance_id: data.instance.id,
};
ModBan::create(pool, &form).await?;

View file

@ -1,9 +1,12 @@
use crate::structs::{
CommentView,
LocalUserView,
PersonContentCombinedView,
PersonContentCombinedViewInternal,
PostView,
use crate::{
structs::{
CommentView,
LocalUserView,
PersonContentCombinedView,
PersonContentCombinedViewInternal,
PostView,
},
utils::home_instance_person_join,
};
use diesel::{
BoolExpressionMethods,
@ -54,17 +57,19 @@ impl PersonContentCombinedViewInternal {
.or(comment::post_id.eq(post::id)),
);
let item_creator_join = person::table.on(
comment::creator_id
.eq(item_creator)
// Need to filter out the post rows where the post_id given is null
// Otherwise you'll get duped post rows
.or(
post::creator_id
.eq(item_creator)
.and(person_content_combined::post_id.is_not_null()),
),
);
let item_creator_join = person::table
.on(
comment::creator_id
.eq(item_creator)
// Need to filter out the post rows where the post_id given is null
// Otherwise you'll get duped post rows
.or(
post::creator_id
.eq(item_creator)
.and(person_content_combined::post_id.is_not_null()),
),
)
.left_join(home_instance_person_join());
let community_join = community::table.on(post::community_id.eq(community::id));
@ -257,6 +262,7 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal {
comment_actions: v.comment_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
creator_community_actions: v.creator_community_actions,
creator_is_admin: v.item_creator_is_admin,
can_mod: v.can_mod,
@ -271,6 +277,7 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal {
post_actions: v.post_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
creator_community_actions: v.creator_community_actions,
creator_is_admin: v.item_creator_is_admin,
can_mod: v.can_mod,

View file

@ -1,9 +1,12 @@
use crate::structs::{
CommentView,
LocalUserView,
PersonSavedCombinedView,
PersonSavedCombinedViewInternal,
PostView,
use crate::{
structs::{
CommentView,
LocalUserView,
PersonSavedCombinedView,
PersonSavedCombinedViewInternal,
PostView,
},
utils::home_instance_person_join,
};
use diesel::{
BoolExpressionMethods,
@ -93,17 +96,19 @@ impl PersonSavedCombinedViewInternal {
.or(comment::post_id.eq(post::id)),
);
let item_creator_join = person::table.on(
comment::creator_id
.eq(item_creator)
// Need to filter out the post rows where the post_id given is null
// Otherwise you'll get duped post rows
.or(
post::creator_id
.eq(item_creator)
.and(person_saved_combined::post_id.is_not_null()),
),
);
let item_creator_join = person::table
.on(
comment::creator_id
.eq(item_creator)
// Need to filter out the post rows where the post_id given is null
// Otherwise you'll get duped post rows
.or(
post::creator_id
.eq(item_creator)
.and(person_saved_combined::post_id.is_not_null()),
),
)
.left_join(home_instance_person_join());
let community_join = community::table.on(post::community_id.eq(community::id));
@ -244,6 +249,7 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal {
comment_actions: v.comment_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
creator_community_actions: v.creator_community_actions,
creator_is_admin: v.item_creator_is_admin,
can_mod: v.can_mod,
@ -258,6 +264,7 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal {
post_actions: v.post_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
creator_community_actions: v.creator_community_actions,
creator_is_admin: v.item_creator_is_admin,
can_mod: v.can_mod,
@ -310,6 +317,7 @@ mod tests {
let timmy_view = LocalUserView {
local_user: timmy_local_user,
person: timmy.clone(),
instance_actions: None,
};
let sara_form = PersonInsertForm::test_form(instance.id, "sara_pcv");

View file

@ -513,6 +513,7 @@ mod tests {
let timmy_view = LocalUserView {
local_user: timmy_local_user,
person: inserted_timmy.clone(),
instance_actions: None,
};
// Make an admin, to be able to see private message reports.
@ -523,6 +524,7 @@ mod tests {
let admin_view = LocalUserView {
local_user: admin_local_user,
person: inserted_admin.clone(),
instance_actions: None,
};
let sara_form = PersonInsertForm::test_form(inserted_instance.id, "sara_rcv");

View file

@ -8,7 +8,7 @@ use crate::{
SearchCombinedView,
SearchCombinedViewInternal,
},
utils::{filter_is_subscribed, filter_not_unlisted_or_is_subscribed},
utils::{filter_is_subscribed, filter_not_unlisted_or_is_subscribed, home_instance_person_join},
};
use diesel::{
dsl::not,
@ -54,21 +54,23 @@ impl SearchCombinedViewInternal {
fn joins(my_person_id: Option<PersonId>) -> _ {
let item_creator = person::id;
let item_creator_join = person::table.on(
search_combined::person_id
.eq(item_creator.nullable())
.or(
search_combined::comment_id
.is_not_null()
.and(comment::creator_id.eq(item_creator)),
)
.or(
search_combined::post_id
.is_not_null()
.and(post::creator_id.eq(item_creator)),
)
.and(not(person::deleted)),
);
let item_creator_join = person::table
.on(
search_combined::person_id
.eq(item_creator.nullable())
.or(
search_combined::comment_id
.is_not_null()
.and(comment::creator_id.eq(item_creator)),
)
.or(
search_combined::post_id
.is_not_null()
.and(post::creator_id.eq(item_creator)),
)
.and(not(person::deleted)),
)
.left_join(home_instance_person_join());
let comment_join = comment::table.on(
search_combined::comment_id
@ -381,6 +383,7 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
creator,
community_actions: v.community_actions,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
creator_community_actions: v.creator_community_actions,
person_actions: v.person_actions,
comment_actions: v.comment_actions,
@ -398,6 +401,7 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
image_details: v.image_details,
community_actions: v.community_actions,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
creator_community_actions: v.creator_community_actions,
person_actions: v.person_actions,
post_actions: v.post_actions,
@ -414,6 +418,8 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
Some(SearchCombinedView::Person(PersonView {
person,
is_admin: v.item_creator_is_admin,
instance_actions: v.instance_actions,
home_instance_actions: v.home_instance_actions,
}))
} else {
None
@ -477,6 +483,7 @@ mod tests {
let timmy_view = LocalUserView {
local_user: timmy_local_user,
person: timmy.clone(),
instance_actions: None,
};
let community_form = CommunityInsertForm {

View file

@ -1,6 +1,6 @@
use crate::{
structs::{CommentSlimView, CommentView},
utils::filter_blocked,
utils::{filter_blocked, home_instance_person_join},
};
use diesel::{
dsl::exists,
@ -79,8 +79,10 @@ impl CommentView {
let local_user_join = local_user::table.on(local_user::person_id.nullable().eq(my_person_id));
let person_join = person::table.left_join(home_instance_person_join());
comment::table
.inner_join(person::table)
.inner_join(person_join)
.inner_join(post::table)
.inner_join(community_join)
.left_join(community_actions_join)
@ -128,6 +130,7 @@ impl CommentView {
creator_community_actions: self.creator_community_actions,
person_actions: self.person_actions,
instance_actions: self.instance_actions,
home_instance_actions: self.home_instance_actions,
creator_is_admin: self.creator_is_admin,
can_mod: self.can_mod,
}
@ -488,6 +491,7 @@ mod tests {
let timmy_local_user_view = LocalUserView {
local_user: inserted_timmy_local_user.clone(),
person: inserted_timmy_person.clone(),
instance_actions: None,
};
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
let site = Site::create(pool, &site_form).await?;

View file

@ -31,6 +31,7 @@ impl CommunityView {
.and(community_actions::person_id.nullable().eq(person_id)),
);
// join with instance actions for community instance
let instance_actions_join = instance_actions::table.on(
instance_actions::instance_id
.eq(community::instance_id)

View file

@ -1,9 +1,12 @@
use crate::structs::LocalUserView;
use crate::{
structs::{LocalUserView, SiteView},
utils::local_instance_person_join,
};
use actix_web::{dev::Payload, FromRequest, HttpMessage, HttpRequest};
use diesel::{result::Error, BoolExpressionMethods, ExpressionMethods, QueryDsl, SelectableHelper};
use diesel::{BoolExpressionMethods, ExpressionMethods, QueryDsl, SelectableHelper};
use diesel_async::RunQueryDsl;
use lemmy_db_schema::{
newtypes::{LocalUserId, OAuthProviderId, PersonId},
newtypes::{InstanceId, LocalUserId, OAuthProviderId, PersonId},
schema::{local_user, oauth_account, person},
source::{
instance::Instance,
@ -17,90 +20,112 @@ use lemmy_db_schema::{
DbPool,
},
};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType, LemmyResult};
use std::future::{ready, Ready};
impl LocalUserView {
#[diesel::dsl::auto_type(no_type_alias)]
fn joins() -> _ {
local_user::table.inner_join(person::table)
fn joins(local_instance_id: InstanceId) -> _ {
let p: local_instance_person_join = local_instance_person_join(local_instance_id);
local_user::table.inner_join(person::table.left_join(p))
}
pub async fn read(pool: &mut DbPool<'_>, local_user_id: LocalUserId) -> Result<Self, Error> {
pub async fn read(pool: &mut DbPool<'_>, local_user_id: LocalUserId) -> LemmyResult<Self> {
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let conn = &mut get_conn(pool).await?;
Self::joins()
.filter(local_user::id.eq(local_user_id))
.select(Self::as_select())
.first(conn)
.await
Ok(
Self::joins(local_instance_id)
.filter(local_user::id.eq(local_user_id))
.select(Self::as_select())
.first(conn)
.await?,
)
}
pub async fn read_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Self, Error> {
pub async fn read_person(pool: &mut DbPool<'_>, person_id: PersonId) -> LemmyResult<Self> {
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let conn = &mut get_conn(pool).await?;
Self::joins()
.filter(person::id.eq(person_id))
.select(Self::as_select())
.first(conn)
.await
Ok(
Self::joins(local_instance_id)
.filter(person::id.eq(person_id))
.select(Self::as_select())
.first(conn)
.await?,
)
}
pub async fn read_from_name(pool: &mut DbPool<'_>, name: &str) -> Result<Self, Error> {
pub async fn read_from_name(pool: &mut DbPool<'_>, name: &str) -> LemmyResult<Self> {
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let conn = &mut get_conn(pool).await?;
Self::joins()
.filter(lower(person::name).eq(name.to_lowercase()))
.select(Self::as_select())
.first(conn)
.await
Ok(
Self::joins(local_instance_id)
.filter(lower(person::name).eq(name.to_lowercase()))
.select(Self::as_select())
.first(conn)
.await?,
)
}
pub async fn find_by_email_or_name(
pool: &mut DbPool<'_>,
name_or_email: &str,
) -> Result<Self, Error> {
) -> LemmyResult<Self> {
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let conn = &mut get_conn(pool).await?;
Self::joins()
.filter(
lower(person::name)
.eq(lower(name_or_email.to_lowercase()))
.or(lower(coalesce(local_user::email, "")).eq(name_or_email.to_lowercase())),
)
.select(Self::as_select())
.first(conn)
.await
Ok(
Self::joins(local_instance_id)
.filter(
lower(person::name)
.eq(lower(name_or_email.to_lowercase()))
.or(lower(coalesce(local_user::email, "")).eq(name_or_email.to_lowercase())),
)
.select(Self::as_select())
.first(conn)
.await?,
)
}
pub async fn find_by_email(pool: &mut DbPool<'_>, from_email: &str) -> Result<Self, Error> {
pub async fn find_by_email(pool: &mut DbPool<'_>, from_email: &str) -> LemmyResult<Self> {
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let conn = &mut get_conn(pool).await?;
Self::joins()
.filter(lower(coalesce(local_user::email, "")).eq(from_email.to_lowercase()))
.select(Self::as_select())
.first(conn)
.await
Ok(
Self::joins(local_instance_id)
.filter(lower(coalesce(local_user::email, "")).eq(from_email.to_lowercase()))
.select(Self::as_select())
.first(conn)
.await?,
)
}
pub async fn find_by_oauth_id(
pool: &mut DbPool<'_>,
oauth_provider_id: OAuthProviderId,
oauth_user_id: &str,
) -> Result<Self, Error> {
) -> LemmyResult<Self> {
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let conn = &mut get_conn(pool).await?;
Self::joins()
.inner_join(oauth_account::table)
.filter(oauth_account::oauth_provider_id.eq(oauth_provider_id))
.filter(oauth_account::oauth_user_id.eq(oauth_user_id))
.select(Self::as_select())
.first(conn)
.await
Ok(
Self::joins(local_instance_id)
.inner_join(oauth_account::table)
.filter(oauth_account::oauth_provider_id.eq(oauth_provider_id))
.filter(oauth_account::oauth_user_id.eq(oauth_user_id))
.select(Self::as_select())
.first(conn)
.await?,
)
}
pub async fn list_admins_with_emails(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
pub async fn list_admins_with_emails(pool: &mut DbPool<'_>) -> LemmyResult<Vec<Self>> {
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let conn = &mut get_conn(pool).await?;
Self::joins()
.filter(local_user::email.is_not_null())
.filter(local_user::admin.eq(true))
.select(Self::as_select())
.load::<Self>(conn)
.await
Ok(
Self::joins(local_instance_id)
.filter(local_user::email.is_not_null())
.filter(local_user::admin.eq(true))
.select(Self::as_select())
.load::<Self>(conn)
.await?,
)
}
pub async fn create_test_user(
@ -129,6 +154,13 @@ impl LocalUserView {
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
pub fn banned(&self) -> bool {
self
.instance_actions
.as_ref()
.is_some_and(|i| i.received_ban.is_some())
}
}
impl FromRequest for LocalUserView {

View file

@ -1,6 +1,8 @@
use crate::structs::PersonView;
use crate::{
structs::{PersonView, SiteView},
utils::{home_instance_person_join, local_instance_person_join},
};
use diesel::{
result::Error,
BoolExpressionMethods,
ExpressionMethods,
NullableExpressionMethods,
@ -10,8 +12,8 @@ use diesel::{
use diesel_async::RunQueryDsl;
use i_love_jesus::PaginatedQueryBuilder;
use lemmy_db_schema::{
newtypes::{PaginationCursor, PersonId},
schema::{local_user, person},
newtypes::{InstanceId, PaginationCursor, PersonId},
schema::{instance_actions, local_user, person},
source::person::{person_keys as key, Person},
traits::PaginationCursorBuilder,
utils::{get_conn, limit_fetch, now, DbPool},
@ -44,17 +46,22 @@ impl PaginationCursorBuilder for PersonView {
impl PersonView {
#[diesel::dsl::auto_type(no_type_alias)]
fn joins() -> _ {
person::table.left_join(local_user::table)
fn joins(local_instance_id: InstanceId) -> _ {
let p: local_instance_person_join = local_instance_person_join(local_instance_id);
person::table
.left_join(p)
.left_join(local_user::table)
.left_join(home_instance_person_join())
}
pub async fn read(
pool: &mut DbPool<'_>,
person_id: PersonId,
is_admin: bool,
) -> Result<Self, Error> {
) -> LemmyResult<Self> {
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let conn = &mut get_conn(pool).await?;
let mut query = Self::joins()
let mut query = Self::joins(local_instance_id)
.filter(person::id.eq(person_id))
.select(Self::as_select())
.into_boxed();
@ -63,7 +70,14 @@ impl PersonView {
query = query.filter(person::deleted.eq(false))
}
query.first(conn).await
Ok(query.first(conn).await?)
}
pub fn banned(&self) -> bool {
self
.instance_actions
.as_ref()
.is_some_and(|i| i.received_ban.is_some())
}
}
@ -77,10 +91,13 @@ pub struct PersonQuery {
}
impl PersonQuery {
pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<PersonView>, Error> {
pub async fn list(
self,
local_instance_id: InstanceId,
pool: &mut DbPool<'_>,
) -> LemmyResult<Vec<PersonView>> {
let conn = &mut get_conn(pool).await?;
let mut query = PersonView::joins()
let mut query = PersonView::joins(local_instance_id)
.filter(person::deleted.eq(false))
.select(PersonView::as_select())
.into_boxed();
@ -88,11 +105,13 @@ impl PersonQuery {
// Filters
if self.banned_only.unwrap_or_default() {
query = query.filter(
person::local.and(person::banned).and(
person::ban_expires
.is_null()
.or(person::ban_expires.gt(now().nullable())),
),
person::local
.and(instance_actions::received_ban.is_not_null())
.and(
instance_actions::ban_expires
.is_null()
.or(instance_actions::ban_expires.gt(now().nullable())),
),
);
}
@ -128,14 +147,15 @@ impl PersonQuery {
mod tests {
use super::*;
use crate::site::site_view::create_test_instance;
use lemmy_db_schema::{
assert_length,
source::{
instance::Instance,
instance::{Instance, InstanceActions, InstanceBanForm},
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
person::{Person, PersonInsertForm, PersonUpdateForm},
},
traits::Crud,
traits::{Bannable, Crud},
utils::build_db_pool_for_tests,
};
use lemmy_utils::error::LemmyResult;
@ -150,11 +170,11 @@ mod tests {
}
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
let instance = create_test_instance(pool).await?;
let alice_form = PersonInsertForm {
local: Some(true),
..PersonInsertForm::test_form(inserted_instance.id, "alice")
..PersonInsertForm::test_form(instance.id, "alice")
};
let alice = Person::create(pool, &alice_form).await?;
let alice_local_user_form = LocalUserInsertForm::test_form(alice.id);
@ -163,7 +183,7 @@ mod tests {
let bob_form = PersonInsertForm {
bot_account: Some(true),
local: Some(false),
..PersonInsertForm::test_form(inserted_instance.id, "bob")
..PersonInsertForm::test_form(instance.id, "bob")
};
let bob = Person::create(pool, &bob_form).await?;
let bob_local_user_form = LocalUserInsertForm::test_form(bob.id);
@ -220,13 +240,9 @@ mod tests {
let pool = &mut pool.into();
let data = init_data(pool).await?;
Person::update(
InstanceActions::ban(
pool,
data.alice.id,
&PersonUpdateForm {
banned: Some(true),
..Default::default()
},
&InstanceBanForm::new(data.alice.id, data.alice.instance_id, None),
)
.await?;
@ -234,7 +250,7 @@ mod tests {
banned_only: Some(true),
..Default::default()
}
.list(pool)
.list(data.alice.instance_id, pool)
.await?;
assert_length!(1, list);
assert_eq!(list[0].person.id, data.alice.id);
@ -263,7 +279,7 @@ mod tests {
admins_only: Some(true),
..Default::default()
}
.list(pool)
.list(data.alice.instance_id, pool)
.await?;
assert_length!(1, list);
assert_eq!(list[0].person.id, data.alice.id);

View file

@ -1,6 +1,11 @@
use crate::{
structs::{PostPaginationCursor, PostView},
utils::{filter_blocked, filter_is_subscribed, filter_not_unlisted_or_is_subscribed},
utils::{
filter_blocked,
filter_is_subscribed,
filter_not_unlisted_or_is_subscribed,
home_instance_person_join,
},
};
use diesel::{
debug_query,
@ -107,8 +112,10 @@ impl PostView {
let local_user_join = local_user::table.on(local_user::person_id.nullable().eq(my_person_id));
let person_join = person::table.left_join(home_instance_person_join());
post::table
.inner_join(person::table)
.inner_join(person_join)
.inner_join(community::table)
.left_join(image_details_join)
.left_join(community_actions_join)
@ -787,15 +794,18 @@ mod tests {
let tegan_local_user_view = LocalUserView {
local_user: inserted_tegan_local_user,
person: inserted_tegan_person,
instance_actions: None,
};
let john_local_user_view = LocalUserView {
local_user: inserted_john_local_user,
person: inserted_john_person,
instance_actions: None,
};
let bot_local_user_view = LocalUserView {
local_user: inserted_bot_local_user,
person: inserted_bot_person,
instance_actions: None,
};
let site = Site {

View file

@ -233,8 +233,6 @@ mod tests {
avatar: None,
ap_id: sara_person.ap_id.clone(),
local: true,
banned: false,
ban_expires: None,
deleted: false,
bot_account: false,
bio: None,
@ -306,8 +304,6 @@ mod tests {
avatar: None,
ap_id: timmy_person.ap_id.clone(),
local: true,
banned: false,
ban_expires: None,
deleted: false,
bot_account: false,
bio: None,

View file

@ -2,25 +2,52 @@ use crate::structs::SiteView;
use diesel::{ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl, SelectableHelper};
use diesel_async::RunQueryDsl;
use lemmy_db_schema::{
schema::{local_site, local_site_rate_limit, site},
schema::{instance, local_site, local_site_rate_limit, site},
source::{
instance::Instance,
local_site::{LocalSite, LocalSiteInsertForm},
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitInsertForm},
site::{Site, SiteInsertForm},
},
traits::Crud,
utils::{get_conn, DbPool},
};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use lemmy_utils::{
build_cache,
error::{LemmyError, LemmyErrorType, LemmyResult},
CacheLock,
};
use std::sync::{Arc, LazyLock};
impl SiteView {
pub async fn read_local(pool: &mut DbPool<'_>) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
Ok(
site::table
.inner_join(local_site::table)
.inner_join(
local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)),
)
.select(Self::as_select())
.first(conn)
.await
.optional()?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?,
)
static CACHE: CacheLock<SiteView> = LazyLock::new(build_cache);
CACHE
.try_get_with((), async move {
let conn = &mut get_conn(pool).await?;
let local_site = site::table
.inner_join(local_site::table)
.inner_join(instance::table)
.inner_join(
local_site_rate_limit::table
.on(local_site::id.eq(local_site_rate_limit::local_site_id)),
)
.select(Self::as_select())
.first(conn)
.await
.optional()?
.ok_or(LemmyErrorType::LocalSiteNotSetup)?;
Ok(local_site)
})
.await
.map_err(|_e: Arc<LemmyError>| LemmyErrorType::LocalSiteNotSetup.into())
}
}
pub async fn create_test_instance(pool: &mut DbPool<'_>) -> LemmyResult<Instance> {
let instance = Instance::read_or_create(pool, "example.com".to_string()).await?;
let site = Site::create(pool, &SiteInsertForm::new("name".to_string(), instance.id)).await?;
let local_site = LocalSite::create(pool, &LocalSiteInsertForm::new(site.id)).await?;
LocalSiteRateLimit::create(pool, &LocalSiteRateLimitInsertForm::new(local_site.id)).await?;
Ok(instance)
}

View file

@ -4,6 +4,7 @@ use crate::utils::{
comment_select_remove_deletes,
creator_community_actions_select,
creator_is_admin,
home_instance_actions_select,
local_user_can_mod,
local_user_community_can_mod,
local_user_is_admin,
@ -80,6 +81,7 @@ use lemmy_db_schema::{
schema::local_user,
utils::functions::coalesce,
CreatorCommunityActionsAllColumnsTuple,
HomeInstanceActionsAllColumnsTuple,
Person1AliasAllColumnsTuple,
Person2AliasAllColumnsTuple,
};
@ -177,6 +179,11 @@ pub struct CommentView {
#[cfg_attr(feature = "full", diesel(embed))]
#[cfg_attr(feature = "full", ts(optional))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
#[cfg_attr(feature = "full", ts(optional))]
pub home_instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", ts(optional))]
#[cfg_attr(feature = "full",
diesel(
@ -216,6 +223,11 @@ pub struct CommentSlimView {
pub creator_community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", ts(optional))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
#[cfg_attr(feature = "full", ts(optional))]
pub home_instance_actions: Option<InstanceActions>,
pub creator_is_admin: bool,
pub can_mod: bool,
}
@ -253,6 +265,9 @@ pub struct LocalUserView {
pub local_user: LocalUser,
#[cfg_attr(feature = "full", diesel(embed))]
pub person: Person,
#[cfg_attr(feature = "full", diesel(embed))]
#[cfg_attr(feature = "full", ts(optional))]
pub instance_actions: Option<InstanceActions>,
}
#[skip_serializing_none]
@ -348,6 +363,11 @@ pub struct PostView {
#[cfg_attr(feature = "full", diesel(embed))]
#[cfg_attr(feature = "full", ts(optional))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
#[cfg_attr(feature = "full", ts(optional))]
pub home_instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", ts(optional))]
#[cfg_attr(feature = "full",
diesel(
@ -435,6 +455,8 @@ pub struct SiteView {
pub local_site: LocalSite,
#[cfg_attr(feature = "full", diesel(embed))]
pub local_site_rate_limit: LocalSiteRateLimit,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance: Instance,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
@ -572,6 +594,10 @@ pub(crate) struct PersonContentCombinedViewInternal {
pub community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
pub home_instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]
@ -630,6 +656,10 @@ pub(crate) struct PersonSavedCombinedViewInternal {
pub community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
pub home_instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]
@ -761,6 +791,11 @@ pub struct PersonCommentMentionView {
pub person_actions: Option<PersonActions>,
#[cfg_attr(feature = "full", ts(optional))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
#[cfg_attr(feature = "full", ts(optional))]
pub home_instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", ts(optional))]
pub creator_community_actions: Option<CommunityActions>,
pub creator_is_admin: bool,
@ -789,6 +824,11 @@ pub struct PersonPostMentionView {
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", ts(optional))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
#[cfg_attr(feature = "full", ts(optional))]
pub home_instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", ts(optional))]
pub creator_community_actions: Option<CommunityActions>,
pub creator_is_admin: bool,
@ -816,6 +856,11 @@ pub struct CommentReplyView {
pub person_actions: Option<PersonActions>,
#[cfg_attr(feature = "full", ts(optional))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
#[cfg_attr(feature = "full", ts(optional))]
pub home_instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", ts(optional))]
pub creator_community_actions: Option<CommunityActions>,
pub creator_is_admin: bool,
@ -837,6 +882,14 @@ pub struct PersonView {
)
)]
pub is_admin: bool,
#[cfg_attr(feature = "full", diesel(embed))]
#[cfg_attr(feature = "full", ts(optional))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
#[cfg_attr(feature = "full", ts(optional))]
pub home_instance_actions: Option<InstanceActions>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
@ -913,6 +966,10 @@ pub struct InboxCombinedViewInternal {
pub community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
pub home_instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]
@ -1288,6 +1345,10 @@ pub(crate) struct SearchCombinedViewInternal {
pub community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(
select_expression_type = Nullable<HomeInstanceActionsAllColumnsTuple>,
select_expression = home_instance_actions_select()))]
pub home_instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]

View file

@ -1,14 +1,22 @@
use diesel::{
dsl::{case_when, exists, not},
dsl::{case_when, exists, not, Nullable},
helper_types::{Eq, NotEq},
BoolExpressionMethods,
ExpressionMethods,
JoinOnDsl,
NullableExpressionMethods,
PgExpressionMethods,
QueryDsl,
};
use lemmy_db_schema::{
aliases::{creator_community_actions, creator_local_user, person1, person2},
aliases::{
creator_community_actions,
creator_local_user,
home_instance_actions,
person1,
person2,
},
newtypes::InstanceId,
schema::{
comment,
community,
@ -22,6 +30,7 @@ use lemmy_db_schema::{
source::community::CommunityFollowerState,
CommunityVisibility,
CreatorCommunityActionsAllColumnsTuple,
HomeInstanceActionsAllColumnsTuple,
Person1AliasAllColumnsTuple,
Person2AliasAllColumnsTuple,
};
@ -154,6 +163,12 @@ pub(crate) fn creator_community_actions_select() -> CreatorCommunityActionsAllCo
creator_community_actions.fields(community_actions::all_columns)
}
pub(crate) fn home_instance_actions_select() -> Nullable<HomeInstanceActionsAllColumnsTuple> {
home_instance_actions
.fields(instance_actions::all_columns)
.nullable()
}
type IsSubscribedType =
Eq<lemmy_db_schema::schema::community_actions::follow_state, Option<CommunityFollowerState>>;
@ -169,3 +184,29 @@ pub(crate) fn filter_not_unlisted_or_is_subscribed() -> _ {
let is_subscribed: IsSubscribedType = filter_is_subscribed();
not_unlisted.or(is_subscribed)
}
/// join with instance actions for local instance
///
/// Requires annotation for return type, see https://docs.diesel.rs/2.2.x/diesel/dsl/attr.auto_type.html#annotating-types
#[diesel::dsl::auto_type]
pub fn local_instance_person_join(local_instance_id: InstanceId) -> _ {
instance_actions::table.on(
instance_actions::instance_id
.eq(local_instance_id)
.and(instance_actions::person_id.eq(person::id)),
)
}
#[diesel::dsl::auto_type]
pub fn home_instance_person_join() -> _ {
home_instance_actions.on(
home_instance_actions
.field(instance_actions::instance_id)
.eq(person::instance_id)
.and(
home_instance_actions
.field(instance_actions::person_id)
.eq(person::id),
),
)
}

View file

@ -60,7 +60,7 @@ pub async fn delete_community_icon(
local_user_view: LocalUserView,
) -> LemmyResult<Json<SuccessResponse>> {
let community = Community::read(&mut context.pool(), data.id).await?;
is_mod_or_admin(&mut context.pool(), &local_user_view.person, community.id).await?;
is_mod_or_admin(&mut context.pool(), &local_user_view, community.id).await?;
delete_old_image(&community.icon, &context).await?;
@ -79,7 +79,7 @@ pub async fn delete_community_banner(
local_user_view: LocalUserView,
) -> LemmyResult<Json<SuccessResponse>> {
let community = Community::read(&mut context.pool(), data.id).await?;
is_mod_or_admin(&mut context.pool(), &local_user_view.person, community.id).await?;
is_mod_or_admin(&mut context.pool(), &local_user_view, community.id).await?;
delete_old_image(&community.icon, &context).await?;

View file

@ -11,8 +11,8 @@ use lemmy_api_common::{
context::LemmyContext,
image::{ImageGetParams, ImageProxyParams},
};
use lemmy_db_schema::source::{images::RemoteImage, local_site::LocalSite};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_schema::source::images::RemoteImage;
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::error::LemmyResult;
use url::Url;
@ -25,7 +25,7 @@ pub async fn get_image(
) -> LemmyResult<HttpResponse> {
// block access to images if instance is private
if local_user_view.is_none() {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_site = SiteView::read_local(&mut context.pool()).await?.local_site;
if local_site.private_instance {
return Ok(HttpResponse::Unauthorized().finish());
}

View file

@ -87,7 +87,7 @@ pub async fn upload_community_icon(
context: Data<LemmyContext>,
) -> LemmyResult<Json<UploadImageResponse>> {
let community: Community = Community::read(&mut context.pool(), query.id).await?;
is_mod_or_admin(&mut context.pool(), &local_user_view.person, community.id).await?;
is_mod_or_admin(&mut context.pool(), &local_user_view, community.id).await?;
let image = do_upload_image(req, body, Avatar, &local_user_view, &context).await?;
delete_old_image(&community.icon, &context).await?;
@ -109,7 +109,7 @@ pub async fn upload_community_banner(
context: Data<LemmyContext>,
) -> LemmyResult<Json<UploadImageResponse>> {
let community: Community = Community::read(&mut context.pool(), query.id).await?;
is_mod_or_admin(&mut context.pool(), &local_user_view.person, community.id).await?;
is_mod_or_admin(&mut context.pool(), &local_user_view, community.id).await?;
let image = do_upload_image(req, body, Banner, &local_user_view, &context).await?;
delete_old_image(&community.banner, &context).await?;

View file

@ -388,7 +388,7 @@ async fn initialize_local_site_2022_10_10(
info!("Running initialize_local_site_2022_10_10");
// Check to see if local_site exists
if LocalSite::read(pool).await.is_ok() {
if SiteView::read_local(pool).await.is_ok() {
return Ok(());
}
info!("No Local Site found, creating it.");

View file

@ -27,6 +27,7 @@ use lemmy_db_schema::{
community_actions,
federation_blocklist,
instance,
instance_actions,
person,
post,
received_activity,
@ -41,6 +42,7 @@ use lemmy_db_schema::{
traits::Crud,
utils::{functions::coalesce, get_conn, now, uplete, DbPool, DELETED_REPLACEMENT_TEXT},
};
use lemmy_db_views::{structs::SiteView, utils::local_instance_person_join};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use reqwest_middleware::ClientWithMiddleware;
use std::time::Duration;
@ -389,21 +391,19 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) -> LemmyResult<()> {
info!("Updating banned column if it expires ...");
let mut conn = get_conn(pool).await?;
diesel::update(
person::table
.filter(person::banned.eq(true))
.filter(person::ban_expires.lt(now().nullable())),
)
.set(person::banned.eq(false))
.execute(&mut conn)
.await?;
uplete::new(community_actions::table.filter(community_actions::ban_expires.lt(now().nullable())))
.set_null(community_actions::received_ban)
.set_null(community_actions::ban_expires)
.as_query()
.execute(&mut conn)
.await?;
uplete::new(instance_actions::table.filter(instance_actions::ban_expires.lt(now().nullable())))
.set_null(instance_actions::received_ban)
.set_null(instance_actions::ban_expires)
.as_query()
.execute(&mut conn)
.await?;
Ok(())
}
@ -423,6 +423,7 @@ async fn delete_instance_block_when_expired(pool: &mut DbPool<'_>) -> LemmyResul
/// Find all unpublished posts with scheduled date in the future, and publish them.
async fn publish_scheduled_posts(context: &Data<LemmyContext>) -> LemmyResult<()> {
let pool = &mut context.pool();
let local_instance_id = SiteView::read_local(pool).await?.instance.id;
let mut conn = get_conn(pool).await?;
let not_banned_action = community_actions::table
@ -431,13 +432,14 @@ async fn publish_scheduled_posts(context: &Data<LemmyContext>) -> LemmyResult<()
let scheduled_posts: Vec<_> = post::table
.inner_join(community::table)
.inner_join(person::table)
.inner_join(person::table.left_join(local_instance_person_join(local_instance_id)))
// find all posts which have scheduled_publish_time that is in the past
.filter(post::scheduled_publish_time.is_not_null())
.filter(coalesce(post::scheduled_publish_time, now()).lt(now()))
// make sure the post, person and community are still around
.filter(not(post::deleted.or(post::removed)))
.filter(not(person::banned.or(person::deleted)))
.filter(not(person::deleted))
.filter(instance_actions::received_ban.is_null())
.filter(not(community::removed.or(community::deleted)))
// ensure that user isnt banned from community
.filter(not(exists(not_banned_action)))
@ -555,6 +557,7 @@ mod tests {
use super::*;
use lemmy_api_common::request::client_builder;
use lemmy_db_views::site::site_view::create_test_instance;
use lemmy_utils::{
error::{LemmyErrorType, LemmyResult},
settings::structs::Settings,
@ -587,11 +590,13 @@ mod tests {
#[serial]
async fn test_scheduled_tasks_no_errors() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let instance = create_test_instance(&mut context.pool()).await?;
startup_jobs(&mut context.pool()).await?;
update_instance_software(&mut context.pool(), context.client()).await?;
delete_expired_captcha_answers(&mut context.pool()).await?;
publish_scheduled_posts(&context).await?;
Instance::delete(&mut context.pool(), instance.id).await?;
Ok(())
}
}

Some files were not shown because too many files have changed in this diff Show more