feat: inbox/outbox security and obj embedding

This commit is contained in:
əlemi 2024-04-12 19:36:00 +02:00
parent 0d340185cf
commit af8d11e75b
Signed by: alemi
GPG key ID: A4895B84D311642C
6 changed files with 42 additions and 49 deletions

View file

@ -5,6 +5,7 @@ use apb::{ActivityMut, ObjectMut, BaseMut, Node};
use super::{jsonld::LD, JsonLD}; 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 { pub fn ap_activity(activity: model::activity::Model) -> serde_json::Value {
serde_json::Value::new_object() serde_json::Value::new_object()
.set_id(Some(&activity.id)) .set_id(Some(&activity.id))

View file

@ -1,7 +1,7 @@
use axum::{extract::{Query, State}, http::StatusCode}; 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}; use super::{activity::ap_activity, jsonld::LD, JsonLD, Pagination};
@ -19,14 +19,8 @@ pub async fn page(
) -> Result<JsonLD<serde_json::Value>, UpubError> { ) -> Result<JsonLD<serde_json::Value>, UpubError> {
let limit = page.batch.unwrap_or(20).min(50); let limit = page.batch.unwrap_or(20).min(50);
let offset = page.offset.unwrap_or(0); let offset = page.offset.unwrap_or(0);
let mut condition = Condition::any() let activities = model::addressing::Entity::find_activities()
.add(model::addressing::Column::Actor.eq(apb::target::PUBLIC)); .filter(auth.filter_condition())
if let Identity::Local(user) = auth {
condition = condition
.add(model::addressing::Column::Actor.eq(user));
}
let activities = model::addressing::Entity::find()
.filter(condition)
.order_by(model::addressing::Column::Published, Order::Asc) .order_by(model::addressing::Column::Published, Order::Asc)
.find_also_related(model::activity::Entity) .find_also_related(model::activity::Entity)
.limit(limit) .limit(limit)

View file

@ -6,6 +6,7 @@ use crate::{model::{self, object}, server::Context};
use super::{jsonld::LD, JsonLD}; 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 { pub fn ap_object(object: model::object::Model) -> serde_json::Value {
serde_json::Value::new_object() serde_json::Value::new_object()
.set_id(Some(&object.id)) .set_id(Some(&object.id))

View file

@ -1,8 +1,8 @@
use axum::{extract::{Path, Query, State}, http::StatusCode, Json}; 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 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( pub async fn get(
State(ctx): State<Context>, State(ctx): State<Context>,
@ -33,20 +33,11 @@ pub async fn page(
Identity::Local(user) => if uid == user { Identity::Local(user) => if uid == user {
let limit = page.batch.unwrap_or(20).min(50); let limit = page.batch.unwrap_or(20).min(50);
let offset = page.offset.unwrap_or(0); let offset = page.offset.unwrap_or(0);
let select = model::addressing::Entity::find() match model::addressing::Entity::find_activities()
.filter(Condition::all().add(model::addressing::Column::Actor.eq(uid))) .filter(Condition::all().add(model::addressing::Column::Actor.eq(&user)))
.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)
.offset(offset) .offset(offset)
.into_model::<crate::tools::ActivityWithObject>() .limit(limit)
.into_model::<EmbeddedActivity>()
.all(ctx.db()) .all(ctx.db())
.await .await
{ {
@ -57,9 +48,11 @@ pub async fn page(
offset, limit, offset, limit,
activities activities
.into_iter() .into_iter()
.map(|ActivityWithObject { activity, object }| { .map(|EmbeddedActivity { activity, object }| match object {
ap_activity(activity) None => ap_activity(activity),
.set_object(apb::Node::maybe_object(object.map(ap_object))) Some(x) =>
ap_activity(activity)
.set_object(apb::Node::object(ap_object(x))),
}) })
.collect::<Vec<serde_json::Value>>() .collect::<Vec<serde_json::Value>>()
).ld_context() ).ld_context()

View file

@ -1,8 +1,8 @@
use axum::{extract::{Path, Query, State}, http::StatusCode, Json}; 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 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( pub async fn get(
State(ctx): State<Context>, State(ctx): State<Context>,
@ -17,27 +17,19 @@ pub async fn page(
State(ctx): State<Context>, State(ctx): State<Context>,
Path(id): Path<String>, Path(id): Path<String>,
Query(page): Query<Pagination>, Query(page): Query<Pagination>,
AuthIdentity(_auth): AuthIdentity, AuthIdentity(auth): AuthIdentity,
) -> Result<JsonLD<serde_json::Value>, StatusCode> { ) -> Result<JsonLD<serde_json::Value>, StatusCode> {
let uid = ctx.uid(id.clone());
let limit = page.batch.unwrap_or(20).min(50); let limit = page.batch.unwrap_or(20).min(50);
let offset = page.offset.unwrap_or(0); let offset = page.offset.unwrap_or(0);
// let mut conditions = Condition::any() match model::addressing::Entity::find_activities()
// .add(model::addressing::Column::Actor.eq(PUBLIC_TARGET)); .filter(Condition::all().add(model::activity::Column::Actor.eq(&uid)))
.filter(auth.filter_condition())
// if let Identity::User(ref x) = auth { .order_by(model::addressing::Column::Published, Order::Desc)
// 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)
.limit(limit) .limit(limit)
.offset(offset) .offset(offset)
.into_model::<EmbeddedActivity>()
.all(ctx.db()).await .all(ctx.db()).await
{ {
Err(_e) => Err(StatusCode::INTERNAL_SERVER_ERROR), Err(_e) => Err(StatusCode::INTERNAL_SERVER_ERROR),
@ -48,10 +40,10 @@ pub async fn page(
offset, limit, offset, limit,
items items
.into_iter() .into_iter()
.map(|(a, o)| { .map(|EmbeddedActivity { activity, object }| {
let oid = a.object.clone(); let oid = activity.object.clone();
super::super::activity::ap_activity(a) super::super::activity::ap_activity(activity)
.set_object(match o { .set_object(match object {
Some(o) => Node::object(super::super::object::ap_object(o)), Some(o) => Node::object(super::super::object::ap_object(o)),
None => Node::maybe_link(oid), None => Node::maybe_link(oid),
}) })

View file

@ -13,6 +13,18 @@ pub enum Identity {
Remote(String), 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); pub struct AuthIdentity(pub Identity);
#[axum::async_trait] #[axum::async_trait]