diff --git a/Cargo.lock b/Cargo.lock index c373b81..8a6fffb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "activitypub_federation" -version = "0.4.0-rc1" +version = "0.4.0-rc2" dependencies = [ "activitystreams-kinds", "actix-rt", diff --git a/src/activity_queue.rs b/src/activity_queue.rs index c2e8cbc..fbc8a42 100644 --- a/src/activity_queue.rs +++ b/src/activity_queue.rs @@ -7,7 +7,7 @@ use crate::{ error::Error, http_signatures::sign_request, reqwest_shim::ResponseExt, - traits::ActivityHandler, + traits::{ActivityHandler, Actor}, APUB_JSON_CONTENT_TYPE, }; use anyhow::anyhow; @@ -40,9 +40,9 @@ use url::Url; /// signature. Generated with [crate::http_signatures::generate_actor_keypair]. /// - `inboxes`: List of actor inboxes that should receive the activity. Should be built by calling /// [crate::traits::Actor::shared_inbox_or_inbox] for each target actor. -pub async fn send_activity( +pub async fn send_activity( activity: Activity, - private_key: String, + actor: ActorType, inboxes: Vec, data: &RequestData, ) -> Result<(), ::Error> @@ -50,11 +50,14 @@ where Activity: ActivityHandler + Serialize, ::Error: From + From, Datatype: Clone, + ActorType: Actor, + for<'de2> ActorType::ApubType: Deserialize<'de2>, { let config = &data.config; 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 inboxes: Vec = inboxes .into_iter() .unique() diff --git a/src/actix_web/inbox.rs b/src/actix_web/inbox.rs index fdcce81..04184fd 100644 --- a/src/actix_web/inbox.rs +++ b/src/actix_web/inbox.rs @@ -112,8 +112,8 @@ mod test { let request_builder = ClientWithMiddleware::from(Client::default()).post("https://example.com/inbox"); let activity = Follow { - actor: ObjectId::new("http://localhost:123").unwrap(), - object: ObjectId::new("http://localhost:124").unwrap(), + actor: ObjectId::parse("http://localhost:123").unwrap(), + object: ObjectId::parse("http://localhost:124").unwrap(), kind: Default::default(), id: "http://localhost:123/1".try_into().unwrap(), }; diff --git a/src/fetch/object_id.rs b/src/fetch/object_id.rs index af9b4fa..d803086 100644 --- a/src/fetch/object_id.rs +++ b/src/fetch/object_id.rs @@ -5,9 +5,21 @@ use serde::{Deserialize, Serialize}; use std::{ fmt::{Debug, Display, Formatter}, marker::PhantomData, + str::FromStr, }; use url::Url; +impl FromStr for ObjectId +where + T: ApubObject + Send + 'static, + for<'de2> ::ApubType: Deserialize<'de2>, +{ + type Err = url::ParseError; + + fn from_str(s: &str) -> Result { + ObjectId::parse(s) + } +} /// Typed wrapper for Activitypub Object ID which helps with dereferencing and caching. /// /// It provides convenient methods for fetching the object from remote server or local database. @@ -32,7 +44,7 @@ use url::Url; /// .app_data(db_connection) /// .build()?; /// let request_data = config.to_request_data(); -/// let object_id = ObjectId::::new("https://lemmy.ml/u/nutomic")?; +/// let object_id = ObjectId::::parse("https://lemmy.ml/u/nutomic")?; /// // Attempt to fetch object from local database or fall back to remote server /// let user = object_id.dereference(&request_data).await; /// assert!(user.is_ok()); @@ -55,7 +67,7 @@ where for<'de2> ::ApubType: serde::Deserialize<'de2>, { /// Construct a new objectid instance - pub fn new(url: T) -> Result + pub fn parse(url: T) -> Result where T: TryInto, url::ParseError: From<>::Error>, @@ -238,7 +250,7 @@ pub mod tests { #[test] fn test_deserialize() { - let id = ObjectId::::new("http://test.com/").unwrap(); + let id = ObjectId::::parse("http://test.com/").unwrap(); let string = serde_json::to_string(&id).unwrap(); assert_eq!("\"http://test.com/\"", string); diff --git a/src/protocol/public_key.rs b/src/protocol/public_key.rs index 27dd550..ecfcd3c 100644 --- a/src/protocol/public_key.rs +++ b/src/protocol/public_key.rs @@ -21,7 +21,7 @@ impl PublicKey { /// Create a new [PublicKey] struct for the `owner` with `public_key_pem`. /// /// It uses an standard key id of `{actor_id}#main-key` - pub fn new(owner: Url, public_key_pem: String) -> Self { + pub(crate) fn new(owner: Url, public_key_pem: String) -> Self { let id = main_key_id(&owner); PublicKey { id, diff --git a/src/traits.rs b/src/traits.rs index 4744632..4dd71b1 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -227,7 +227,7 @@ pub trait ActivityHandler { } /// Trait to allow retrieving common Actor data. -pub trait Actor: ApubObject { +pub trait Actor: ApubObject + Send + 'static { /// `id` field of the actor fn id(&self) -> &Url; @@ -237,6 +237,12 @@ pub trait Actor: ApubObject { /// actor keypair. fn public_key_pem(&self) -> &str; + /// The actor's private key for signing outgoing activities. + /// + /// Use [generate_actor_keypair](crate::http_signatures::generate_actor_keypair) to create the + /// actor keypair. + fn private_key_pem(&self) -> Option; + /// The inbox where activities for this user should be sent to fn inbox(&self) -> Url; @@ -412,6 +418,10 @@ pub mod tests { &self.public_key } + fn private_key_pem(&self) -> Option { + self.private_key.clone() + } + fn inbox(&self) -> Url { self.inbox.clone() }