diff --git a/src/activity_queue.rs b/src/activity_queue.rs index 04eb0a7..8f7a6b7 100644 --- a/src/activity_queue.rs +++ b/src/activity_queue.rs @@ -56,7 +56,9 @@ where let actor_id = activity.actor(); let activity_id = activity.id(); let activity_serialized = serde_json::to_string_pretty(&activity)?; - let private_key = actor.private_key_pem().unwrap(); + let private_key = actor + .private_key_pem() + .expect("Actor for sending activity has private key"); let inboxes: Vec = inboxes .into_iter() .unique() diff --git a/src/fetch/collection_id.rs b/src/fetch/collection_id.rs index 28c4cd2..4524e6f 100644 --- a/src/fetch/collection_id.rs +++ b/src/fetch/collection_id.rs @@ -1,4 +1,4 @@ -use crate::{error::Error, traits::ApubCollection}; +use crate::{config::Data, error::Error, fetch::fetch_object_http, traits::ApubCollection}; use serde::{Deserialize, Serialize}; use std::{ fmt::{Debug, Display, Formatter}, @@ -6,16 +6,19 @@ use std::{ }; use url::Url; -/// TODO: implement and document this, update trait docs -/// TODO: which handlers need to receive owner, and should it be simply an url or what? -/// TODO: current trait impl without read_from_apub_id() method wont work for http handlers -/// -> maybe remove method into_apub() and instead add read_local() (reads from db and -/// directly returns serialized collection) +/// Typed wrapper for Activitypub Collection ID which helps with dereferencing. #[derive(Serialize, Deserialize)] #[serde(transparent)] -pub struct CollectionId(Box, PhantomData); +pub struct CollectionId(Box, PhantomData) +where + Kind: ApubCollection, + for<'de2> ::ApubType: Deserialize<'de2>; -impl CollectionId { +impl CollectionId +where + Kind: ApubCollection, + for<'de2> ::ApubType: Deserialize<'de2>, +{ /// Construct a new CollectionId instance pub fn parse(url: T) -> Result where @@ -25,14 +28,30 @@ impl CollectionId { Ok(Self(Box::new(url.try_into()?), PhantomData::)) } - /// TODO - pub async fn dereference(&self, _: T) -> Result { - todo!() + /// Fetches collection over HTTP + /// + /// Unlike [ObjectId::fetch](crate::fetch::object_id::ObjectId::fetch) this method doesn't do + /// any caching. + pub async fn dereference( + &self, + owner: &::Owner, + data: &Data<::DataType>, + ) -> Result::Error> + where + ::Error: From, + { + let apub = fetch_object_http(&self.0, data).await?; + Kind::verify(&apub, &self.0, data).await?; + Kind::from_apub(apub, owner, data).await } } /// Need to implement clone manually, to avoid requiring Kind to be Clone -impl Clone for CollectionId { +impl Clone for CollectionId +where + Kind: ApubCollection, + for<'de2> ::ApubType: serde::Deserialize<'de2>, +{ fn clone(&self) -> Self { CollectionId(self.0.clone(), self.1) } diff --git a/src/protocol/context.rs b/src/protocol/context.rs index 7a1f24d..433f8f3 100644 --- a/src/protocol/context.rs +++ b/src/protocol/context.rs @@ -79,3 +79,15 @@ where self.inner.receive(data).await } } + +impl Clone for WithContext +where + T: Clone, +{ + fn clone(&self) -> Self { + Self { + context: self.context.clone(), + inner: self.inner.clone(), + } + } +} diff --git a/src/traits.rs b/src/traits.rs index 4b98794..90e5369 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -3,6 +3,7 @@ use crate::{config::Data, protocol::public_key::PublicKey}; use async_trait::async_trait; use chrono::NaiveDateTime; +use serde::Deserialize; use std::{fmt::Debug, ops::Deref}; use url::Url; @@ -151,8 +152,8 @@ pub trait ApubObject: Sized { /// Convert object from ActivityPub type to database type. /// /// Called when an object is received from HTTP fetch or as part of an activity. This method - /// should do verification and write the received object to database. Note that there is no - /// distinction between create and update, so an `upsert` operation should be used. + /// should write the received object to database. Note that there is no distinction between + /// create and update, so an `upsert` operation should be used. async fn from_apub( apub: Self::ApubType, data: &Data, @@ -252,7 +253,7 @@ pub trait Actor: ApubObject + Send + 'static { /// Generates a public key struct for use in the actor json representation fn public_key(&self) -> PublicKey { - PublicKey::new(self.id().clone(), self.public_key_pem().to_string()) + PublicKey::new(self.id(), self.public_key_pem().to_string()) } /// The actor's shared inbox, if any @@ -301,17 +302,13 @@ pub trait ApubCollection: Sized { /// [crate::config::FederationConfigBuilder::app_data] type. type DataType: Clone + Send + Sync; /// The type of protocol struct which gets sent over network to federate this database struct. - type ApubType; + type ApubType: for<'de2> Deserialize<'de2>; /// Error type returned by handler methods type Error; - /// Convert database type to Activitypub type. - /// - /// Called when a local object gets fetched by another instance over HTTP, or when an object - /// gets sent in an activity. - async fn into_apub( - self, - owner: Self::Owner, + /// Reads local collection from database and returns it as Activitypub JSON. + async fn read_local( + owner: &Self::Owner, data: &Data, ) -> Result; @@ -319,12 +316,8 @@ pub trait ApubCollection: Sized { /// /// You should check here that the domain of id matches `expected_domain`. Additionally you /// should perform any application specific checks. - /// - /// It is necessary to use a separate method for this, because it might be used for activities - /// like `Delete/Note`, which shouldn't perform any database write for the inner `Note`. async fn verify( apub: &Self::ApubType, - owner: Self::Owner, expected_domain: &Url, data: &Data, ) -> Result<(), Self::Error>; @@ -332,11 +325,11 @@ pub trait ApubCollection: Sized { /// Convert object from ActivityPub type to database type. /// /// Called when an object is received from HTTP fetch or as part of an activity. This method - /// should do verification and write the received object to database. Note that there is no - /// distinction between create and update, so an `upsert` operation should be used. + /// should also write the received object to database. Note that there is no distinction + /// between create and update, so an `upsert` operation should be used. async fn from_apub( apub: Self::ApubType, - owner: Self::Owner, + owner: &Self::Owner, data: &Data, ) -> Result; }