From a4c555d0c53fd1e4c012fb850ba141df4cffbde5 Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 18 Apr 2024 07:02:42 +0200 Subject: [PATCH 1/2] fix: deliveries will try to resolve actor inbox --- src/server/context.rs | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/server/context.rs b/src/server/context.rs index 3e0ffba0..378b1d8a 100644 --- a/src/server/context.rs +++ b/src/server/context.rs @@ -4,7 +4,7 @@ use apb::{BaseMut, CollectionMut, CollectionPageMut}; use openssl::rsa::Rsa; use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect, SelectColumns, Set}; -use crate::{model, routes::activitypub::jsonld::LD}; +use crate::{model, routes::activitypub::jsonld::LD, server::fetcher::Fetcher}; use super::dispatcher::Dispatcher; @@ -176,23 +176,31 @@ impl Context { } pub async fn deliver_to(&self, aid: &str, from: &str, targets: &[String]) -> crate::Result<()> { - let deliveries : Vec = targets - .iter() + let mut deliveries = Vec::new(); + for target in targets.iter() .filter(|to| !to.is_empty()) .filter(|to| Context::server(to) != self.base()) .filter(|to| to != &apb::target::PUBLIC) - .map(|to| model::delivery::ActiveModel { - id: sea_orm::ActiveValue::NotSet, - actor: Set(from.to_string()), - // TODO we should resolve each user by id and check its inbox because we can't assume - // it's /users/{id}/inbox for every software, but oh well it's waaaaay easier now - target: Set(format!("{}/inbox", to)), - activity: Set(aid.to_string()), - created: Set(chrono::Utc::now()), - not_before: Set(chrono::Utc::now()), - attempt: Set(0), - }) - .collect(); + { + // TODO fetch concurrently + match self.fetch_user(target).await { + Ok(model::user::Model { inbox: Some(inbox), .. }) => deliveries.push( + model::delivery::ActiveModel { + id: sea_orm::ActiveValue::NotSet, + actor: Set(from.to_string()), + // TODO we should resolve each user by id and check its inbox because we can't assume + // it's /users/{id}/inbox for every software, but oh well it's waaaaay easier now + target: Set(inbox), + activity: Set(aid.to_string()), + created: Set(chrono::Utc::now()), + not_before: Set(chrono::Utc::now()), + attempt: Set(0), + } + ), + Ok(_) => tracing::error!("resolved target but missing inbox: '{target}', skipping delivery"), + Err(e) => tracing::error!("failed resolving target inbox: {e}, skipping delivery to '{target}'"), + } + } if !deliveries.is_empty() { model::delivery::Entity::insert_many(deliveries) From fc6aedbca17ea2decb3ace4e494d4d2516f3cba2 Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 18 Apr 2024 14:01:55 +0200 Subject: [PATCH 2/2] fix: track deletions and rejected activities --- src/routes/activitypub/inbox.rs | 20 ++++++++++++++------ src/server/inbox.rs | 1 + 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/routes/activitypub/inbox.rs b/src/routes/activitypub/inbox.rs index 92cfe510..23362a33 100644 --- a/src/routes/activitypub/inbox.rs +++ b/src/routes/activitypub/inbox.rs @@ -40,6 +40,11 @@ pub async fn page( )) } +macro_rules! pretty_json { + ($json:ident) => { + serde_json::to_string_pretty(&$json).expect("failed serializing to string serde_json::Value") + } +} pub async fn post( @@ -47,16 +52,19 @@ pub async fn post( AuthIdentity(auth): AuthIdentity, Json(activity): Json ) -> crate::Result<()> { - match auth { - Identity::Remote(_server) => {}, - Identity::Local(_user) => return Err(UpubError::forbidden()), - Identity::Anonymous => return Err(UpubError::unauthorized()), + if !matches!(auth, Identity::Remote(_)) { + tracing::warn!("refusing unauthorized activity: {}", pretty_json!(activity)); + match auth { + Identity::Local(_user) => return Err(UpubError::forbidden()), + Identity::Anonymous => return Err(UpubError::unauthorized()), + _ => {}, + } } // TODO we could process Links and bare Objects maybe, but probably out of AP spec? match activity.activity_type().ok_or_else(UpubError::bad_request)? { ActivityType::Activity => { - tracing::warn!("skipping unprocessable base activity: {}", serde_json::to_string_pretty(&activity).unwrap()); + tracing::warn!("skipping unprocessable base activity: {}", pretty_json!(activity)); Err(StatusCode::UNPROCESSABLE_ENTITY.into()) // won't ingest useless stuff }, @@ -71,7 +79,7 @@ pub async fn post( // ActivityType::Announce => Ok(ctx.announce(activity).await?), _x => { - tracing::info!("received unimplemented activity on inbox: {}", serde_json::to_string_pretty(&activity).unwrap()); + tracing::info!("received unimplemented activity on inbox: {}", pretty_json!(activity)); Err(StatusCode::NOT_IMPLEMENTED.into()) }, } diff --git a/src/server/inbox.rs b/src/server/inbox.rs index ea5e664a..5f2239b9 100644 --- a/src/server/inbox.rs +++ b/src/server/inbox.rs @@ -149,6 +149,7 @@ impl apb::server::Inbox for Context { async fn delete(&self, activity: serde_json::Value) -> crate::Result<()> { // TODO verify the signature before just deleting lmao let oid = activity.object().id().ok_or(UpubError::bad_request())?; + tracing::info!("deleting '{oid}'"); // TODO maybe we should keep the tombstone? model::user::Entity::delete_by_id(&oid).exec(self.db()).await.info_failed("failed deleting from users"); model::activity::Entity::delete_by_id(&oid).exec(self.db()).await.info_failed("failed deleting from activities");