From acd4fa0bd477cdb92f8407c43324a4b05cd10133 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 25 Mar 2024 02:00:57 +0100 Subject: [PATCH] feat: work on authed outbox/inbox --- src/activitypub/user/inbox.rs | 6 +- src/activitypub/user/mod.rs | 6 +- src/activitypub/user/outbox.rs | 140 +++++++++++++++++++++++---------- src/router.rs | 6 +- 4 files changed, 110 insertions(+), 48 deletions(-) diff --git a/src/activitypub/user/inbox.rs b/src/activitypub/user/inbox.rs index a93167a7..fb640387 100644 --- a/src/activitypub/user/inbox.rs +++ b/src/activitypub/user/inbox.rs @@ -3,7 +3,7 @@ use sea_orm::{sea_query::Expr, ColumnTrait, EntityTrait, IntoActiveModel, QueryF use crate::{activitypub::JsonLD, activitystream::{object::{activity::{Activity, ActivityType}, Addressed, ObjectType}, Base, BaseType, Node}, errors::LoggableError, model::{self, activity, addressing, object}, server::Context}; -pub async fn inbox( +pub async fn post( State(ctx): State, Path(_id): Path, Json(object): Json @@ -123,3 +123,7 @@ pub async fn inbox( } } } + +pub async fn get() -> StatusCode { + StatusCode::NOT_IMPLEMENTED +} diff --git a/src/activitypub/user/mod.rs b/src/activitypub/user/mod.rs index 0729c8f6..01fb2087 100644 --- a/src/activitypub/user/mod.rs +++ b/src/activitypub/user/mod.rs @@ -1,8 +1,6 @@ -mod inbox; -pub use inbox::inbox; +pub mod inbox; -mod outbox; -pub use outbox::outbox; +pub mod outbox; mod following; pub use following::follow___; diff --git a/src/activitypub/user/outbox.rs b/src/activitypub/user/outbox.rs index 4ee35978..95b77574 100644 --- a/src/activitypub/user/outbox.rs +++ b/src/activitypub/user/outbox.rs @@ -1,53 +1,111 @@ -use axum::{extract::{Path, Query, State}, http::StatusCode}; -use sea_orm::{EntityTrait, Order, QueryOrder, QuerySelect}; +use axum::{extract::{Path, Query, State}, http::StatusCode, Json}; +use sea_orm::{ColumnTrait, Condition, EntityTrait, Order, QueryFilter, QueryOrder, QuerySelect}; -use crate::{activitypub::{jsonld::LD, JsonLD, Pagination}, activitystream::{object::{activity::ActivityMut, collection::{page::CollectionPageMut, CollectionMut, CollectionType}}, BaseMut, Node}, model::{activity, object}, server::Context, url}; +use crate::{activitypub::{jsonld::LD, JsonLD, Pagination, PUBLIC_TARGET}, activitystream::{object::{activity::{accept::AcceptType, reject::RejectType, ActivityMut, ActivityType}, collection::{page::CollectionPageMut, CollectionMut, CollectionType}}, Base, BaseMut, BaseType, Node, ObjectType}, auth::{AuthIdentity, Identity}, model::{self, activity, object}, server::Context, url}; -pub async fn outbox( +pub async fn get( + State(ctx): State, + Path(id): Path, +) -> Result, StatusCode> { + Ok(JsonLD( + serde_json::Value::new_object() + .set_id(Some(&url!(ctx, "/users/{id}/outbox"))) + .set_collection_type(Some(CollectionType::OrderedCollection)) + .set_first(Node::link(url!(ctx, "/users/{id}/outbox/page"))) + .ld_context() + )) +} + +pub async fn page( State(ctx): State, Path(id): Path, Query(page): Query, + AuthIdentity(auth): AuthIdentity, ) -> Result, StatusCode> { let limit = page.batch.unwrap_or(20).min(50); let offset = page.offset.unwrap_or(0); - if let Some(true) = page.page { - match activity::Entity::find() - .find_also_related(object::Entity) - .order_by(activity::Column::Published, Order::Desc) - .limit(limit) - .offset(offset) - .all(ctx.db()).await - { - Err(_e) => Err(StatusCode::INTERNAL_SERVER_ERROR), - Ok(items) => { - let next = ctx.id(items.last().map(|(a, _o)| a.id.as_str()).unwrap_or("").to_string()); - let items = items - .into_iter() - .map(|(a, o)| - super::super::activity::ap_activity(a) - .set_object(Node::maybe_object(o.map(super::super::object::ap_object))) - ) - .collect(); - Ok(JsonLD( - serde_json::Value::new_object() - // TODO set id, calculate uri from given args - .set_collection_type(Some(CollectionType::OrderedCollectionPage)) - .set_part_of(Node::link(url!(ctx, "/users/{id}/outbox"))) - .set_next(Node::link(url!(ctx, "/users/{id}/outbox?page=true&max_id={next}"))) - .set_ordered_items(Node::array(items)) - .ld_context() - )) - }, - } - } else { - Ok(JsonLD( - serde_json::Value::new_object() - .set_id(Some(&url!(ctx, "/users/{id}/outbox"))) - .set_collection_type(Some(CollectionType::OrderedCollection)) - .set_first(Node::link(url!(ctx, "/users/{id}/outbox?page=true"))) - .ld_context() - )) + let mut conditions = Condition::any() + .add(model::addressing::Column::Actor.eq(PUBLIC_TARGET)); + + if let Identity::User(x) = auth { + conditions = conditions.add(model::addressing::Column::Actor.eq(x)); + } + + if let Identity::Server(x) = auth { + conditions = conditions.add(model::addressing::Column::Server.eq(x)); + } + + let x = model::addressing::Entity::find() + .filter(conditions) + .inner_join(model::activity::Entity) + .left_join(model::object::Entity) + .all(ctx.db()) + .await; + + + match activity::Entity::find() + .find_also_related(object::Entity) + .order_by(activity::Column::Published, Order::Desc) + .limit(limit) + .offset(offset) + .all(ctx.db()).await + { + Err(_e) => Err(StatusCode::INTERNAL_SERVER_ERROR), + Ok(items) => { + Ok(JsonLD( + serde_json::Value::new_object() + // TODO set id, calculate uri from given args + .set_id(Some(&url!(ctx, "/users/{id}/outbox/page?offset={offset}"))) + .set_collection_type(Some(CollectionType::OrderedCollectionPage)) + .set_part_of(Node::link(url!(ctx, "/users/{id}/outbox"))) + .set_next(Node::link(url!(ctx, "/users/{id}/outbox/page?offset={}", limit+offset))) + .set_ordered_items(Node::array( + items + .into_iter() + .map(|(a, o)| + super::super::activity::ap_activity(a) + .set_object(Node::maybe_object(o.map(super::super::object::ap_object))) + ) + .collect() + )) + .ld_context() + )) + }, } } +pub async fn post( + State(ctx): State, + Path(id): Path, + Json(activity): Json, + AuthIdentity(auth): AuthIdentity, +) -> Result, StatusCode> { + match auth { + Identity::Anonymous => Err(StatusCode::UNAUTHORIZED), + Identity::Server(_) => Err(StatusCode::NOT_IMPLEMENTED), + Identity::User(uid) => if ctx.uid(id) == uid { + match activity.base_type() { + None => Err(StatusCode::BAD_REQUEST), + Some(BaseType::Link(_)) => Err(StatusCode::UNPROCESSABLE_ENTITY), + // Some(BaseType::Object(ObjectType::Note)) => { + // }, + // Some(BaseType::Object(ObjectType::Activity(ActivityType::Create))) => { + // }, + // Some(BaseType::Object(ObjectType::Activity(ActivityType::Like))) => { + // }, + // Some(BaseType::Object(ObjectType::Activity(ActivityType::Follow))) => { + // }, + // Some(BaseType::Object(ObjectType::Activity(ActivityType::Undo))) => { + // }, + // Some(BaseType::Object(ObjectType::Activity(ActivityType::Accept(AcceptType::Accept)))) => { + // }, + // Some(BaseType::Object(ObjectType::Activity(ActivityType::Reject(RejectType::Reject)))) => { + // }, + Some(_) => Err(StatusCode::NOT_IMPLEMENTED), + } + } else { + Err(StatusCode::FORBIDDEN) + } + } +} diff --git a/src/router.rs b/src/router.rs index e853ef11..60177d8a 100644 --- a/src/router.rs +++ b/src/router.rs @@ -21,8 +21,10 @@ pub async fn serve(db: DatabaseConnection, domain: String) { .route("/nodeinfo/:version", get(ap::well_known::nodeinfo)) // actor routes .route("/users/:id", get(ap::user::view)) - .route("/users/:id/inbox", post(ap::user::inbox)) - .route("/users/:id/outbox", get(ap::user::outbox)) + .route("/users/:id/inbox", get(ap::user::inbox::get)) + .route("/users/:id/inbox", post(ap::user::inbox::post)) + .route("/users/:id/outbox", get(ap::user::outbox::get)) + .route("/users/:id/outbox/page", get(ap::user::outbox::page)) .route("/users/:id/followers", get(ap::user::follow___::)) .route("/users/:id/following", get(ap::user::follow___::)) // specific object routes