Skip to content

Commit

Permalink
WIP: Instance blocks with mod log entry and expiration (fixes #2506)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nutomic committed Nov 19, 2024
1 parent 417e18e commit 6262929
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 63 deletions.
42 changes: 42 additions & 0 deletions crates/api/src/site/admin_block_instance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
context::LemmyContext,
site::{AdminBlockInstance, BlockInstanceResponse}, utils::is_admin,
};
use lemmy_db_schema::{
source::{federation_blocklist::FederationBlockListForm, instance_block::{InstanceBlock, InstanceBlockForm}},
traits::Blockable,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};

#[tracing::instrument(skip(context))]
pub async fn block_instance(
data: Json<AdminBlockInstance>,
local_user_view: LocalUserView,
context: Data<LemmyContext>,
) -> LemmyResult<Json<BlockInstanceResponse>> {
is_admin(&local_user_view)?;

let instance_block_form = FederationBlockListForm {
instance_id,
person_id:
reason: data.reason,
expires: data.expires
};

if data.block {
InstanceBlock::block(&mut context.pool(), &instance_block_form)
.await
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)?;
} else {
InstanceBlock::unblock(&mut context.pool(), &instance_block_form)
.await
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)?;
}

Ok(Json(BlockInstanceResponse {
blocked: data.block,
}))
}
1 change: 1 addition & 0 deletions crates/api/src/site/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod list_all_media;
pub mod mod_log;
pub mod purge;
pub mod registration_applications;
pub mod admin_block_instance;
34 changes: 18 additions & 16 deletions crates/api/src/site/mod_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,10 @@ use lemmy_api_common::{
use lemmy_db_schema::{source::local_site::LocalSite, ModlogActionType};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_moderator::structs::{
AdminPurgeCommentView,
AdminPurgeCommunityView,
AdminPurgePersonView,
AdminPurgePostView,
ModAddCommunityView,
ModAddView,
ModBanFromCommunityView,
ModBanView,
ModFeaturePostView,
ModHideCommunityView,
ModLockPostView,
ModRemoveCommentView,
ModRemoveCommunityView,
ModRemovePostView,
ModTransferCommunityView,
ModlogListParams,
AdminPurgeCommentView, AdminPurgeCommunityView, AdminPurgePersonView, AdminPurgePostView,
FederationBlockList, ModAddCommunityView, ModAddView, ModBanFromCommunityView, ModBanView,
ModFeaturePostView, ModHideCommunityView, ModLockPostView, ModRemoveCommentView,
ModRemoveCommunityView, ModRemovePostView, ModTransferCommunityView, ModlogListParams,
};
use lemmy_utils::error::LemmyResult;
use ModlogActionType::*;
Expand Down Expand Up @@ -121,6 +109,7 @@ pub async fn get_mod_log(
admin_purged_communities,
admin_purged_posts,
admin_purged_comments,
admin_block_instance,
) = if data.community_id.is_none() {
(
match type_ {
Expand Down Expand Up @@ -161,6 +150,18 @@ pub async fn get_mod_log(
}
_ => Default::default(),
},
match type_ {
All | AdminPurgeComment if other_person_id.is_none() => {
AdminPurgeCommentView::list(&mut context.pool(), params).await?
}
_ => Default::default(),
},
match type_ {
All | AdminBlockInstance if other_person_id.is_none() => {
AdminBlockInstance::list(&mut context.pool(), params).await?
}
_ => Default::default(),
},
)
} else {
Default::default()
Expand All @@ -183,5 +184,6 @@ pub async fn get_mod_log(
admin_purged_posts,
admin_purged_comments,
hidden_communities,
admin_block_instance,
}))
}
12 changes: 12 additions & 0 deletions crates/api_common/src/site.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ use lemmy_db_views_actor::structs::{
PersonView,
};
use lemmy_db_views_moderator::structs::{
AdminBlockInstance,
AdminPurgeCommentView,
AdminPurgeCommunityView,
AdminPurgePersonView,
Expand Down Expand Up @@ -183,6 +184,7 @@ pub struct GetModlogResponse {
pub admin_purged_posts: Vec<AdminPurgePostView>,
pub admin_purged_comments: Vec<AdminPurgeCommentView>,
pub hidden_communities: Vec<ModHideCommunityView>,
pub admin_block_instance: Vec<AdminBlockInstance>,
}

#[skip_serializing_none]
Expand Down Expand Up @@ -660,3 +662,13 @@ pub struct BlockInstance {
pub struct BlockInstanceResponse {
pub blocked: bool,
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))]
pub struct AdminBlockInstance {
pub instance_id: InstanceId,
pub block: bool,
pub reason: Option<String>,
pub expires: DateTime<Utc>,
}
8 changes: 0 additions & 8 deletions crates/api_crud/src/site/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ use lemmy_api_common::{
use lemmy_db_schema::{
source::{
actor_language::SiteLanguage,
federation_allowlist::FederationAllowList,
federation_blocklist::FederationBlockList,
local_site::{LocalSite, LocalSiteUpdateForm},
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
local_site_url_blocklist::LocalSiteUrlBlocklist,
Expand Down Expand Up @@ -152,12 +150,6 @@ pub async fn update_site(
.await
.ok();

// Replace the blocked and allowed instances
let allowed = data.allowed_instances.clone();
FederationAllowList::replace(&mut context.pool(), allowed).await?;
let blocked = data.blocked_instances.clone();
FederationBlockList::replace(&mut context.pool(), blocked).await?;

if let Some(url_blocklist) = data.blocked_urls.clone() {
let parsed_urls = check_urls_are_valid(&url_blocklist)?;
LocalSiteUrlBlocklist::replace(&mut context.pool(), parsed_urls).await?;
Expand Down
46 changes: 9 additions & 37 deletions crates/db_schema/src/impls/federation_blocklist.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,21 @@
use crate::{
schema::federation_blocklist,
source::{
federation_blocklist::{FederationBlockList, FederationBlockListForm},
instance::Instance,
},
source::federation_blocklist::{FederationBlockList, FederationBlockListForm},
utils::{get_conn, DbPool},
};
use diesel::{dsl::insert_into, result::Error};
use diesel_async::{AsyncPgConnection, RunQueryDsl};
use diesel_async::RunQueryDsl;

impl FederationBlockList {
pub async fn replace(pool: &mut DbPool<'_>, list_opt: Option<Vec<String>>) -> Result<(), Error> {
pub async fn create(
pool: &mut DbPool<'_>,
form: &FederationBlockListForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
conn
.build_transaction()
.run(|conn| {
Box::pin(async move {
if let Some(list) = list_opt {
Self::clear(conn).await?;

for domain in list {
// Upsert all of these as instances
let instance = Instance::read_or_create(&mut conn.into(), domain).await?;

let form = FederationBlockListForm {
instance_id: instance.id,
updated: None,
};
insert_into(federation_blocklist::table)
.values(form)
.get_result::<Self>(conn)
.await?;
}
Ok(())
} else {
Ok(())
}
}) as _
})
.await
}

async fn clear(conn: &mut AsyncPgConnection) -> Result<usize, Error> {
diesel::delete(federation_blocklist::table)
.execute(conn)
insert_into(federation_blocklist::table)
.values(form)
.get_result::<Self>(conn)
.await
}
}
1 change: 1 addition & 0 deletions crates/db_schema/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ pub enum ModlogActionType {
AdminPurgeCommunity,
AdminPurgePost,
AdminPurgeComment,
AdminBlockInstance,
}

#[derive(
Expand Down
4 changes: 4 additions & 0 deletions crates/db_schema/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ diesel::table! {
instance_id -> Int4,
published -> Timestamptz,
updated -> Nullable<Timestamptz>,
admin_person_id -> Nullable<Int4>,
reason -> Nullable<Text>,
expires -> Nullable<Timestamptz>,
}
}

Expand Down Expand Up @@ -955,6 +958,7 @@ diesel::joinable!(custom_emoji_keyword -> custom_emoji (custom_emoji_id));
diesel::joinable!(email_verification -> local_user (local_user_id));
diesel::joinable!(federation_allowlist -> instance (instance_id));
diesel::joinable!(federation_blocklist -> instance (instance_id));
diesel::joinable!(federation_blocklist -> person (admin_person_id));
diesel::joinable!(federation_queue_state -> instance (instance_id));
diesel::joinable!(instance_actions -> instance (instance_id));
diesel::joinable!(instance_actions -> person (person_id));
Expand Down
13 changes: 11 additions & 2 deletions crates/db_schema/src/source/federation_blocklist.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::newtypes::InstanceId;
use crate::newtypes::{InstanceId, PersonId};
#[cfg(feature = "full")]
use crate::schema::federation_blocklist;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use ts_rs::TS;

#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "full",
derive(Queryable, Selectable, Associations, Identifiable)
derive(TS, Queryable, Selectable, Associations, Identifiable)
)]
#[cfg_attr(
feature = "full",
Expand All @@ -17,10 +18,15 @@ use std::fmt::Debug;
#[cfg_attr(feature = "full", diesel(table_name = federation_blocklist))]
#[cfg_attr(feature = "full", diesel(primary_key(instance_id)))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
#[cfg_attr(feature = "full", ts(export))]
pub struct FederationBlockList {
pub instance_id: InstanceId,
pub published: DateTime<Utc>,
pub updated: Option<DateTime<Utc>>,
// TODO: would be good to make this mandatory but value doesnt exist for old entries
pub admin_person_id: Option<PersonId>,
pub reason: Option<String>,
pub expires: Option<DateTime<Utc>>,
}

#[derive(Clone, Default)]
Expand All @@ -29,4 +35,7 @@ pub struct FederationBlockList {
pub struct FederationBlockListForm {
pub instance_id: InstanceId,
pub updated: Option<DateTime<Utc>>,
pub admin_person_id: Option<PersonId>,
pub reason: Option<String>,
pub expires: Option<DateTime<Utc>>,
}
57 changes: 57 additions & 0 deletions crates/db_views_moderator/src/admin_block_instance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::structs::{AdminBlockInstance, ModlogListParams};
use diesel::{
result::Error,
BoolExpressionMethods,
ExpressionMethods,
IntoSql,
JoinOnDsl,
NullableExpressionMethods,
QueryDsl,
};
use diesel_async::RunQueryDsl;
use lemmy_db_schema::{
newtypes::PersonId,
schema::{federation_blocklist, instance, person},
utils::{functions::coalesce, get_conn, limit_and_offset, DbPool},
};

impl AdminBlockInstance {
pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result<Vec<Self>, Error> {
let conn = &mut get_conn(pool).await?;

let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1));
let show_mod_names = !params.hide_modlog_names;
let show_mod_names_expr = show_mod_names.as_sql::<diesel::sql_types::Bool>();

let admin_names_join = coalesce(federation_blocklist::admin_person_id, 0)
.eq(person::id)
.and(show_mod_names_expr.or(person::id.eq(admin_person_id_join)));
let mut query = federation_blocklist::table
.left_join(person::table.on(admin_names_join))
.inner_join(instance::table)
.select((
federation_blocklist::all_columns,
instance::all_columns,
person::all_columns.nullable(),
))
.into_boxed();

if let Some(admin_person_id) = params.mod_person_id {
query = query.filter(federation_blocklist::admin_person_id.eq(admin_person_id));
};

// If a post or comment ID is given, then don't find any results
if params.post_id.is_some() || params.comment_id.is_some() {
return Ok(vec![]);
}

let (limit, offset) = limit_and_offset(params.page, params.limit)?;

query
.limit(limit)
.offset(offset)
.order_by(federation_blocklist::published.desc())
.load::<AdminBlockInstance>(conn)
.await
}
}
2 changes: 2 additions & 0 deletions crates/db_views_moderator/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#[cfg(feature = "full")]
pub mod admin_block_instance;
#[cfg(feature = "full")]
pub mod admin_purge_comment_view;
#[cfg(feature = "full")]
pub mod admin_purge_community_view;
Expand Down
15 changes: 15 additions & 0 deletions crates/db_views_moderator/src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use lemmy_db_schema::{
source::{
comment::Comment,
community::Community,
federation_blocklist::FederationBlockList,
instance::Instance,
moderator::{
AdminPurgeComment,
AdminPurgeCommunity,
Expand Down Expand Up @@ -233,6 +235,19 @@ pub struct AdminPurgePostView {
pub community: Community,
}

#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS, Queryable))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
#[cfg_attr(feature = "full", ts(export))]
/// When an admin purges a post.
pub struct AdminBlockInstance {
pub blocklist_entry: FederationBlockList,
pub instance: Instance,
#[cfg_attr(feature = "full", ts(optional))]
pub admin: Option<Person>,
}

#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
#[cfg_attr(feature = "full", derive(TS, Queryable))]
Expand Down
3 changes: 3 additions & 0 deletions migrations/2024-11-19-142005_instance-block-mod-log/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
alter table federation_blocklist drop column reason;
alter table federation_blocklist drop column expires;
alter table federation_blocklist drop column admin_person_id;
3 changes: 3 additions & 0 deletions migrations/2024-11-19-142005_instance-block-mod-log/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
alter table federation_blocklist add column admin_person_id int REFERENCES person(id) ON UPDATE CASCADE ON DELETE CASCADE;
alter table federation_blocklist add column reason text;
alter table federation_blocklist add column expires timestamptz;

0 comments on commit 6262929

Please sign in to comment.