From 33989f5518331545be8f0a758e8d70624f9144bb Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 14 Feb 2024 04:49:55 -0500 Subject: [PATCH] Blocking an instance also hides private messages from their users. (#4447) * Blocking an instance also hides private messages from their users. - Fixes #4444 * Separating private message tests. --- crates/db_schema/src/impls/local_user.rs | 9 ++ crates/db_schema/src/impls/person.rs | 12 +- crates/db_views/src/post_view.rs | 32 ++--- crates/db_views/src/private_message_view.rs | 132 ++++++++++++++++++-- 4 files changed, 152 insertions(+), 33 deletions(-) diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index 98d1c8494..58c37ff3c 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -140,6 +140,15 @@ impl LocalUser { } } +impl LocalUserInsertForm { + pub fn test_form(person_id: PersonId) -> Self { + Self::builder() + .person_id(person_id) + .password_encrypted(String::new()) + .build() + } +} + pub struct UserBackupLists { pub followed_communities: Vec, pub saved_posts: Vec, diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index 32ce6c97a..9fb1ee1c5 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -1,5 +1,5 @@ use crate::{ - newtypes::{CommunityId, DbUrl, PersonId}, + newtypes::{CommunityId, DbUrl, InstanceId, PersonId}, schema::{instance, local_user, person, person_follower}, source::person::{ Person, @@ -86,6 +86,16 @@ impl Person { } } +impl PersonInsertForm { + pub fn test_form(instance_id: InstanceId, name: &str) -> Self { + Self::builder() + .name(name.to_owned()) + .public_key("pubkey".to_string()) + .instance_id(instance_id) + .build() + } +} + #[async_trait] impl ApubActor for Person { async fn read_from_apub_id( diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index cdfa923d4..6c5a983b1 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -698,7 +698,7 @@ mod tests { use lemmy_db_schema::{ aggregates::structs::PostAggregates, impls::actor_language::UNDETERMINED_ID, - newtypes::{InstanceId, LanguageId, PersonId}, + newtypes::LanguageId, source::{ actor_language::LocalUserLanguage, comment::{Comment, CommentInsertForm}, @@ -757,37 +757,22 @@ mod tests { } } - fn default_person_insert_form(instance_id: InstanceId, name: &str) -> PersonInsertForm { - PersonInsertForm::builder() - .name(name.to_owned()) - .public_key("pubkey".to_string()) - .instance_id(instance_id) - .build() - } - - fn default_local_user_form(person_id: PersonId) -> LocalUserInsertForm { - LocalUserInsertForm::builder() - .person_id(person_id) - .password_encrypted(String::new()) - .build() - } - async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult { let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; - let new_person = default_person_insert_form(inserted_instance.id, "tegan"); + let new_person = PersonInsertForm::test_form(inserted_instance.id, "tegan"); let inserted_person = Person::create(pool, &new_person).await?; let local_user_form = LocalUserInsertForm { admin: Some(true), - ..default_local_user_form(inserted_person.id) + ..LocalUserInsertForm::test_form(inserted_person.id) }; let inserted_local_user = LocalUser::create(pool, &local_user_form).await?; let new_bot = PersonInsertForm { bot_account: Some(true), - ..default_person_insert_form(inserted_instance.id, "mybot") + ..PersonInsertForm::test_form(inserted_instance.id, "mybot") }; let inserted_bot = Person::create(pool, &new_bot).await?; @@ -802,12 +787,15 @@ mod tests { let inserted_community = Community::create(pool, &new_community).await?; // Test a person block, make sure the post query doesn't include their post - let blocked_person = default_person_insert_form(inserted_instance.id, "john"); + let blocked_person = PersonInsertForm::test_form(inserted_instance.id, "john"); let inserted_blocked_person = Person::create(pool, &blocked_person).await?; - let inserted_blocked_local_user = - LocalUser::create(pool, &default_local_user_form(inserted_blocked_person.id)).await?; + let inserted_blocked_local_user = LocalUser::create( + pool, + &LocalUserInsertForm::test_form(inserted_blocked_person.id), + ) + .await?; let post_from_blocked_person = PostInsertForm::builder() .name(POST_BY_BLOCKED_PERSON.to_string()) diff --git a/crates/db_views/src/private_message_view.rs b/crates/db_views/src/private_message_view.rs index d04ff7b49..a3e2469c9 100644 --- a/crates/db_views/src/private_message_view.rs +++ b/crates/db_views/src/private_message_view.rs @@ -12,7 +12,7 @@ use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases, newtypes::{PersonId, PrivateMessageId}, - schema::{person, person_block, private_message}, + schema::{instance_block, person, person_block, private_message}, utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn}, }; use tracing::debug; @@ -34,6 +34,13 @@ fn queries<'a>() -> Queries< .and(person_block::person_id.eq(aliases::person1.field(person::id))), ), ) + .left_join( + instance_block::table.on( + person::instance_id + .eq(instance_block::instance_id) + .and(instance_block::person_id.eq(aliases::person1.field(person::id))), + ), + ) }; let selection = ( @@ -55,7 +62,9 @@ fn queries<'a>() -> Queries< let mut query = all_joins(private_message::table.into_boxed()) .select(selection) // Dont show replies from blocked users - .filter(person_block::person_id.is_null()); + .filter(person_block::person_id.is_null()) + // Dont show replies from blocked instances + .filter(instance_block::person_id.is_null()); // If its unread, I only want the ones to me if options.unread_only { @@ -116,6 +125,8 @@ impl PrivateMessageView { use diesel::dsl::count; let conn = &mut get_conn(pool).await?; private_message::table + // Necessary to get the senders instance_id + .inner_join(person::table.on(private_message::creator_id.eq(person::id))) .left_join( person_block::table.on( private_message::creator_id @@ -123,8 +134,17 @@ impl PrivateMessageView { .and(person_block::person_id.eq(my_person_id)), ), ) + .left_join( + instance_block::table.on( + person::instance_id + .eq(instance_block::instance_id) + .and(instance_block::person_id.eq(my_person_id)), + ), + ) // Dont count replies from blocked users .filter(person_block::person_id.is_null()) + // Dont count replies from blocked instances + .filter(instance_block::person_id.is_null()) .filter(private_message::read.eq(false)) .filter(private_message::recipient_id.eq(my_person_id)) .filter(private_message::deleted.eq(false)) @@ -160,24 +180,30 @@ mod tests { use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView}; use lemmy_db_schema::{ assert_length, + newtypes::InstanceId, source::{ instance::Instance, + instance_block::{InstanceBlock, InstanceBlockForm}, person::{Person, PersonInsertForm}, person_block::{PersonBlock, PersonBlockForm}, private_message::{PrivateMessage, PrivateMessageInsertForm}, }, traits::{Blockable, Crud}, - utils::build_db_pool_for_tests, + utils::{build_db_pool_for_tests, DbPool}, }; + use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; - #[tokio::test] - #[serial] - async fn test_crud() { + struct Data { + instance: Instance, + timmy: Person, + jess: Person, + sara: Person, + } + + async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult { let message_content = String::new(); - let pool = &build_db_pool_for_tests().await; - let pool = &mut pool.into(); let instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) .await @@ -243,6 +269,32 @@ mod tests { .await .unwrap(); + Ok(Data { + instance, + timmy, + jess, + sara, + }) + } + + async fn cleanup(instance_id: InstanceId, pool: &mut DbPool<'_>) -> LemmyResult<()> { + // This also deletes all persons and private messages thanks to sql `on delete cascade` + Instance::delete(pool, instance_id).await.unwrap(); + Ok(()) + } + + #[tokio::test] + #[serial] + async fn read_private_messages() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + let Data { + timmy, + jess, + sara, + instance, + } = init_data(pool).await?; + let timmy_messages = PrivateMessageQuery { unread_only: false, creator_id: None, @@ -303,6 +355,21 @@ mod tests { assert_eq!(timmy_sara_unread_messages[0].creator.id, sara.id); assert_eq!(timmy_sara_unread_messages[0].recipient.id, timmy.id); + cleanup(instance.id, pool).await + } + + #[tokio::test] + #[serial] + async fn ensure_person_block() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + let Data { + timmy, + sara, + instance, + jess: _, + } = init_data(pool).await?; + // Make sure blocks are working let timmy_blocks_sara_form = PersonBlockForm { person_id: timmy.id, @@ -336,7 +403,52 @@ mod tests { .unwrap(); assert_eq!(timmy_unread_messages, 1); - // This also deletes all persons and private messages thanks to sql `on delete cascade` - Instance::delete(pool, instance.id).await.unwrap(); + cleanup(instance.id, pool).await + } + + #[tokio::test] + #[serial] + async fn ensure_instance_block() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + let Data { + timmy, + jess: _, + sara, + instance, + } = init_data(pool).await?; + // Make sure instance_blocks are working + let timmy_blocks_instance_form = InstanceBlockForm { + person_id: timmy.id, + instance_id: sara.instance_id, + }; + + let inserted_instance_block = InstanceBlock::block(pool, &timmy_blocks_instance_form) + .await + .unwrap(); + + let expected_instance_block = InstanceBlock { + person_id: timmy.id, + instance_id: sara.instance_id, + published: inserted_instance_block.published, + }; + assert_eq!(expected_instance_block, inserted_instance_block); + + let timmy_messages = PrivateMessageQuery { + unread_only: true, + creator_id: None, + ..Default::default() + } + .list(pool, timmy.id) + .await + .unwrap(); + + assert_length!(0, &timmy_messages); + + let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id) + .await + .unwrap(); + assert_eq!(timmy_unread_messages, 0); + cleanup(instance.id, pool).await } }