From af8d11e75b742112ea08d4120bb48ac4f421bf61 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 12 Apr 2024 19:36:00 +0200 Subject: [PATCH] feat: inbox/outbox security and obj embedding --- src/routes/activitypub/activity.rs | 1 + src/routes/activitypub/inbox.rs | 14 ++++------- src/routes/activitypub/object.rs | 1 + src/routes/activitypub/user/inbox.rs | 29 +++++++++-------------- src/routes/activitypub/user/outbox.rs | 34 ++++++++++----------------- src/server/auth.rs | 12 ++++++++++ 6 files changed, 42 insertions(+), 49 deletions(-) diff --git a/src/routes/activitypub/activity.rs b/src/routes/activitypub/activity.rs index ac5667d..7126190 100644 --- a/src/routes/activitypub/activity.rs +++ b/src/routes/activitypub/activity.rs @@ -5,6 +5,7 @@ use apb::{ActivityMut, ObjectMut, BaseMut, Node}; use super::{jsonld::LD, JsonLD}; +// TODO this is used outside /routes, maybe move in model? pub fn ap_activity(activity: model::activity::Model) -> serde_json::Value { serde_json::Value::new_object() .set_id(Some(&activity.id)) diff --git a/src/routes/activitypub/inbox.rs b/src/routes/activitypub/inbox.rs index 6b53c75..4cb77b2 100644 --- a/src/routes/activitypub/inbox.rs +++ b/src/routes/activitypub/inbox.rs @@ -1,7 +1,7 @@ use axum::{extract::{Query, State}, http::StatusCode}; -use sea_orm::{ColumnTrait, Condition, EntityTrait, Order, QueryFilter, QueryOrder, QuerySelect}; +use sea_orm::{Order, QueryFilter, QueryOrder, QuerySelect}; -use crate::{server::auth::{AuthIdentity, Identity}, errors::UpubError, model, server::Context, url}; +use crate::{server::auth::AuthIdentity, errors::UpubError, model, server::Context, url}; use super::{activity::ap_activity, jsonld::LD, JsonLD, Pagination}; @@ -19,14 +19,8 @@ pub async fn page( ) -> Result, UpubError> { let limit = page.batch.unwrap_or(20).min(50); let offset = page.offset.unwrap_or(0); - let mut condition = Condition::any() - .add(model::addressing::Column::Actor.eq(apb::target::PUBLIC)); - if let Identity::Local(user) = auth { - condition = condition - .add(model::addressing::Column::Actor.eq(user)); - } - let activities = model::addressing::Entity::find() - .filter(condition) + let activities = model::addressing::Entity::find_activities() + .filter(auth.filter_condition()) .order_by(model::addressing::Column::Published, Order::Asc) .find_also_related(model::activity::Entity) .limit(limit) diff --git a/src/routes/activitypub/object.rs b/src/routes/activitypub/object.rs index 40d7cbd..f0e85b6 100644 --- a/src/routes/activitypub/object.rs +++ b/src/routes/activitypub/object.rs @@ -6,6 +6,7 @@ use crate::{model::{self, object}, server::Context}; use super::{jsonld::LD, JsonLD}; +// TODO this is used outside /routes, maybe move in model? pub fn ap_object(object: model::object::Model) -> serde_json::Value { serde_json::Value::new_object() .set_id(Some(&object.id)) diff --git a/src/routes/activitypub/user/inbox.rs b/src/routes/activitypub/user/inbox.rs index 2d310ca..26718bf 100644 --- a/src/routes/activitypub/user/inbox.rs +++ b/src/routes/activitypub/user/inbox.rs @@ -1,8 +1,8 @@ use axum::{extract::{Path, Query, State}, http::StatusCode, Json}; -use sea_orm::{ColumnTrait, Condition, EntityTrait, JoinType, Order, QueryFilter, QueryOrder, QuerySelect, RelationTrait}; use apb::{server::Inbox, ActivityMut, ActivityType, Base, BaseType, ObjectType}; -use crate::{errors::UpubError, model, routes::activitypub::{activity::ap_activity, jsonld::LD, object::ap_object, JsonLD, Pagination}, server::{auth::{AuthIdentity, Identity}, Context}, tools::ActivityWithObject, url}; +use sea_orm::{ColumnTrait, Condition, QueryFilter, QuerySelect}; +use crate::{errors::UpubError, model::{self, addressing::EmbeddedActivity}, routes::activitypub::{activity::ap_activity, jsonld::LD, object::ap_object, JsonLD, Pagination}, server::{auth::{AuthIdentity, Identity}, Context}, url}; pub async fn get( State(ctx): State, @@ -33,20 +33,11 @@ pub async fn page( Identity::Local(user) => if uid == user { let limit = page.batch.unwrap_or(20).min(50); let offset = page.offset.unwrap_or(0); - let select = model::addressing::Entity::find() - .filter(Condition::all().add(model::addressing::Column::Actor.eq(uid))) - .order_by(model::addressing::Column::Published, Order::Asc) - .select_only(); - - match crate::tools::Prefixer::new(select) - .add_columns(model::activity::Entity) - .add_columns(model::object::Entity) - .selector - .join(JoinType::LeftJoin, model::activity::Relation::Addressing.def().rev()) - .join(JoinType::LeftJoin, model::object::Relation::Activity.def().rev()) - .limit(limit) + match model::addressing::Entity::find_activities() + .filter(Condition::all().add(model::addressing::Column::Actor.eq(&user))) .offset(offset) - .into_model::() + .limit(limit) + .into_model::() .all(ctx.db()) .await { @@ -57,9 +48,11 @@ pub async fn page( offset, limit, activities .into_iter() - .map(|ActivityWithObject { activity, object }| { - ap_activity(activity) - .set_object(apb::Node::maybe_object(object.map(ap_object))) + .map(|EmbeddedActivity { activity, object }| match object { + None => ap_activity(activity), + Some(x) => + ap_activity(activity) + .set_object(apb::Node::object(ap_object(x))), }) .collect::>() ).ld_context() diff --git a/src/routes/activitypub/user/outbox.rs b/src/routes/activitypub/user/outbox.rs index 1c97acd..c6c7aa8 100644 --- a/src/routes/activitypub/user/outbox.rs +++ b/src/routes/activitypub/user/outbox.rs @@ -1,8 +1,8 @@ use axum::{extract::{Path, Query, State}, http::StatusCode, Json}; -use sea_orm::{EntityTrait, Order, QueryOrder, QuerySelect}; +use sea_orm::{ColumnTrait, Condition, Order, QueryFilter, QueryOrder, QuerySelect}; use apb::{server::Outbox, AcceptType, ActivityMut, ActivityType, Base, BaseType, Node, ObjectType, RejectType}; -use crate::{routes::activitypub::{jsonld::LD, CreationResult, JsonLD, Pagination}, server::auth::{AuthIdentity, Identity}, errors::UpubError, model, server::Context, url}; +use crate::{errors::UpubError, model::{self, addressing::EmbeddedActivity}, routes::activitypub::{jsonld::LD, CreationResult, JsonLD, Pagination}, server::{auth::{AuthIdentity, Identity}, Context}, url}; pub async fn get( State(ctx): State, @@ -17,27 +17,19 @@ pub async fn page( State(ctx): State, Path(id): Path, Query(page): Query, - AuthIdentity(_auth): AuthIdentity, + AuthIdentity(auth): AuthIdentity, ) -> Result, StatusCode> { + let uid = ctx.uid(id.clone()); let limit = page.batch.unwrap_or(20).min(50); let offset = page.offset.unwrap_or(0); - // let mut conditions = Condition::any() - // .add(model::addressing::Column::Actor.eq(PUBLIC_TARGET)); - - // if let Identity::User(ref x) = auth { - // conditions = conditions.add(model::addressing::Column::Actor.eq(x)); - // } - - // if let Identity::Server(ref x) = auth { - // conditions = conditions.add(model::addressing::Column::Server.eq(x)); - // } - - match model::activity::Entity::find() - .find_also_related(model::object::Entity) - .order_by(model::activity::Column::Published, Order::Desc) + match model::addressing::Entity::find_activities() + .filter(Condition::all().add(model::activity::Column::Actor.eq(&uid))) + .filter(auth.filter_condition()) + .order_by(model::addressing::Column::Published, Order::Desc) .limit(limit) .offset(offset) + .into_model::() .all(ctx.db()).await { Err(_e) => Err(StatusCode::INTERNAL_SERVER_ERROR), @@ -48,10 +40,10 @@ pub async fn page( offset, limit, items .into_iter() - .map(|(a, o)| { - let oid = a.object.clone(); - super::super::activity::ap_activity(a) - .set_object(match o { + .map(|EmbeddedActivity { activity, object }| { + let oid = activity.object.clone(); + super::super::activity::ap_activity(activity) + .set_object(match object { Some(o) => Node::object(super::super::object::ap_object(o)), None => Node::maybe_link(oid), }) diff --git a/src/server/auth.rs b/src/server/auth.rs index 51eaea3..9469e6a 100644 --- a/src/server/auth.rs +++ b/src/server/auth.rs @@ -13,6 +13,18 @@ pub enum Identity { Remote(String), } +impl Identity { + pub fn filter_condition(&self) -> Condition { + let base_cond = Condition::all().add(model::addressing::Column::Actor.eq(apb::target::PUBLIC)); + match self { + Identity::Anonymous => base_cond, + Identity::Local(uid) => base_cond.add(model::addressing::Column::Actor.eq(uid)), + Identity::Remote(server) => base_cond.add(model::addressing::Column::Server.eq(server)), + // TODO should we allow all users on same server to see? or just specific user?? + } + } +} + pub struct AuthIdentity(pub Identity); #[axum::async_trait]