diff --git a/crates/api/src/community.rs b/crates/api/src/community.rs index 5f4fe3c71..8e499414c 100644 --- a/crates/api/src/community.rs +++ b/crates/api/src/community.rs @@ -10,14 +10,11 @@ use lemmy_api_common::{ is_mod_or_admin, }; use lemmy_apub::{ + activities::block::SiteOrCommunity, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::{ - community::{ - add_mod::AddMod, - block_user::BlockUserFromCommunity, - remove_mod::RemoveMod, - undo_block_user::UndoBlockUserFromCommunity, - }, + block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, + community::{add_mod::AddMod, remove_mod::RemoveMod}, following::{follow::FollowCommunity as FollowCommunityApub, undo_follow::UndoFollowCommunity}, }, }; @@ -213,6 +210,7 @@ impl Perform for BanFromCommunity { let community_id = data.community_id; let banned_person_id = data.person_id; + let remove_data = data.remove_data.unwrap_or(false); let expires = data.expires.map(naive_from_unix); // Verify that only mods or admins can ban @@ -254,10 +252,11 @@ impl Perform for BanFromCommunity { .await? .ok(); - BlockUserFromCommunity::send( - &community, + BlockUser::send( + &SiteOrCommunity::Community(community), &banned_person, &local_user_view.person.clone().into(), + remove_data, expires, context, ) @@ -268,8 +267,8 @@ impl Perform for BanFromCommunity { .await? .map_err(LemmyError::from) .map_err(|e| e.with_message("community_user_already_banned"))?; - UndoBlockUserFromCommunity::send( - &community, + UndoBlockUser::send( + &SiteOrCommunity::Community(community), &banned_person, &local_user_view.person.clone().into(), context, @@ -278,7 +277,7 @@ impl Perform for BanFromCommunity { } // Remove/Restore their data if that's desired - if data.remove_data.unwrap_or(false) { + if remove_data { // Posts blocking(context.pool(), move |conn: &'_ _| { Post::update_removed_for_creator(conn, banned_person_id, Some(community_id), true) diff --git a/crates/api/src/local_user.rs b/crates/api/src/local_user.rs index bd32280e7..3418bedc7 100644 --- a/crates/api/src/local_user.rs +++ b/crates/api/src/local_user.rs @@ -14,6 +14,10 @@ use lemmy_api_common::{ send_password_reset_email, send_verification_email, }; +use lemmy_apub::{ + activities::block::SiteOrCommunity, + protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, +}; use lemmy_db_schema::{ diesel_option_overwrite, diesel_option_overwrite_to_url, @@ -443,13 +447,14 @@ impl Perform for BanPerson { let expires = data.expires.map(naive_from_unix); let ban_person = move |conn: &'_ _| Person::ban_person(conn, banned_person_id, ban, expires); - blocking(context.pool(), ban_person) + let person = blocking(context.pool(), ban_person) .await? .map_err(LemmyError::from) .map_err(|e| e.with_message("couldnt_update_user"))?; // Remove their data if that's desired - if data.remove_data.unwrap_or(false) { + let remove_data = data.remove_data.unwrap_or(false); + if remove_data { // Posts blocking(context.pool(), move |conn: &'_ _| { Post::update_removed_for_creator(conn, banned_person_id, None, true) @@ -501,6 +506,31 @@ impl Perform for BanPerson { }) .await??; + let site = SiteOrCommunity::Site( + blocking(context.pool(), Site::read_local_site) + .await?? + .into(), + ); + if ban { + BlockUser::send( + &site, + &person.into(), + &local_user_view.person.into(), + remove_data, + expires, + context, + ) + .await?; + } else { + UndoBlockUser::send( + &site, + &person.into(), + &local_user_view.person.into(), + context, + ) + .await?; + } + let res = BanPersonResponse { person_view, banned: data.ban, diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs index a49e8e4f3..1ece690a0 100644 --- a/crates/api_common/src/lib.rs +++ b/crates/api_common/src/lib.rs @@ -35,7 +35,6 @@ use lemmy_utils::{ LemmyError, Sensitive, }; -use url::Url; pub async fn blocking(pool: &DbPool, f: F) -> Result where @@ -311,7 +310,7 @@ pub async fn build_federated_instances( let mut linked = distinct_communities .iter() - .map(|actor_id| Ok(Url::parse(actor_id)?.host_str().unwrap_or("").to_string())) + .map(|actor_id| Ok(actor_id.host_str().unwrap_or("").to_string())) .collect::, LemmyError>>()?; if let Some(allowed) = allowed.as_ref() { diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs index 9b4266e2a..3e813e475 100644 --- a/crates/api_crud/src/site/create.rs +++ b/crates/api_crud/src/site/create.rs @@ -7,7 +7,7 @@ use lemmy_api_common::{ site::*, site_description_length_check, }; -use lemmy_apub::generate_inbox_url; +use lemmy_apub::generate_site_inbox_url; use lemmy_db_schema::{ diesel_option_overwrite, diesel_option_overwrite_to_url, @@ -75,7 +75,7 @@ impl PerformCrud for CreateSite { enable_nsfw: data.enable_nsfw, community_creation_admin_only: data.community_creation_admin_only, last_refreshed_at: Some(naive_now()), - inbox_url: Some(generate_inbox_url(&actor_id)?), + inbox_url: Some(generate_site_inbox_url(&actor_id)?), private_key: Some(Some(keypair.private_key)), public_key: Some(keypair.public_key), ..SiteForm::default() diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/block/block_user.rs similarity index 68% rename from crates/apub/src/activities/community/block_user.rs rename to crates/apub/src/activities/block/block_user.rs index f5a6f02ce..8411ceb03 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -1,7 +1,9 @@ use crate::{ activities::{ + block::{generate_cc, generate_instance_inboxes, SiteOrCommunity}, community::{announce::GetCommunity, send_activity_in_community}, generate_activity_id, + send_lemmy_activity, verify_activity, verify_is_public, verify_mod_action, @@ -9,9 +11,10 @@ use crate::{ }, activity_lists::AnnouncableActivities, objects::{community::ApubCommunity, person::ApubPerson}, - protocol::activities::community::block_user::BlockUserFromCommunity, + protocol::activities::block::block_user::BlockUser, }; use activitystreams_kinds::{activity::BlockType, public}; +use anyhow::anyhow; use chrono::NaiveDateTime; use lemmy_api_common::blocking; use lemmy_apub_lib::{ @@ -31,21 +34,23 @@ use lemmy_db_schema::{ use lemmy_utils::{utils::convert_datetime, LemmyError}; use lemmy_websocket::LemmyContext; -impl BlockUserFromCommunity { - pub(in crate::activities::community) fn new( - community: &ApubCommunity, - target: &ApubPerson, - actor: &ApubPerson, +impl BlockUser { + pub(in crate::activities::block) async fn new( + target: &SiteOrCommunity, + user: &ApubPerson, + mod_: &ApubPerson, + remove_data: Option, expires: Option, context: &LemmyContext, - ) -> Result { - Ok(BlockUserFromCommunity { - actor: ObjectId::new(actor.actor_id()), + ) -> Result { + Ok(BlockUser { + actor: ObjectId::new(mod_.actor_id()), to: vec![public()], - object: ObjectId::new(target.actor_id()), - cc: vec![community.actor_id()], - target: ObjectId::new(community.actor_id()), + object: ObjectId::new(user.actor_id()), + cc: generate_cc(target, context.pool()).await?, + target: target.id(), kind: BlockType::Block, + remove_data, id: generate_activity_id( BlockType::Block, &context.settings().get_protocol_and_hostname(), @@ -57,23 +62,32 @@ impl BlockUserFromCommunity { #[tracing::instrument(skip_all)] pub async fn send( - community: &ApubCommunity, - target: &ApubPerson, - actor: &ApubPerson, + target: &SiteOrCommunity, + user: &ApubPerson, + mod_: &ApubPerson, + remove_data: bool, expires: Option, context: &LemmyContext, ) -> Result<(), LemmyError> { - let block = BlockUserFromCommunity::new(community, target, actor, expires, context)?; + let block = BlockUser::new(target, user, mod_, Some(remove_data), expires, context).await?; let block_id = block.id.clone(); - let activity = AnnouncableActivities::BlockUserFromCommunity(block); - let inboxes = vec![target.shared_inbox_or_inbox_url()]; - send_activity_in_community(activity, &block_id, actor, community, inboxes, context).await + match target { + SiteOrCommunity::Site(_) => { + let inboxes = generate_instance_inboxes(user, context.pool()).await?; + send_lemmy_activity(context, &block, &block_id, mod_, inboxes, false).await + } + SiteOrCommunity::Community(c) => { + let activity = AnnouncableActivities::BlockUser(block); + let inboxes = vec![user.shared_inbox_or_inbox_url()]; + send_activity_in_community(activity, &block_id, mod_, c, inboxes, context).await + } + } } } #[async_trait::async_trait(?Send)] -impl ActivityHandler for BlockUserFromCommunity { +impl ActivityHandler for BlockUser { type DataType = LemmyContext; #[tracing::instrument(skip_all)] @@ -130,16 +144,20 @@ impl ActivityHandler for BlockUserFromCommunity { } #[async_trait::async_trait(?Send)] -impl GetCommunity for BlockUserFromCommunity { +impl GetCommunity for BlockUser { #[tracing::instrument(skip_all)] async fn get_community( &self, context: &LemmyContext, request_counter: &mut i32, ) -> Result { - self + let target = self .target .dereference(context, context.client(), request_counter) - .await + .await?; + match target { + SiteOrCommunity::Community(c) => Ok(c), + SiteOrCommunity::Site(_) => Err(anyhow!("Calling get_community() on site activity").into()), + } } } diff --git a/crates/apub/src/activities/block/mod.rs b/crates/apub/src/activities/block/mod.rs new file mode 100644 index 000000000..7460acec8 --- /dev/null +++ b/crates/apub/src/activities/block/mod.rs @@ -0,0 +1,144 @@ +use crate::{ + objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson}, + protocol::objects::{group::Group, instance::Instance}, +}; +use chrono::NaiveDateTime; +use lemmy_api_common::blocking; +use lemmy_apub_lib::{ + object_id::ObjectId, + traits::{ActorType, ApubObject}, +}; +use lemmy_db_schema::{source::site::Site, DbPool}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use serde::Deserialize; +use url::Url; + +pub mod block_user; +pub mod undo_block_user; + +#[derive(Clone, Debug)] +pub enum SiteOrCommunity { + Site(ApubSite), + Community(ApubCommunity), +} + +#[derive(Deserialize)] +#[serde(untagged)] +pub enum InstanceOrGroup { + Instance(Instance), + Group(Group), +} + +#[async_trait::async_trait(?Send)] +impl ApubObject for SiteOrCommunity { + type DataType = LemmyContext; + type ApubType = InstanceOrGroup; + type TombstoneType = (); + + #[tracing::instrument(skip_all)] + fn last_refreshed_at(&self) -> Option { + Some(match self { + SiteOrCommunity::Site(i) => i.last_refreshed_at, + SiteOrCommunity::Community(c) => c.last_refreshed_at, + }) + } + + #[tracing::instrument(skip_all)] + async fn read_from_apub_id( + object_id: Url, + data: &Self::DataType, + ) -> Result, LemmyError> + where + Self: Sized, + { + let site = ApubSite::read_from_apub_id(object_id.clone(), data).await?; + Ok(match site { + Some(o) => Some(SiteOrCommunity::Site(o)), + None => ApubCommunity::read_from_apub_id(object_id, data) + .await? + .map(SiteOrCommunity::Community), + }) + } + + async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> { + unimplemented!() + } + + async fn into_apub(self, _data: &Self::DataType) -> Result { + unimplemented!() + } + + fn to_tombstone(&self) -> Result { + unimplemented!() + } + + #[tracing::instrument(skip_all)] + async fn verify( + apub: &Self::ApubType, + expected_domain: &Url, + data: &Self::DataType, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + match apub { + InstanceOrGroup::Instance(i) => { + ApubSite::verify(i, expected_domain, data, request_counter).await + } + InstanceOrGroup::Group(g) => { + ApubCommunity::verify(g, expected_domain, data, request_counter).await + } + } + } + + #[tracing::instrument(skip_all)] + async fn from_apub( + apub: Self::ApubType, + data: &Self::DataType, + request_counter: &mut i32, + ) -> Result + where + Self: Sized, + { + Ok(match apub { + InstanceOrGroup::Instance(p) => { + SiteOrCommunity::Site(ApubSite::from_apub(p, data, request_counter).await?) + } + InstanceOrGroup::Group(n) => { + SiteOrCommunity::Community(ApubCommunity::from_apub(n, data, request_counter).await?) + } + }) + } +} + +impl SiteOrCommunity { + fn id(&self) -> ObjectId { + match self { + SiteOrCommunity::Site(s) => ObjectId::new(s.actor_id.clone()), + SiteOrCommunity::Community(c) => ObjectId::new(c.actor_id.clone()), + } + } +} + +async fn generate_cc(target: &SiteOrCommunity, pool: &DbPool) -> Result, LemmyError> { + Ok(match target { + SiteOrCommunity::Site(_) => blocking(pool, Site::read_remote_sites) + .await?? + .into_iter() + .map(|s| s.actor_id.into()) + .collect(), + SiteOrCommunity::Community(c) => vec![c.actor_id()], + }) +} + +async fn generate_instance_inboxes( + blocked_user: &ApubPerson, + pool: &DbPool, +) -> Result, LemmyError> { + let mut inboxes: Vec = blocking(pool, Site::read_remote_sites) + .await?? + .into_iter() + .map(|s| s.inbox_url.into()) + .collect(); + inboxes.push(blocked_user.shared_inbox_or_inbox_url()); + Ok(inboxes) +} diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs similarity index 72% rename from crates/apub/src/activities/community/undo_block_user.rs rename to crates/apub/src/activities/block/undo_block_user.rs index a62315ddd..2c6a0bcf4 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/block/undo_block_user.rs @@ -1,7 +1,9 @@ use crate::{ activities::{ + block::{generate_cc, generate_instance_inboxes, SiteOrCommunity}, community::{announce::GetCommunity, send_activity_in_community}, generate_activity_id, + send_lemmy_activity, verify_activity, verify_is_public, verify_mod_action, @@ -9,10 +11,7 @@ use crate::{ }, activity_lists::AnnouncableActivities, objects::{community::ApubCommunity, person::ApubPerson}, - protocol::activities::community::{ - block_user::BlockUserFromCommunity, - undo_block_user::UndoBlockUserFromCommunity, - }, + protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, }; use activitystreams_kinds::{activity::UndoType, public}; use lemmy_api_common::blocking; @@ -28,38 +27,46 @@ use lemmy_db_schema::{ use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; -impl UndoBlockUserFromCommunity { +impl UndoBlockUser { #[tracing::instrument(skip_all)] pub async fn send( - community: &ApubCommunity, - target: &ApubPerson, - actor: &ApubPerson, + target: &SiteOrCommunity, + user: &ApubPerson, + mod_: &ApubPerson, context: &LemmyContext, ) -> Result<(), LemmyError> { - let block = BlockUserFromCommunity::new(community, target, actor, None, context)?; + let block = BlockUser::new(target, user, mod_, None, None, context).await?; let id = generate_activity_id( UndoType::Undo, &context.settings().get_protocol_and_hostname(), )?; - let undo = UndoBlockUserFromCommunity { - actor: ObjectId::new(actor.actor_id()), + let undo = UndoBlockUser { + actor: ObjectId::new(mod_.actor_id()), to: vec![public()], object: block, - cc: vec![community.actor_id()], + cc: generate_cc(target, context.pool()).await?, kind: UndoType::Undo, id: id.clone(), unparsed: Default::default(), }; - let activity = AnnouncableActivities::UndoBlockUserFromCommunity(undo); - let inboxes = vec![target.shared_inbox_or_inbox_url()]; - send_activity_in_community(activity, &id, actor, community, inboxes, context).await + let inboxes = vec![user.shared_inbox_or_inbox_url()]; + match target { + SiteOrCommunity::Site(_) => { + let inboxes = generate_instance_inboxes(user, context.pool()).await?; + send_lemmy_activity(context, &undo, &id, mod_, inboxes, false).await + } + SiteOrCommunity::Community(c) => { + let activity = AnnouncableActivities::UndoBlockUser(undo); + send_activity_in_community(activity, &id, mod_, c, inboxes, context).await + } + } } } #[async_trait::async_trait(?Send)] -impl ActivityHandler for UndoBlockUserFromCommunity { +impl ActivityHandler for UndoBlockUser { type DataType = LemmyContext; #[tracing::instrument(skip_all)] @@ -106,7 +113,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity { } #[async_trait::async_trait(?Send)] -impl GetCommunity for UndoBlockUserFromCommunity { +impl GetCommunity for UndoBlockUser { #[tracing::instrument(skip_all)] async fn get_community( &self, diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 670b9a7b0..d8d8097eb 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -11,10 +11,8 @@ use url::Url; pub mod add_mod; pub mod announce; -pub mod block_user; pub mod remove_mod; pub mod report; -pub mod undo_block_user; pub mod update; #[tracing::instrument(skip_all)] diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index e09d69255..343633c8f 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -25,6 +25,7 @@ use tracing::info; use url::{ParseError, Url}; use uuid::Uuid; +pub mod block; pub mod comment; pub mod community; pub mod deletion; diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs index 0512ed62c..c8666b93a 100644 --- a/crates/apub/src/activity_lists.rs +++ b/crates/apub/src/activity_lists.rs @@ -3,13 +3,12 @@ use crate::{ objects::community::ApubCommunity, protocol::{ activities::{ + block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, community::{ add_mod::AddMod, announce::AnnounceActivity, - block_user::BlockUserFromCommunity, remove_mod::RemoveMod, report::Report, - undo_block_user::UndoBlockUserFromCommunity, update::UpdateCommunity, }, create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost}, @@ -78,14 +77,22 @@ pub enum AnnouncableActivities { Delete(Delete), UndoDelete(UndoDelete), UpdateCommunity(UpdateCommunity), - BlockUserFromCommunity(BlockUserFromCommunity), - UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), + BlockUser(BlockUser), + UndoBlockUser(UndoBlockUser), AddMod(AddMod), RemoveMod(RemoveMod), // For compatibility with Pleroma/Mastodon (send only) Page(Page), } +#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] +#[serde(untagged)] +#[activity_handler(LemmyContext)] +pub enum SiteInboxActivities { + BlockUser(BlockUser), + UndoBlockUser(UndoBlockUser), +} + #[async_trait::async_trait(?Send)] impl GetCommunity for AnnouncableActivities { #[tracing::instrument(skip(self, context))] @@ -103,8 +110,8 @@ impl GetCommunity for AnnouncableActivities { Delete(a) => a.get_community(context, request_counter).await?, UndoDelete(a) => a.get_community(context, request_counter).await?, UpdateCommunity(a) => a.get_community(context, request_counter).await?, - BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, - UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?, + BlockUser(a) => a.get_community(context, request_counter).await?, + UndoBlockUser(a) => a.get_community(context, request_counter).await?, AddMod(a) => a.get_community(context, request_counter).await?, RemoveMod(a) => a.get_community(context, request_counter).await?, Page(_) => unimplemented!(), diff --git a/crates/apub/src/http/routes.rs b/crates/apub/src/http/routes.rs index 271c1b14e..c62a45ac8 100644 --- a/crates/apub/src/http/routes.rs +++ b/crates/apub/src/http/routes.rs @@ -11,7 +11,7 @@ use crate::http::{ person::{get_apub_person_http, get_apub_person_outbox, person_inbox}, post::get_apub_post, shared_inbox, - site::get_apub_site_http, + site::{get_apub_site_http, get_apub_site_inbox, get_apub_site_outbox}, }; use actix_web::{ guard::{Guard, GuardContext}, @@ -28,7 +28,8 @@ pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) { cfg .route("/", web::get().to(get_apub_site_http)) - .route("/site_outbox", web::get().to(get_apub_site_http)) + .route("/site_outbox", web::get().to(get_apub_site_outbox)) + .route("/site_inbox", web::get().to(get_apub_site_inbox)) .route( "/c/{community_name}", web::get().to(get_apub_community_http), diff --git a/crates/apub/src/http/site.rs b/crates/apub/src/http/site.rs index 3439ef1d9..26be5f924 100644 --- a/crates/apub/src/http/site.rs +++ b/crates/apub/src/http/site.rs @@ -1,14 +1,17 @@ use crate::{ - http::create_apub_response, + activity_lists::SiteInboxActivities, + context::WithContext, + http::{create_apub_response, payload_to_string, receive_activity, ActivityCommonFields}, objects::instance::ApubSite, protocol::collections::empty_outbox::EmptyOutbox, }; -use actix_web::{web, HttpResponse}; +use actix_web::{web, web::Payload, HttpRequest, HttpResponse}; use lemmy_api_common::blocking; use lemmy_apub_lib::traits::ApubObject; use lemmy_db_schema::source::site::Site; use lemmy_utils::{settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; +use tracing::info; use url::Url; pub(crate) async fn get_apub_site_http( @@ -31,3 +34,17 @@ pub(crate) async fn get_apub_site_outbox() -> Result { let outbox = EmptyOutbox::new(Url::parse(&outbox_id)?).await?; Ok(create_apub_response(&outbox)) } + +#[tracing::instrument(skip_all)] +pub async fn get_apub_site_inbox( + request: HttpRequest, + payload: Payload, + _path: web::Path, + context: web::Data, +) -> Result { + let unparsed = payload_to_string(payload).await?; + info!("Received site inbox activity {}", unparsed); + let activity_data: ActivityCommonFields = serde_json::from_str(&unparsed)?; + let activity = serde_json::from_str::>(&unparsed)?; + receive_activity(request, activity.inner(), activity_data, &context).await +} diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index 94752a397..4e33ba392 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -164,6 +164,10 @@ pub fn generate_inbox_url(actor_id: &DbUrl) -> Result { Ok(Url::parse(&format!("{}/inbox", actor_id))?.into()) } +pub fn generate_site_inbox_url(actor_id: &DbUrl) -> Result { + Ok(Url::parse(&format!("{}/site_inbox", actor_id))?.into()) +} + pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result { let actor_id: Url = actor_id.clone().into(); let url = format!( diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index 8070c131a..4a618bf88 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -3,7 +3,7 @@ use crate::{ collections::{community_moderators::ApubCommunityModerators, CommunityContext}, generate_moderators_url, generate_outbox_url, - objects::instance::{instance_actor_id_from_url, ApubSite}, + objects::instance::fetch_instance_actor_for_object, protocol::{ objects::{group::Group, tombstone::Tombstone, Endpoints}, ImageObject, @@ -26,7 +26,7 @@ use lemmy_utils::{ }; use lemmy_websocket::LemmyContext; use std::ops::Deref; -use tracing::{debug, info}; +use tracing::debug; use url::Url; #[derive(Clone, Debug)] @@ -153,14 +153,7 @@ impl ApubObject for ApubCommunity { .ok(); } - // try to fetch the instance actor (to make things like instance rules available) - let instance_id = instance_actor_id_from_url(community.actor_id.clone().into()); - let site = ObjectId::::new(instance_id.clone()) - .dereference(context, context.client(), request_counter) - .await; - if let Err(e) = site { - info!("Failed to dereference site for {}: {}", instance_id, e); - } + fetch_instance_actor_for_object(community.actor_id(), context, request_counter).await; Ok(community) } diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs index 333092b1f..cdd76c5a9 100644 --- a/crates/apub/src/objects/instance.rs +++ b/crates/apub/src/objects/instance.rs @@ -1,6 +1,5 @@ use crate::{ check_is_apub_id_valid, - generate_outbox_url, objects::get_summary_from_string_or_source, protocol::{objects::instance::Instance, ImageObject, Source, Unparsed}, }; @@ -23,6 +22,7 @@ use lemmy_utils::{ }; use lemmy_websocket::LemmyContext; use std::ops::Deref; +use tracing::debug; use url::Url; #[derive(Clone, Debug)] @@ -82,7 +82,7 @@ impl ApubObject for ApubSite { icon: self.icon.clone().map(ImageObject::new), image: self.banner.clone().map(ImageObject::new), inbox: self.inbox_url.clone().into(), - outbox: generate_outbox_url(&self.actor_id)?.into(), + outbox: Url::parse(&format!("{}/site_outbox", self.actor_id))?, public_key: self.get_public_key()?, published: convert_datetime(self.published), updated: self.updated.map(convert_datetime), @@ -167,6 +167,22 @@ pub fn instance_actor_id_from_url(mut url: Url) -> Url { url } +/// try to fetch the instance actor (to make things like instance rules available) +pub(in crate::objects) async fn fetch_instance_actor_for_object( + object_id: Url, + context: &LemmyContext, + request_counter: &mut i32, +) { + // try to fetch the instance actor (to make things like instance rules available) + let instance_id = instance_actor_id_from_url(object_id); + let site = ObjectId::::new(instance_id.clone()) + .dereference(context, context.client(), request_counter) + .await; + if let Err(e) = site { + debug!("Failed to dereference site for {}: {}", instance_id, e); + } +} + #[cfg(test)] pub(crate) mod tests { use super::*; diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index 14245acf7..a4a6db9fa 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -1,7 +1,7 @@ use crate::{ check_is_apub_id_valid, generate_outbox_url, - objects::get_summary_from_string_or_source, + objects::{get_summary_from_string_or_source, instance::fetch_instance_actor_for_object}, protocol::{ objects::{ person::{Person, UserTypes}, @@ -137,7 +137,7 @@ impl ApubObject for ApubPerson { async fn from_apub( person: Person, context: &LemmyContext, - _request_counter: &mut i32, + request_counter: &mut i32, ) -> Result { let person_form = PersonForm { name: person.preferred_username, @@ -168,6 +168,10 @@ impl ApubObject for ApubPerson { DbPerson::upsert(conn, &person_form) }) .await??; + + let actor_id = person.actor_id.clone().into(); + fetch_instance_actor_for_object(actor_id, context, request_counter).await; + Ok(person.into()) } } diff --git a/crates/apub/src/protocol/activities/community/block_user.rs b/crates/apub/src/protocol/activities/block/block_user.rs similarity index 70% rename from crates/apub/src/protocol/activities/community/block_user.rs rename to crates/apub/src/protocol/activities/block/block_user.rs index 891fe1f55..690835d57 100644 --- a/crates/apub/src/protocol/activities/community/block_user.rs +++ b/crates/apub/src/protocol/activities/block/block_user.rs @@ -1,7 +1,4 @@ -use crate::{ - objects::{community::ApubCommunity, person::ApubPerson}, - protocol::Unparsed, -}; +use crate::{activities::block::SiteOrCommunity, objects::person::ApubPerson, protocol::Unparsed}; use activitystreams_kinds::activity::BlockType; use chrono::{DateTime, FixedOffset}; use lemmy_apub_lib::object_id::ObjectId; @@ -10,16 +7,19 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct BlockUserFromCommunity { +pub struct BlockUser { pub(crate) actor: ObjectId, #[serde(deserialize_with = "crate::deserialize_one_or_many")] pub(crate) to: Vec, pub(crate) object: ObjectId, #[serde(deserialize_with = "crate::deserialize_one_or_many")] pub(crate) cc: Vec, - pub(crate) target: ObjectId, + pub(crate) target: ObjectId, #[serde(rename = "type")] pub(crate) kind: BlockType, + /// Quick and dirty solution. + /// TODO: send a separate Delete activity instead + pub(crate) remove_data: Option, pub(crate) id: Url, #[serde(flatten)] pub(crate) unparsed: Unparsed, diff --git a/crates/apub/src/protocol/activities/block/mod.rs b/crates/apub/src/protocol/activities/block/mod.rs new file mode 100644 index 000000000..75f169b8a --- /dev/null +++ b/crates/apub/src/protocol/activities/block/mod.rs @@ -0,0 +1,20 @@ +pub mod block_user; +pub mod undo_block_user; + +#[cfg(test)] +mod tests { + use crate::protocol::{ + activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, + tests::test_parse_lemmy_item, + }; + + #[actix_rt::test] + async fn test_parse_lemmy_block() { + test_parse_lemmy_item::("assets/lemmy/activities/community/block_user.json") + .unwrap(); + test_parse_lemmy_item::( + "assets/lemmy/activities/community/undo_block_user.json", + ) + .unwrap(); + } +} diff --git a/crates/apub/src/protocol/activities/community/undo_block_user.rs b/crates/apub/src/protocol/activities/block/undo_block_user.rs similarity index 78% rename from crates/apub/src/protocol/activities/community/undo_block_user.rs rename to crates/apub/src/protocol/activities/block/undo_block_user.rs index 02218367b..d3db580e4 100644 --- a/crates/apub/src/protocol/activities/community/undo_block_user.rs +++ b/crates/apub/src/protocol/activities/block/undo_block_user.rs @@ -1,6 +1,6 @@ use crate::{ objects::person::ApubPerson, - protocol::{activities::community::block_user::BlockUserFromCommunity, Unparsed}, + protocol::{activities::block::block_user::BlockUser, Unparsed}, }; use activitystreams_kinds::activity::UndoType; use lemmy_apub_lib::object_id::ObjectId; @@ -9,11 +9,11 @@ use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct UndoBlockUserFromCommunity { +pub struct UndoBlockUser { pub(crate) actor: ObjectId, #[serde(deserialize_with = "crate::deserialize_one_or_many")] pub(crate) to: Vec, - pub(crate) object: BlockUserFromCommunity, + pub(crate) object: BlockUser, #[serde(deserialize_with = "crate::deserialize_one_or_many")] pub(crate) cc: Vec, #[serde(rename = "type")] diff --git a/crates/apub/src/protocol/activities/community/mod.rs b/crates/apub/src/protocol/activities/community/mod.rs index 56332a877..77ffa9313 100644 --- a/crates/apub/src/protocol/activities/community/mod.rs +++ b/crates/apub/src/protocol/activities/community/mod.rs @@ -1,22 +1,21 @@ pub mod add_mod; pub mod announce; -pub mod block_user; pub mod remove_mod; pub mod report; -pub mod undo_block_user; pub mod update; #[cfg(test)] mod tests { use crate::protocol::{ - activities::community::{ - add_mod::AddMod, - announce::AnnounceActivity, - block_user::BlockUserFromCommunity, - remove_mod::RemoveMod, - report::Report, - undo_block_user::UndoBlockUserFromCommunity, - update::UpdateCommunity, + activities::{ + block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, + community::{ + add_mod::AddMod, + announce::AnnounceActivity, + remove_mod::RemoveMod, + report::Report, + update::UpdateCommunity, + }, }, tests::test_parse_lemmy_item, }; @@ -32,11 +31,9 @@ mod tests { test_parse_lemmy_item::("assets/lemmy/activities/community/remove_mod.json") .unwrap(); - test_parse_lemmy_item::( - "assets/lemmy/activities/community/block_user.json", - ) - .unwrap(); - test_parse_lemmy_item::( + test_parse_lemmy_item::("assets/lemmy/activities/community/block_user.json") + .unwrap(); + test_parse_lemmy_item::( "assets/lemmy/activities/community/undo_block_user.json", ) .unwrap(); diff --git a/crates/apub/src/protocol/activities/mod.rs b/crates/apub/src/protocol/activities/mod.rs index 2aa488f57..47b01b241 100644 --- a/crates/apub/src/protocol/activities/mod.rs +++ b/crates/apub/src/protocol/activities/mod.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use strum_macros::Display; +pub mod block; pub mod community; pub mod create_or_update; pub mod deletion; diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index d2b0d9cde..b2b3a6d81 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -122,9 +122,9 @@ impl Community { .get_result::(conn) } - pub fn distinct_federated_communities(conn: &PgConnection) -> Result, Error> { + pub fn distinct_federated_communities(conn: &PgConnection) -> Result, Error> { use crate::schema::community::dsl::*; - community.select(actor_id).distinct().load::(conn) + community.select(actor_id).distinct().load::(conn) } pub fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result { diff --git a/crates/db_schema/src/impls/site.rs b/crates/db_schema/src/impls/site.rs index d38ef9715..60d2c013c 100644 --- a/crates/db_schema/src/impls/site.rs +++ b/crates/db_schema/src/impls/site.rs @@ -54,4 +54,9 @@ impl Site { .map(Into::into), ) } + + pub fn read_remote_sites(conn: &PgConnection) -> Result, Error> { + use crate::schema::site::dsl::*; + site.order_by(id).offset(1).get_results::(conn) + } } diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs index b863a2500..3e68768bc 100644 --- a/crates/db_schema/src/newtypes.rs +++ b/crates/db_schema/src/newtypes.rs @@ -10,6 +10,7 @@ use std::{ fmt, fmt::{Display, Formatter}, io::Write, + ops::Deref, }; use url::Url; @@ -125,3 +126,11 @@ where DbUrl(id.into()) } } + +impl Deref for DbUrl { + type Target = Url; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/src/code_migrations.rs b/src/code_migrations.rs index 07a969ec5..161dd3b60 100644 --- a/src/code_migrations.rs +++ b/src/code_migrations.rs @@ -8,6 +8,7 @@ use lemmy_apub::{ generate_inbox_url, generate_local_apub_endpoint, generate_shared_inbox_url, + generate_site_inbox_url, EndpointType, }; use lemmy_db_schema::{ @@ -304,7 +305,7 @@ fn instance_actor_2022_01_28( name: site.name, actor_id: Some(actor_id.clone().into()), last_refreshed_at: Some(naive_now()), - inbox_url: Some(generate_inbox_url(&actor_id.into())?), + inbox_url: Some(generate_site_inbox_url(&actor_id.into())?), private_key: Some(Some(key_pair.private_key)), public_key: Some(key_pair.public_key), ..Default::default()