Add simple activitypub traits/types, reorganize facade

This commit is contained in:
Aode (lion) 2021-11-28 13:45:35 -06:00
parent 83bc589bc1
commit cfca60a966
18 changed files with 520 additions and 118 deletions

View file

@ -13,7 +13,7 @@ with-background-jobs = ["apub-background-jobs"]
with-openssl = ["apub-openssl"]
with-reqwest = ["apub-reqwest"]
with-rustcrypto = ["apub-rustcrypto"]
utils = ["apub-breaker-session", "apub-deref-client"]
utils = ["apub-breaker-session", "apub-deref-client", "apub-publickey", "apub-simple-activitypub"]
[dependencies]
apub-actix-web = { version = "0.1.0", path = "./apub-actix-web/", optional = true }
@ -23,9 +23,10 @@ apub-breaker-session = { version = "0.1.0", path = "./apub-breaker-session/", op
apub-core = { version = "0.1.0", path = "./apub-core/" }
apub-deref-client = { version = "0.1.0", path = "./apub-deref-client/", optional = true }
apub-openssl = { version = "0.1.0", path = "./apub-openssl/", optional = true }
apub-publickey = { version = "0.1.0", path = "./apub-publickey/" }
apub-publickey = { version = "0.1.0", path = "./apub-publickey/", optional = true }
apub-reqwest = { version = "0.1.0", path = "./apub-reqwest/", optional = true }
apub-rustcrypto = { version = "0.1.0", path = "./apub-rustcrypto/", optional = true }
apub-simple-activitypub = { version = "0.1.0", path = "./apub-simple-activitypub/", optional = true }
[dev-dependencies]
async-trait = "0.1.51"
@ -49,6 +50,7 @@ members = [
"apub-publickey",
"apub-reqwest",
"apub-rustcrypto",
"apub-simple-activitypub",
"examples/actix-web-example",
"examples/awc-example",
"examples/background-jobs-example",

View file

@ -12,7 +12,7 @@ use actix_web::{
FromRequest, HttpRequest, HttpResponse, ResponseError,
};
use apub_core::{
deref::{Dereference, Repo, RepoFactory},
repo::{Dereference, Repo, RepoFactory},
digest::{Digest, DigestBuilder, DigestFactory},
ingest::{Authority, Ingest},
session::SessionFactory,

View file

@ -5,7 +5,7 @@
use actix_http::error::BlockingError;
use apub_core::{
deliver::Deliver,
deref::{Dereference, Repo},
repo::{Dereference, Repo},
digest::{Digest, DigestBuilder, DigestFactory},
session::{Session, SessionError},
signature::{Sign, SignFactory},

View file

@ -0,0 +1,93 @@
//! Traits desccribing basic activitystreams types
use url::Url;
/// A type that represents a Public Key
pub trait PublicKey {
/// The Public Key's ID
fn id(&self) -> &Url;
/// The Public Key's Owner
fn owner(&self) -> &Url;
/// The Public Key's pem-encoded value
fn public_key_pem(&self) -> &str;
}
/// A type that represents an Object
pub trait Object {
/// The Object's ID
fn id(&self) -> &Url;
/// The kind of Object
fn kind(&self) -> &str;
/// Who the object is intended for
fn to(&self) -> &[Url];
/// Additional actors the object should be delivered to
fn cc(&self) -> &[Url];
}
/// A type that represents an Activity
pub trait Activity {
/// The Actor performing the activity
fn actor_id(&self) -> &Url;
/// The object being performed upon
fn object_id(&self) -> &Url;
}
/// A type that represents an Actor
pub trait Actor {
/// An Actor has a Public Key
type PublicKey: PublicKey;
/// The ID of the actor
fn id(&self) -> &Url;
/// The kind of actor
fn kind(&self) -> &str;
/// The actor's inbox
fn inbox(&self) -> &Url;
/// The actor's outbox
fn outbox(&self) -> &Url;
/// The actor's preferred username (handle)
fn preferred_username(&self) -> &str;
/// The actor's public key
fn public_key(&self) -> &Self::PublicKey;
/// The actor's display name
fn name(&self) -> Option<&str>;
/// The actor's shared inbox
fn shared_inbox(&self) -> Option<&Url>;
}
static PUBLIC: &'static str = "https://www.w3.org/ns/activitystreams#Public";
/// Determine the name of an actor
pub fn canonical_name(act: &impl Actor) -> &str {
act.name().unwrap_or_else(|| act.preferred_username())
}
/// Determine whether an object is public
pub fn is_public(obj: &impl Object) -> bool {
obj.to()
.iter()
.chain(obj.cc())
.any(|url| url.as_str() == PUBLIC)
}
/// Determine which inbox an object should be delievered to
pub fn delivery_inbox<'a>(actor: &'a impl Actor, obj: &impl Object) -> &'a Url {
if is_public(obj) {
actor.shared_inbox().unwrap_or_else(|| actor.inbox())
} else {
actor.inbox()
}
}

View file

@ -2,10 +2,11 @@
#![deny(missing_docs)]
pub mod activitystreams;
pub mod deliver;
pub mod deref;
pub mod digest;
pub mod ingest;
pub mod object_id;
pub mod repo;
pub mod session;
pub mod signature;

View file

@ -14,7 +14,7 @@ use url::Url;
/// type.
///
/// ```rust
/// use apub_core::deref::Dereference;
/// use apub_core::repo::Dereference;
/// use url::Url;
///
/// #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]

View file

@ -11,7 +11,7 @@ use url::Url;
/// for fetching local objects from a database
///
/// ```rust
/// use apub_core::{deref::{Dereference, Repo}, session::Session};
/// use apub_core::{repo::{Dereference, Repo}, session::Session};
/// use std::{collections::HashMap, sync::{Arc, Mutex}};
/// use url::Url;
///
@ -61,7 +61,7 @@ pub trait RepoFactory {
/// dereference implementation, can be passed to a Repository to fetch the T object it represents
///
/// ```rust
/// use apub_core::deref::{Dereference, Repo};
/// use apub_core::repo::{Dereference, Repo};
/// use std::borrow::Cow;
/// use url::Url;
///

View file

@ -1,7 +1,7 @@
//! A type to combine a remote Repo with a local Repo
//!
//! ```rust
//! use apub_core::{deref::{Dereference, Repo}, session::Session};
//! use apub_core::{repo::{Dereference, Repo}, session::Session};
//! use apub_deref_client::Client;
//! use apub_openssl::OpenSsl;
//! use apub_reqwest::{ReqwestClient, SignatureConfig};
@ -108,7 +108,7 @@
#![deny(missing_docs)]
use apub_core::{
deref::{Dereference, Repo},
repo::{Dereference, Repo},
session::Session,
};
use url::{Host, Url};

View file

@ -1,5 +1,6 @@
use apub_core::{
deref::{Dereference, Repo},
activitystreams::PublicKey,
repo::{Dereference, Repo},
session::Session,
};
use std::{
@ -12,9 +13,12 @@ use url::{Host, Url};
pub trait PublicKeyRepo {
type Error: std::error::Error;
async fn store(&self, public_key: &PublicKey) -> Result<(), Self::Error>;
async fn store(&self, public_key: &SimplePublicKey) -> Result<(), Self::Error>;
async fn fetch(&self, public_key_id: &PublicKeyId) -> Result<Option<PublicKey>, Self::Error>;
async fn fetch(
&self,
public_key_id: &PublicKeyId,
) -> Result<Option<SimplePublicKey>, Self::Error>;
}
pub trait PublicKeyRepoFactory {
@ -42,6 +46,40 @@ pub enum PublicKeyError<E, R> {
RemoteRepo(#[source] R),
}
#[derive(
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Deserialize, serde::Serialize,
)]
#[serde(transparent)]
pub struct PublicKeyId(pub Url);
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SimplePublicKey {
pub id: PublicKeyId,
pub owner: Url,
pub public_key_pem: String,
}
#[derive(Clone, Debug, serde::Deserialize)]
#[serde(untagged)]
#[serde(rename_all = "camelCase")]
pub enum PublicKeyResponse {
/// I dream of the day when asking for a Public Key gives me a public key
PublicKey(SimplePublicKey),
/// Getting an Actor from a PublicKey fetch is a bug IMO but mastodon does it so :shrug:
Actor {
id: Url,
public_key: SimplePublicKey,
},
}
impl PublicKeyId {
pub fn into_inner(this: Self) -> Url {
this.0
}
}
impl<LocalRepo, RemoteRepo> PublicKeyClient<LocalRepo, RemoteRepo>
where
LocalRepo: PublicKeyRepo,
@ -65,7 +103,7 @@ where
&self,
public_key_id: &PublicKeyId,
session: S,
) -> Result<Option<PublicKey>, PublicKeyError<LocalRepo::Error, RemoteRepo::Error>> {
) -> Result<Option<SimplePublicKey>, PublicKeyError<LocalRepo::Error, RemoteRepo::Error>> {
let opt = self
.local
.fetch(public_key_id)
@ -113,37 +151,6 @@ fn borrow_host(host: &Host<String>) -> Host<&str> {
}
}
#[derive(
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Deserialize, serde::Serialize,
)]
#[serde(transparent)]
pub struct PublicKeyId(pub Url);
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKey {
pub id: PublicKeyId,
pub owner: Url,
pub public_key_pem: String,
}
#[derive(Clone, Debug, serde::Deserialize)]
#[serde(untagged)]
#[serde(rename_all = "camelCase")]
pub enum PublicKeyResponse {
/// I dream of the day when asking for a Public Key gives me a public key
PublicKey(PublicKey),
/// Getting an Actor from a PublicKey fetch is a bug IMO but mastodon does it so :shrug:
Actor { id: Url, public_key: PublicKey },
}
impl PublicKeyId {
pub fn into_inner(this: Self) -> Url {
this.0
}
}
impl FromStr for PublicKeyId {
type Err = <Url as FromStr>::Err;
@ -191,3 +198,17 @@ impl From<Url> for PublicKeyId {
PublicKeyId(url)
}
}
impl PublicKey for SimplePublicKey {
fn id(&self) -> &Url {
&self.id
}
fn owner(&self) -> &Url {
&self.owner
}
fn public_key_pem(&self) -> &str {
&self.public_key_pem
}
}

View file

@ -3,7 +3,7 @@
#![deny(missing_docs)]
use apub_core::{
deliver::Deliver,
deref::{Dereference, Repo},
repo::{Dereference, Repo},
digest::{Digest, DigestBuilder, DigestFactory},
session::{Session, SessionError},
signature::{Sign, SignFactory},

View file

@ -0,0 +1,13 @@
[package]
name = "apub-simple-activitypub"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
apub-core = { version = "0.1.0", path = "../apub-core/" }
apub-publickey = { version = "0.1.0", path = "../apub-publickey/" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
url = { version = "2", features = ["serde"] }

View file

@ -0,0 +1,254 @@
use apub_core::activitystreams::{Activity, Actor, Object};
use apub_publickey::SimplePublicKey;
use serde_json::Map;
use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SimpleObject {
pub id: Url,
#[serde(rename = "type")]
pub kind: String,
#[serde(deserialize_with = "one_or_many")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub to: Vec<Url>,
#[serde(deserialize_with = "one_or_many")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub cc: Vec<Url>,
#[serde(flatten)]
pub rest: Map<String, serde_json::Value>,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SimpleActivity {
pub id: Url,
#[serde(rename = "type")]
pub kind: String,
#[serde(deserialize_with = "one_or_many")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub to: Vec<Url>,
#[serde(deserialize_with = "one_or_many")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub cc: Vec<Url>,
pub actor: Either<Url, SimpleActor>,
pub object: Either<Url, Either<Box<SimpleActivity>, SimpleObject>>,
#[serde(flatten)]
pub rest: Map<String, serde_json::Value>,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SimpleActor {
pub id: Url,
#[serde(rename = "type")]
pub kind: String,
pub inbox: Url,
pub outbox: Url,
pub preferred_username: String,
pub public_key: SimplePublicKey,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoints: Option<SimpleEndpoints>,
#[serde(flatten)]
pub rest: Map<String, serde_json::Value>,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SimpleEndpoints {
#[serde(skip_serializing_if = "Option::is_none")]
pub shared_inbox: Option<Url>,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(untagged)]
pub enum Either<Left, Right> {
Left(Left),
Right(Right),
}
impl Object for SimpleObject {
fn id(&self) -> &Url {
&self.id
}
fn kind(&self) -> &str {
&self.kind
}
fn to(&self) -> &[Url] {
&self.to
}
fn cc(&self) -> &[Url] {
&self.cc
}
}
impl Object for SimpleActivity {
fn id(&self) -> &Url {
&self.id
}
fn kind(&self) -> &str {
&self.kind
}
fn to(&self) -> &[Url] {
&self.to
}
fn cc(&self) -> &[Url] {
&self.cc
}
}
impl Activity for SimpleActivity {
fn actor_id(&self) -> &Url {
match &self.actor {
Either::Left(ref id) => id,
Either::Right(obj) => obj.id(),
}
}
fn object_id(&self) -> &Url {
match &self.object {
Either::Left(ref id) => id,
Either::Right(Either::Left(activity)) => activity.id(),
Either::Right(Either::Right(object)) => object.id(),
}
}
}
impl Actor for SimpleActor {
type PublicKey = SimplePublicKey;
fn id(&self) -> &Url {
&self.id
}
fn kind(&self) -> &str {
&self.kind
}
fn inbox(&self) -> &Url {
&self.inbox
}
fn outbox(&self) -> &Url {
&self.outbox
}
fn preferred_username(&self) -> &str {
&self.preferred_username
}
fn public_key(&self) -> &Self::PublicKey {
&self.public_key
}
fn name(&self) -> Option<&str> {
self.name.as_deref()
}
fn shared_inbox(&self) -> Option<&Url> {
self.endpoints
.as_ref()
.and_then(|endpoints| endpoints.shared_inbox.as_ref())
}
}
fn one_or_many<'de, D>(deserializer: D) -> Result<Vec<Url>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum OneOrMany {
Single(Url),
Many(Vec<Url>),
}
let one_or_many: Option<OneOrMany> = serde::de::Deserialize::deserialize(deserializer)?;
let v = match one_or_many {
Some(OneOrMany::Many(v)) => v,
Some(OneOrMany::Single(o)) => vec![o],
None => vec![],
};
Ok(v)
}
#[cfg(test)]
mod tests {
use super::*;
use apub_core::activitystreams::{is_public, PublicKey};
static ASONIX: &'static str = r#"{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","toot":"http://joinmastodon.org/ns#","featured":{"@id":"toot:featured","@type":"@id"},"featuredTags":{"@id":"toot:featuredTags","@type":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","@type":"@id"},"movedTo":{"@id":"as:movedTo","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value","IdentityProof":"toot:IdentityProof","discoverable":"toot:discoverable","Device":"toot:Device","Ed25519Signature":"toot:Ed25519Signature","Ed25519Key":"toot:Ed25519Key","Curve25519Key":"toot:Curve25519Key","EncryptedMessage":"toot:EncryptedMessage","publicKeyBase64":"toot:publicKeyBase64","deviceId":"toot:deviceId","claim":{"@type":"@id","@id":"toot:claim"},"fingerprintKey":{"@type":"@id","@id":"toot:fingerprintKey"},"identityKey":{"@type":"@id","@id":"toot:identityKey"},"devices":{"@type":"@id","@id":"toot:devices"},"messageFranking":"toot:messageFranking","messageType":"toot:messageType","cipherText":"toot:cipherText","suspended":"toot:suspended","focalPoint":{"@container":"@list","@id":"toot:focalPoint"}}],"id":"https://masto.asonix.dog/users/asonix","type":"Person","following":"https://masto.asonix.dog/users/asonix/following","followers":"https://masto.asonix.dog/users/asonix/followers","inbox":"https://masto.asonix.dog/users/asonix/inbox","outbox":"https://masto.asonix.dog/users/asonix/outbox","featured":"https://masto.asonix.dog/users/asonix/collections/featured","featuredTags":"https://masto.asonix.dog/users/asonix/collections/tags","preferredUsername":"asonix","name":"Alleged Cat Crime Committer","summary":"\u003cp\u003elocal liom, friend, rust (lang) stan, bi \u003c/p\u003e\u003cp\u003eicon by \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://t.me/dropmutt\" target=\"blank\" rel=\"noopener noreferrer\" class=\"u-url mention\"\u003e@\u003cspan\u003edropmutt@telegram.org\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e\u003cbr /\u003eheader by \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://furaffinity.net/user/tronixx\" target=\"blank\" rel=\"noopener noreferrer\" class=\"u-url mention\"\u003e@\u003cspan\u003etronixx@furaffinity.net\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e\u003c/p\u003e\u003cp\u003eTestimonials:\u003c/p\u003e\u003cp\u003eStand: LIONS\u003cbr /\u003eStand User: AODE\u003cbr /\u003e- Keris (not on here)\u003c/p\u003e","url":"https://masto.asonix.dog/@asonix","manuallyApprovesFollowers":true,"discoverable":false,"published":"2021-02-09T00:00:00Z","devices":"https://masto.asonix.dog/users/asonix/collections/devices","publicKey":{"id":"https://masto.asonix.dog/users/asonix#main-key","owner":"https://masto.asonix.dog/users/asonix","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm+YpyXb3bUp5EyryHqRA\npKSvl4RamJh6CLlngYYPFU8lcx92oQR8nFlqOwInAczGPoCoIfojQpZfqV4hFq1I\nlETy6jHHeoO/YkUsH2dYtz6gjEqiZFCFpoWuGxUQO3lwfmPYpxl2/GFEDR4MrUNp\n9fPn9jHUlKydiDkFQqluajqSJgv0BCwnUGBanTEfeQKahnc3OqPTi4xNbsd2cbAW\nZtJ6VYepphQCRHElvkzefe1ra5qm5i8YBdan3Z3oo5wN1vo3u41tqjVGhDptKZkv\nwBevdL0tedoLp5Lj1l/HLTSBP0D0ZT/HUFuo6Zq27PCq/4ZgJaZkMi7YCVVtpjim\nmQIDAQAB\n-----END PUBLIC KEY-----\n"},"tag":[],"attachment":[{"type":"PropertyValue","name":"pronouns","value":"he/they"},{"type":"PropertyValue","name":"software","value":"bad"},{"type":"PropertyValue","name":"gitea","value":"\u003ca href=\"https://git.asonix.dog\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egit.asonix.dog\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"},{"type":"PropertyValue","name":"join my","value":"relay"}],"endpoints":{"sharedInbox":"https://masto.asonix.dog/inbox"},"icon":{"type":"Image","mediaType":"image/png","url":"https://masto.asonix.dog/system/accounts/avatars/000/000/001/original/4f8d8f520ca26354.png"},"image":{"type":"Image","mediaType":"image/png","url":"https://masto.asonix.dog/system/accounts/headers/000/000/001/original/8122ce3e5a745385.png"}}"#;
static CREATE: &'static str = r#"{"id":"https://masto.asonix.dog/users/asonix/statuses/107355727205658651/activity","type":"Create","actor":"https://masto.asonix.dog/users/asonix","published":"2021-11-28T16:53:16Z","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://masto.asonix.dog/users/asonix/followers"],"object":{"id":"https://masto.asonix.dog/users/asonix/statuses/107355727205658651","type":"Note","summary":null,"inReplyTo":null,"published":"2021-11-28T16:53:16Z","url":"https://masto.asonix.dog/@asonix/107355727205658651","attributedTo":"https://masto.asonix.dog/users/asonix","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://masto.asonix.dog/users/asonix/followers"],"sensitive":false,"atomUri":"https://masto.asonix.dog/users/asonix/statuses/107355727205658651","inReplyToAtomUri":null,"conversation":"tag:masto.asonix.dog,2021-11-28:objectId=671618:objectType=Conversation","content":"\u003cp\u003eY\u0026apos;all it\u0026apos;s Masto MSunday\u003c/p\u003e","contentMap":{"lion":"\u003cp\u003eY\u0026apos;all it\u0026apos;s Masto MSunday\u003c/p\u003e"},"attachment":[],"tag":[],"replies":{"id":"https://masto.asonix.dog/users/asonix/statuses/107355727205658651/replies","type":"Collection","first":{"type":"CollectionPage","next":"https://masto.asonix.dog/users/asonix/statuses/107355727205658651/replies?only_other_accounts=true\u0026page=true","partOf":"https://masto.asonix.dog/users/asonix/statuses/107355727205658651/replies","items":[]}}}}"#;
#[test]
fn simple_actor() {
let actor: SimpleActor = serde_json::from_str(ASONIX).unwrap();
assert_eq!(actor.id().as_str(), "https://masto.asonix.dog/users/asonix");
assert_eq!(
actor.shared_inbox().unwrap().as_str(),
"https://masto.asonix.dog/inbox"
);
assert_eq!(
actor.inbox().as_str(),
"https://masto.asonix.dog/users/asonix/inbox"
);
assert_eq!(
actor.outbox().as_str(),
"https://masto.asonix.dog/users/asonix/outbox"
);
assert_eq!(
actor.public_key().id().as_str(),
"https://masto.asonix.dog/users/asonix#main-key"
);
}
#[test]
fn simple_activity() {
let activity: SimpleActivity = serde_json::from_str(CREATE).unwrap();
assert_eq!(
activity.id().as_str(),
"https://masto.asonix.dog/users/asonix/statuses/107355727205658651/activity"
);
assert_eq!(
activity.actor_id().as_str(),
"https://masto.asonix.dog/users/asonix"
);
assert_eq!(
activity.object_id().as_str(),
"https://masto.asonix.dog/users/asonix/statuses/107355727205658651"
);
assert!(is_public(&activity));
}
}

View file

@ -1,14 +1,16 @@
use actix_web::{middleware::Logger, web, App, HttpServer, ResponseError};
use apub::{
clients::{AwcClient, AwcError, AwcSignatureConfig, Dereference, Repo, RepoFactory},
crypto::{
DigestFactory, RsaVerifier, Rustcrypto, RustcryptoError, Sha256Digest, VerifyFactory,
},
servers::{
inbox, serve_objects, ActixWebSignatureConfig, Authority, Config, Ingest, PublicKey,
PublicKeyError, PublicKeyId, PublicKeyRepo, PublicKeyRepoFactory, VerifyError,
activitypub::SimplePublicKey,
actix_web::{
inbox, serve_objects, Config, SignatureConfig as ActixWebSignatureConfig, VerifyError,
},
awc::{AwcClient, AwcError, SignatureConfig as AwcSignatureConfig},
digest::{DigestFactory, Sha256Digest},
ingest::{Authority, Ingest, PublicKeyError, PublicKeyId},
ingest::{PublicKeyRepo, PublicKeyRepoFactory},
repo::{Dereference, Repo, RepoFactory},
session::{BreakerSession, RequestCountSession, Session, SessionFactory},
signature::{RsaVerifier, Rustcrypto, RustcryptoError, VerifyFactory},
};
use dashmap::DashMap;
use example_types::{AcceptedActivity, ActivityType, ObjectId};
@ -98,7 +100,10 @@ impl Repo for MemoryRepo {
impl PublicKeyRepo for MemoryRepo {
type Error = serde_json::Error;
async fn fetch(&self, public_key_id: &PublicKeyId) -> Result<Option<PublicKey>, Self::Error> {
async fn fetch(
&self,
public_key_id: &PublicKeyId,
) -> Result<Option<SimplePublicKey>, Self::Error> {
if let Some(obj_ref) = self.inner.get(&public_key_id) {
serde_json::from_value(obj_ref.clone()).map(Some)
} else {
@ -106,7 +111,7 @@ impl PublicKeyRepo for MemoryRepo {
}
}
async fn store(&self, public_key: &PublicKey) -> Result<(), Self::Error> {
async fn store(&self, public_key: &SimplePublicKey) -> Result<(), Self::Error> {
let value = serde_json::to_value(public_key)?;
self.inner.insert(public_key.id.as_ref().clone(), value);

View file

@ -1,6 +1,7 @@
use apub::{
clients::{Activity, AwcClient, AwcSignatureConfig},
crypto::Rustcrypto,
deliver::Activity,
awc::{AwcClient, SignatureConfig},
signature::Rustcrypto,
session::{BreakerSession, RequestCountSession},
};
use example_types::{object_id, NoteType, ObjectId};
@ -11,7 +12,7 @@ use std::time::Duration;
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let private_key = RsaPrivateKey::new(&mut rand::thread_rng(), 1024)?;
let crypto = Rustcrypto::new("key-id".to_string(), private_key);
let config = AwcSignatureConfig::default();
let config = SignatureConfig::default();
let mut breakers = BreakerSession::limit(10, Duration::from_secs(60 * 60));

View file

@ -1,7 +1,8 @@
use apub::{
background::{client, ClientFactory, DeliverJob},
clients::{Activity, AwcClient, AwcSignatureConfig},
crypto::Rustcrypto,
background_jobs::{client, ClientFactory, DeliverJob},
awc::{AwcClient, SignatureConfig},
deliver::Activity,
signature::Rustcrypto,
session::{BreakerSession, RequestCountSession, SessionFactory},
};
use background_jobs::{memory_storage::Storage, WorkerConfig};
@ -33,7 +34,7 @@ pub fn init_tracing() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Clone)]
struct State {
config: AwcSignatureConfig,
config: SignatureConfig,
breakers: BreakerSession,
client: awc::Client,
}
@ -59,7 +60,7 @@ impl SessionFactory for State {
async fn main() -> Result<(), Box<dyn std::error::Error>> {
init_tracing()?;
let config = AwcSignatureConfig::default();
let config = SignatureConfig::default();
let breakers = BreakerSession::limit(3, Duration::from_secs(60 * 60));
let storage = Storage::new();

View file

@ -1,5 +1,6 @@
use apub::{
clients::{Activity, Dereference, Repo},
repo::{Dereference, Repo},
deliver::Activity,
session::Session,
};
use std::{
@ -43,13 +44,13 @@ impl Dereference for ObjectId<ActivityType> {
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(transparent)]
pub struct ObjectId<Kind>(apub::clients::ObjectId<'static, Kind>);
pub struct ObjectId<Kind>(apub::object_id::ObjectId<'static, Kind>);
pub fn object_id<Kind>(id: Url) -> ObjectId<Kind>
where
ObjectId<Kind>: Dereference,
{
ObjectId(apub::clients::ObjectId::new_owned(id))
ObjectId(apub::object_id::ObjectId::new_owned(id))
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]

View file

@ -1,6 +1,7 @@
use apub::{
clients::{Activity, ReqwestClient, ReqwestSignatureConfig},
crypto::OpenSsl,
deliver::Activity,
reqwest::{ReqwestClient, SignatureConfig},
signature::OpenSsl,
session::{BreakerSession, RequestCountSession},
};
use example_types::{object_id, NoteType, ObjectId};
@ -11,7 +12,7 @@ use std::time::Duration;
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let private_key = PKey::from_rsa(Rsa::generate(1024)?)?;
let crypto = OpenSsl::new("key-id".to_string(), private_key);
let config = ReqwestSignatureConfig::default();
let config = SignatureConfig::default();
let mut breakers = BreakerSession::limit(10, Duration::from_secs(60 * 60));

View file

@ -1,27 +1,5 @@
pub mod clients {
//! Types and traits for fetching and delivering objects
//!
//! Notable types are [`CombinedClient`] behind the `utils` feature, [`ReqwestClient`] behind
//! the `with-reqwest` feature, and [`AwcClient`] behind the `awc` feature. Each of these types
//! implements the [`Repo`] trait, which describes fetching an object from a given repository.
//! While [`ReqwestClient`] and [`AwcClient`] both describe fetching objects from activitypub
//! remotes, [`CombinedClient`] is designed to easily fetch an object from a local repository,
//! such as a database or in-memory store, and fall back to an HTTP client for requests it can't
//! satisfy itself.
//!
//! [`ReqwestClient`] and [`AwcClient`] also implement the [`Client`] trait, which describes
//! delivering objects to activitypub remotes.
//!
//! Both [`ReqwestClient`] and [`AwcClient`] are built to use a generic [`Session`]
//! implementation and a generic [`SignFactory`] impelementation to extend request behavior and
//! support HTTP Signatures and HTTP Digests
//!
//! [`Session`]: crate::session::Session
//! [`SignFactory`]: crate::crypto::SignFactory
pub use apub_core::deliver::{Activity, Deliver};
pub use apub_core::deref::{Dereference, Repo, RepoFactory};
pub use apub_core::object_id::ObjectId;
pub mod repo {
pub use apub_core::repo::{Dereference, Repo, RepoFactory};
#[cfg(feature = "apub-deref-client")]
/// The type that combines Local and Remote repos
@ -30,15 +8,17 @@ pub mod clients {
///
/// ```rust
/// use apub::{
/// clients::{
/// repo::{
/// CombinedClient,
/// Dereference,
/// Repo,
/// ReqwestClient,
/// ReqwestSignatureConfig,
/// },
/// crypto::OpenSsl,
/// reqwest::{
/// ReqwestClient,
/// SignatureConfig,
/// },
/// session::Session,
/// signature::OpenSsl,
/// };
/// use dashmap::DashMap;
/// use openssl::{
@ -107,7 +87,7 @@ pub mod clients {
/// existing_object,
/// );
///
/// let signature_config = ReqwestSignatureConfig::default();
/// let signature_config = SignatureConfig::default();
/// let private_key = PKey::from_rsa(Rsa::generate(1024)?)?;
/// let crypto = OpenSsl::new("key-id".to_string(), private_key);
/// let client = reqwest::Client::new();
@ -142,33 +122,53 @@ pub mod clients {
#[cfg(feature = "apub-deref-client")]
pub use apub_deref_client::ClientError as CombinedError;
}
#[cfg(feature = "apub-awc")]
pub mod deliver {
pub use apub_core::deliver::{Activity, Deliver};
}
pub mod object_id {
pub use apub_core::object_id::ObjectId;
}
#[cfg(feature = "apub-awc")]
pub mod awc {
pub use apub_awc::{
AwcClient, AwcError, InvalidHeaderValue, PrepareSignError,
SignatureConfig as AwcSignatureConfig, SignatureError as AwcSignatureError,
};
#[cfg(feature = "apub-reqwest")]
pub use apub_reqwest::{
ReqwestClient, ReqwestError, SignatureConfig as ReqwestSignatureConfig,
SignatureError as ReqwestSignatureError,
AwcClient, AwcError, InvalidHeaderValue, PrepareSignError, SignatureConfig, SignatureError,
};
}
pub mod servers {
#[cfg(feature = "apub-reqwest")]
pub mod reqwest {
pub use apub_reqwest::{ReqwestClient, ReqwestError, SignatureConfig, SignatureError};
}
pub mod ingest {
pub use apub_core::ingest::{Authority, Ingest};
pub use apub_publickey::{
PublicKey, PublicKeyError, PublicKeyId, PublicKeyRepo, PublicKeyRepoFactory,
};
#[cfg(feature = "apub-actix-web")]
pub use apub_actix_web::{
inbox, serve_objects, Config, SignatureConfig as ActixWebSignatureConfig, VerifyError,
#[cfg(feature = "apub-publickey")]
pub use apub_publickey::{
PublicKeyClient, PublicKeyError, PublicKeyId, PublicKeyRepo, PublicKeyRepoFactory,
};
}
pub mod background {
#[cfg(feature = "apub-actix-web")]
pub mod actix_web {
pub use apub_actix_web::{inbox, serve_objects, Config, SignatureConfig, VerifyError};
}
pub mod activitypub {
pub use apub_core::activitystreams::{Activity, Actor, Object, PublicKey};
#[cfg(feature = "apub-publickey")]
pub use apub_publickey::SimplePublicKey;
#[cfg(feature = "apub-simple-activitypub")]
pub use apub_simple_activitypub::{SimpleActivity, SimpleActor, SimpleObject};
}
pub mod background_jobs {
#[cfg(feature = "apub-background-jobs")]
pub use apub_background_jobs::{client, ClientFactory, DeliverJob, EnqueueError, JobClient};
}
@ -180,13 +180,22 @@ pub mod session {
pub use apub_breaker_session::BreakerSession;
}
pub mod crypto {
pub mod digest {
pub use apub_core::digest::{Digest, DigestBuilder, DigestFactory};
#[cfg(feature = "apub-openssl")]
pub use apub_openssl::OpenSslDigest;
#[cfg(feature = "apub-rustcrypto")]
pub use apub_rustcrypto::Sha256Digest;
}
pub mod signature {
pub use apub_core::signature::{Sign, SignFactory, Verify, VerifyBuilder, VerifyFactory};
#[cfg(feature = "apub-openssl")]
pub use apub_openssl::{OpenSsl, OpenSslDigest, OpenSslSigner, OpenSslVerifier};
pub use apub_openssl::{OpenSsl, OpenSslSigner, OpenSslVerifier};
#[cfg(feature = "apub-rustcrypto")]
pub use apub_rustcrypto::{RsaSigner, RsaVerifier, Rustcrypto, RustcryptoError, Sha256Digest};
pub use apub_rustcrypto::{RsaSigner, RsaVerifier, Rustcrypto, RustcryptoError};
}