Fully lean on activitystreams-new

This commit is contained in:
asonix 2020-05-21 16:24:56 -05:00
parent 0b2763ab8b
commit 729e425e32
27 changed files with 514 additions and 544 deletions

483
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -14,10 +14,11 @@ build = "src/build.rs"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
actix-rt = "1.0.0" actix-rt = "1.1.1"
actix-web = { version = "3.0.0-alpha.1", features = ["rustls"] } actix-web = { version = "3.0.0-alpha.2", features = ["rustls"] }
actix-webfinger = "0.3.0-alpha.6" actix-webfinger = "0.3.0-alpha.6"
activitystreams = "0.5.0" activitystreams-new = { git = "https://git.asonix.dog/asonix/activitystreams-sketch" }
activitystreams-ext = { git = "https://git.asonix.dog/asonix/activitystreams-ext" }
ammonia = "3.1.0" ammonia = "3.1.0"
async-trait = "0.1.24" async-trait = "0.1.24"
background-jobs = "0.8.0-alpha.2" background-jobs = "0.8.0-alpha.2"

View file

@ -1,15 +1,14 @@
use activitystreams::{ use activitystreams_ext::{Ext1, UnparsedExtension};
actor::Actor, use activitystreams_new::{
ext::Extension, activity::ActorAndObject,
object::{Object, ObjectBox}, actor::{Actor, ApActor},
primitives::XsdAnyUri, primitives::XsdAnyUri,
Base, BaseBox, PropRefs, unparsed::UnparsedMutExt,
}; };
use std::collections::HashMap;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct PublicKey { pub struct PublicKeyInner {
pub id: XsdAnyUri, pub id: XsdAnyUri,
pub owner: XsdAnyUri, pub owner: XsdAnyUri,
pub public_key_pem: String, pub public_key_pem: String,
@ -17,21 +16,8 @@ pub struct PublicKey {
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct PublicKeyExtension { pub struct PublicKey {
pub public_key: PublicKey, pub public_key: PublicKeyInner,
}
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, PropRefs)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
pub struct AnyExistingObject {
pub id: XsdAnyUri,
#[serde(rename = "type")]
pub kind: String,
#[serde(flatten)]
ext: HashMap<String, serde_json::Value>,
} }
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
@ -47,141 +33,32 @@ pub enum ValidTypes {
Update, Update,
} }
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
#[serde(untagged)] #[serde(rename_all = "PascalCase")]
#[serde(rename_all = "camelCase")] pub enum UndoTypes {
pub enum ValidObjects { Follow,
Id(XsdAnyUri), Announce,
Object(AnyExistingObject), Create,
} }
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] pub type AcceptedUndoObjects = ActorAndObject<UndoTypes>;
#[serde(rename_all = "camelCase")] pub type AcceptedActivities = ActorAndObject<ValidTypes>;
pub struct AcceptedObjects { pub type AcceptedActors = Ext1<ApActor<Actor<String>>, PublicKey>;
pub id: XsdAnyUri,
#[serde(rename = "type")] impl<U> UnparsedExtension<U> for PublicKey
pub kind: ValidTypes, where
U: UnparsedMutExt,
{
type Error = serde_json::Error;
pub actor: XsdAnyUri, fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
Ok(PublicKey {
public_key: unparsed_mut.remove("publicKey")?,
})
}
pub object: ValidObjects, fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
unparsed_mut.insert("publicKey", self.public_key)?;
#[serde(flatten)] Ok(())
ext: HashMap<String, serde_json::Value>,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AcceptedActors {
pub id: XsdAnyUri,
#[serde(rename = "type")]
pub kind: String,
pub inbox: XsdAnyUri,
pub endpoints: Endpoints,
pub public_key: PublicKey,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Endpoints {
shared_inbox: Option<XsdAnyUri>,
}
impl PublicKey {
pub fn into_ext(self) -> PublicKeyExtension {
self.into()
}
}
impl From<PublicKey> for PublicKeyExtension {
fn from(public_key: PublicKey) -> Self {
PublicKeyExtension { public_key }
}
}
impl<T> Extension<T> for PublicKeyExtension where T: Actor {}
impl ValidObjects {
pub fn id(&self) -> &XsdAnyUri {
match self {
ValidObjects::Id(ref id) => id,
ValidObjects::Object(ref obj) => &obj.id,
}
}
pub fn kind(&self) -> Option<&str> {
match self {
ValidObjects::Id(_) => None,
ValidObjects::Object(AnyExistingObject { kind, .. }) => Some(kind),
}
}
pub fn is_kind(&self, query_kind: &str) -> bool {
match self {
ValidObjects::Id(_) => false,
ValidObjects::Object(AnyExistingObject { kind, .. }) => kind == query_kind,
}
}
pub fn is(&self, uri: &XsdAnyUri) -> bool {
match self {
ValidObjects::Id(id) => id == uri,
ValidObjects::Object(AnyExistingObject { id, .. }) => id == uri,
}
}
pub fn child_object_id(&self) -> Option<XsdAnyUri> {
match self {
ValidObjects::Id(_) => None,
ValidObjects::Object(AnyExistingObject { ext, .. }) => {
if let Some(o) = ext.get("object") {
if let Ok(child_uri) = serde_json::from_value::<XsdAnyUri>(o.clone()) {
return Some(child_uri);
}
}
None
}
}
}
pub fn child_object_is(&self, uri: &XsdAnyUri) -> bool {
if let Some(child_object_id) = self.child_object_id() {
return *uri == child_object_id;
}
false
}
pub fn child_actor_id(&self) -> Option<XsdAnyUri> {
match self {
ValidObjects::Id(_) => None,
ValidObjects::Object(AnyExistingObject { ext, .. }) => {
if let Some(o) = ext.get("actor") {
if let Ok(child_uri) = serde_json::from_value::<XsdAnyUri>(o.clone()) {
return Some(child_uri);
}
}
None
}
}
}
pub fn child_actor_is(&self, uri: &XsdAnyUri) -> bool {
if let Some(child_actor_id) = self.child_actor_id() {
return *uri == child_actor_id;
}
false
}
}
impl AcceptedActors {
pub fn inbox(&self) -> &XsdAnyUri {
self.endpoints.shared_inbox.as_ref().unwrap_or(&self.inbox)
} }
} }

View file

@ -1,5 +1,5 @@
use crate::{apub::AcceptedActors, db::Db, error::MyError, requests::Requests}; use crate::{apub::AcceptedActors, db::Db, error::MyError, requests::Requests};
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::{prelude::*, primitives::XsdAnyUri};
use log::error; use log::error;
use std::{collections::HashSet, sync::Arc, time::Duration}; use std::{collections::HashSet, sync::Arc, time::Duration};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -61,7 +61,8 @@ impl ActorCache {
let accepted_actor = requests.fetch::<AcceptedActors>(id.as_str()).await?; let accepted_actor = requests.fetch::<AcceptedActors>(id.as_str()).await?;
let input_host = id.as_url().host(); let input_host = id.as_url().host();
let actor_host = accepted_actor.id.as_url().host(); let accepted_actor_id = accepted_actor.id().ok_or(MyError::MissingId)?;
let actor_host = accepted_actor_id.as_url().host();
let inbox_host = accepted_actor.inbox().as_url().host(); let inbox_host = accepted_actor.inbox().as_url().host();
if input_host != actor_host { if input_host != actor_host {
@ -81,9 +82,9 @@ impl ActorCache {
let inbox = accepted_actor.inbox().clone(); let inbox = accepted_actor.inbox().clone();
let actor = Actor { let actor = Actor {
id: accepted_actor.id, id: accepted_actor_id.clone(),
public_key: accepted_actor.public_key.public_key_pem, public_key: accepted_actor.ext_one.public_key.public_key_pem,
public_key_id: accepted_actor.public_key.id, public_key_id: accepted_actor.ext_one.public_key.id,
inbox, inbox,
}; };

View file

@ -1,5 +1,5 @@
use crate::{db::Db, error::MyError}; use crate::{db::Db, error::MyError};
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use bytes::Bytes; use bytes::Bytes;
use futures::join; use futures::join;
use lru::LruCache; use lru::LruCache;

View file

@ -1,5 +1,5 @@
use crate::{db::Db, error::MyError}; use crate::{db::Db, error::MyError};
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use log::{debug, error}; use log::{debug, error};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},

View file

@ -5,7 +5,7 @@ use crate::{
error::MyError, error::MyError,
requests::Requests, requests::Requests,
}; };
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use actix_rt::{ use actix_rt::{
spawn, spawn,
time::{interval_at, Instant}, time::{interval_at, Instant},

View file

@ -1,5 +1,5 @@
use crate::error::MyError; use crate::error::MyError;
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use deadpool_postgres::{Manager, Pool}; use deadpool_postgres::{Manager, Pool};
use log::{info, warn}; use log::{info, warn};
use rsa::RSAPrivateKey; use rsa::RSAPrivateKey;

View file

@ -1,4 +1,4 @@
use activitystreams::primitives::XsdAnyUriError; use activitystreams_new::primitives::XsdAnyUriError;
use actix_web::{ use actix_web::{
error::{BlockingError, ResponseError}, error::{BlockingError, ResponseError},
http::StatusCode, http::StatusCode,
@ -96,6 +96,18 @@ pub enum MyError {
#[error("Response has invalid status code, {0}")] #[error("Response has invalid status code, {0}")]
Status(StatusCode), Status(StatusCode),
#[error("Expected an Object, found something else")]
ObjectFormat,
#[error("Expected a single object, found array")]
ObjectCount,
#[error("Input is missing a 'type' field")]
MissingKind,
#[error("Input is missing a 'id' field")]
MissingId,
#[error("URI is missing domain field")] #[error("URI is missing domain field")]
Domain, Domain,
@ -112,7 +124,9 @@ impl ResponseError for MyError {
| MyError::BadActor(_, _) => StatusCode::FORBIDDEN, | MyError::BadActor(_, _) => StatusCode::FORBIDDEN,
MyError::NotSubscribed(_) => StatusCode::UNAUTHORIZED, MyError::NotSubscribed(_) => StatusCode::UNAUTHORIZED,
MyError::Duplicate => StatusCode::ACCEPTED, MyError::Duplicate => StatusCode::ACCEPTED,
MyError::Kind(_) => StatusCode::BAD_REQUEST, MyError::Kind(_) | MyError::MissingKind | MyError::MissingId | MyError::ObjectCount => {
StatusCode::BAD_REQUEST
}
_ => StatusCode::INTERNAL_SERVER_ERROR, _ => StatusCode::INTERNAL_SERVER_ERROR,
} }
} }

View file

@ -7,7 +7,7 @@ use crate::{
DeliverMany, JobState, DeliverMany, JobState,
}, },
}; };
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::{activity::Announce as AsAnnounce, primitives::XsdAnyUri};
use background_jobs::ActixJob; use background_jobs::ActixJob;
use std::{future::Future, pin::Pin}; use std::{future::Future, pin::Pin};
@ -41,13 +41,11 @@ fn generate_announce(
config: &Config, config: &Config,
activity_id: &XsdAnyUri, activity_id: &XsdAnyUri,
object_id: &XsdAnyUri, object_id: &XsdAnyUri,
) -> Result<activitystreams::activity::Announce, MyError> { ) -> Result<AsAnnounce, MyError> {
let mut announce = activitystreams::activity::Announce::default(); let announce = AsAnnounce::new(
config.generate_url(UrlKind::Actor).parse::<XsdAnyUri>()?,
announce object_id.clone(),
.announce_props );
.set_object_xsd_any_uri(object_id.clone())?
.set_actor_xsd_any_uri(config.generate_url(UrlKind::Actor))?;
prepare_activity( prepare_activity(
announce, announce,

View file

@ -1,23 +1,27 @@
use crate::{ use crate::{
apub::AcceptedObjects, apub::AcceptedActivities,
config::{Config, UrlKind}, config::{Config, UrlKind},
data::Actor, data::Actor,
error::MyError, error::MyError,
jobs::{apub::prepare_activity, Deliver, JobState}, jobs::{apub::prepare_activity, Deliver, JobState},
}; };
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::{
activity::{Accept as AsAccept, Follow as AsFollow},
prelude::*,
primitives::XsdAnyUri,
};
use background_jobs::ActixJob; use background_jobs::ActixJob;
use std::{future::Future, pin::Pin}; use std::{future::Future, pin::Pin};
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Follow { pub struct Follow {
is_listener: bool, is_listener: bool,
input: AcceptedObjects, input: AcceptedActivities,
actor: Actor, actor: Actor,
} }
impl Follow { impl Follow {
pub fn new(is_listener: bool, input: AcceptedObjects, actor: Actor) -> Self { pub fn new(is_listener: bool, input: AcceptedActivities, actor: Actor) -> Self {
Follow { Follow {
is_listener, is_listener,
input, input,
@ -32,7 +36,7 @@ impl Follow {
let my_id: XsdAnyUri = state.config.generate_url(UrlKind::Actor).parse()?; let my_id: XsdAnyUri = state.config.generate_url(UrlKind::Actor).parse()?;
// if following relay directly, not just following 'public', followback // if following relay directly, not just following 'public', followback
if self.input.object.is(&my_id) && !state.actors.is_following(&self.actor.id).await { if self.input.object_is(&my_id) && !state.actors.is_following(&self.actor.id).await {
let follow = generate_follow(&state.config, &self.actor.id, &my_id)?; let follow = generate_follow(&state.config, &self.actor.id, &my_id)?;
state state
.job_server .job_server
@ -41,7 +45,12 @@ impl Follow {
state.actors.follower(&self.actor).await?; state.actors.follower(&self.actor).await?;
let accept = generate_accept_follow(&state.config, &self.actor.id, &self.input.id, &my_id)?; let accept = generate_accept_follow(
&state.config,
&self.actor.id,
self.input.id().ok_or(MyError::MissingId)?,
&my_id,
)?;
state state
.job_server .job_server
@ -55,13 +64,8 @@ fn generate_follow(
config: &Config, config: &Config,
actor_id: &XsdAnyUri, actor_id: &XsdAnyUri,
my_id: &XsdAnyUri, my_id: &XsdAnyUri,
) -> Result<activitystreams::activity::Follow, MyError> { ) -> Result<AsFollow, MyError> {
let mut follow = activitystreams::activity::Follow::default(); let follow = AsFollow::new(my_id.clone(), actor_id.clone());
follow
.follow_props
.set_object_xsd_any_uri(actor_id.clone())?
.set_actor_xsd_any_uri(my_id.clone())?;
prepare_activity( prepare_activity(
follow, follow,
@ -76,23 +80,12 @@ fn generate_accept_follow(
actor_id: &XsdAnyUri, actor_id: &XsdAnyUri,
input_id: &XsdAnyUri, input_id: &XsdAnyUri,
my_id: &XsdAnyUri, my_id: &XsdAnyUri,
) -> Result<activitystreams::activity::Accept, MyError> { ) -> Result<AsAccept, MyError> {
let mut accept = activitystreams::activity::Accept::default(); let mut follow = AsFollow::new(actor_id.clone(), my_id.clone());
accept follow.set_id(input_id.clone());
.accept_props
.set_actor_xsd_any_uri(my_id.clone())?
.set_object_base_box({
let mut follow = activitystreams::activity::Follow::default();
follow.object_props.set_id(input_id.clone())?; let accept = AsAccept::new(my_id.clone(), follow.into_any_base()?);
follow
.follow_props
.set_object_xsd_any_uri(my_id.clone())?
.set_actor_xsd_any_uri(actor_id.clone())?;
follow
})?;
prepare_activity( prepare_activity(
accept, accept,

View file

@ -1,24 +1,30 @@
use crate::{ use crate::{
apub::AcceptedObjects, apub::AcceptedActivities,
data::Actor, data::Actor,
error::MyError,
jobs::{apub::get_inboxes, DeliverMany, JobState}, jobs::{apub::get_inboxes, DeliverMany, JobState},
}; };
use activitystreams_new::prelude::*;
use background_jobs::ActixJob; use background_jobs::ActixJob;
use std::{future::Future, pin::Pin}; use std::{future::Future, pin::Pin};
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Forward { pub struct Forward {
input: AcceptedObjects, input: AcceptedActivities,
actor: Actor, actor: Actor,
} }
impl Forward { impl Forward {
pub fn new(input: AcceptedObjects, actor: Actor) -> Self { pub fn new(input: AcceptedActivities, actor: Actor) -> Self {
Forward { input, actor } Forward { input, actor }
} }
async fn perform(self, state: JobState) -> Result<(), anyhow::Error> { async fn perform(self, state: JobState) -> Result<(), anyhow::Error> {
let object_id = self.input.object.id(); let object_id = self
.input
.object()
.as_single_id()
.ok_or(MyError::MissingId)?;
let inboxes = get_inboxes(&state.state, &self.actor, object_id).await?; let inboxes = get_inboxes(&state.state, &self.actor, object_id).await?;

View file

@ -3,8 +3,12 @@ use crate::{
data::{Actor, State}, data::{Actor, State},
error::MyError, error::MyError,
}; };
use activitystreams::{ use activitystreams_new::{
context, object::properties::ObjectProperties, primitives::XsdAnyUri, security, activity::{Follow as AsFollow, Undo as AsUndo},
context,
prelude::*,
primitives::XsdAnyUri,
security,
}; };
use std::convert::TryInto; use std::convert::TryInto;
@ -30,19 +34,18 @@ async fn get_inboxes(
Ok(state.listeners_without(&actor.inbox, &domain).await) Ok(state.listeners_without(&actor.inbox, &domain).await)
} }
fn prepare_activity<T, U, V>( fn prepare_activity<T, U, V, Kind>(
mut t: T, mut t: T,
id: impl TryInto<XsdAnyUri, Error = U>, id: impl TryInto<XsdAnyUri, Error = U>,
to: impl TryInto<XsdAnyUri, Error = V>, to: impl TryInto<XsdAnyUri, Error = V>,
) -> Result<T, MyError> ) -> Result<T, MyError>
where where
T: AsMut<ObjectProperties>, T: ObjectExt<Kind> + BaseExt<Kind>,
MyError: From<U> + From<V>, MyError: From<U> + From<V>,
{ {
t.as_mut() t.set_id(id.try_into()?)
.set_id(id.try_into()?)? .set_many_tos(vec![to.try_into()?])
.set_many_to_xsd_any_uris(vec![to.try_into()?])? .set_many_contexts(vec![context(), security()]);
.set_many_context_xsd_any_uris(vec![context(), security()])?;
Ok(t) Ok(t)
} }
@ -51,24 +54,12 @@ fn generate_undo_follow(
config: &Config, config: &Config,
actor_id: &XsdAnyUri, actor_id: &XsdAnyUri,
my_id: &XsdAnyUri, my_id: &XsdAnyUri,
) -> Result<activitystreams::activity::Undo, MyError> { ) -> Result<AsUndo, MyError> {
let mut undo = activitystreams::activity::Undo::default(); let mut follow = AsFollow::new(my_id.clone(), actor_id.clone());
undo.undo_props follow.set_id(config.generate_url(UrlKind::Activity).parse()?);
.set_actor_xsd_any_uri(my_id.clone())?
.set_object_base_box({
let mut follow = activitystreams::activity::Follow::default();
follow let undo = AsUndo::new(my_id.clone(), follow.into_any_base()?);
.object_props
.set_id(config.generate_url(UrlKind::Activity))?;
follow
.follow_props
.set_actor_xsd_any_uri(actor_id.clone())?
.set_object_xsd_any_uri(actor_id.clone())?;
follow
})?;
prepare_activity(undo, config.generate_url(UrlKind::Actor), actor_id.clone()) prepare_activity(undo, config.generate_url(UrlKind::Actor), actor_id.clone())
} }

View file

@ -3,7 +3,7 @@ use crate::{
data::Actor, data::Actor,
jobs::{apub::generate_undo_follow, Deliver, JobState}, jobs::{apub::generate_undo_follow, Deliver, JobState},
}; };
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use background_jobs::ActixJob; use background_jobs::ActixJob;
use std::{future::Future, pin::Pin}; use std::{future::Future, pin::Pin};

View file

@ -1,21 +1,21 @@
use crate::{ use crate::{
apub::AcceptedObjects, apub::AcceptedActivities,
config::UrlKind, config::UrlKind,
data::Actor, data::Actor,
jobs::{apub::generate_undo_follow, Deliver, JobState}, jobs::{apub::generate_undo_follow, Deliver, JobState},
}; };
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use background_jobs::ActixJob; use background_jobs::ActixJob;
use std::{future::Future, pin::Pin}; use std::{future::Future, pin::Pin};
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Undo { pub struct Undo {
input: AcceptedObjects, input: AcceptedActivities,
actor: Actor, actor: Actor,
} }
impl Undo { impl Undo {
pub fn new(input: AcceptedObjects, actor: Actor) -> Self { pub fn new(input: AcceptedActivities, actor: Actor) -> Self {
Undo { input, actor } Undo { input, actor }
} }

View file

@ -1,5 +1,5 @@
use crate::{error::MyError, jobs::JobState}; use crate::{error::MyError, jobs::JobState};
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use anyhow::Error; use anyhow::Error;
use background_jobs::{ActixJob, Backoff}; use background_jobs::{ActixJob, Backoff};
use std::{future::Future, pin::Pin}; use std::{future::Future, pin::Pin};

View file

@ -2,7 +2,7 @@ use crate::{
error::MyError, error::MyError,
jobs::{Deliver, JobState}, jobs::{Deliver, JobState},
}; };
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use anyhow::Error; use anyhow::Error;
use background_jobs::ActixJob; use background_jobs::ActixJob;
use futures::future::{ready, Ready}; use futures::future::{ready, Ready};

View file

@ -1,5 +1,5 @@
use crate::{config::UrlKind, jobs::JobState}; use crate::{config::UrlKind, jobs::JobState};
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use anyhow::Error; use anyhow::Error;
use background_jobs::ActixJob; use background_jobs::ActixJob;
use futures::join; use futures::join;

View file

@ -1,5 +1,5 @@
use crate::jobs::JobState; use crate::jobs::JobState;
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use anyhow::Error; use anyhow::Error;
use background_jobs::ActixJob; use background_jobs::ActixJob;
use std::{future::Future, pin::Pin}; use std::{future::Future, pin::Pin};

View file

@ -1,5 +1,5 @@
use crate::{data::ActorCache, error::MyError, requests::Requests}; use crate::{data::ActorCache, error::MyError, requests::Requests};
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use actix_web::web; use actix_web::web;
use http_signature_normalization_actix::{prelude::*, verify::DeprecatedAlgorithm}; use http_signature_normalization_actix::{prelude::*, verify::DeprecatedAlgorithm};
use log::error; use log::error;

View file

@ -3,7 +3,7 @@ use crate::{
db::listen, db::listen,
jobs::{JobServer, QueryInstance, QueryNodeinfo}, jobs::{JobServer, QueryInstance, QueryNodeinfo},
}; };
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use actix_rt::{spawn, time::delay_for}; use actix_rt::{spawn, time::delay_for};
use futures::stream::{poll_fn, StreamExt}; use futures::stream::{poll_fn, StreamExt};
use log::{debug, error, warn}; use log::{debug, error, warn};

View file

@ -1,5 +1,5 @@
use crate::error::MyError; use crate::error::MyError;
use activitystreams::primitives::XsdAnyUri; use activitystreams_new::primitives::XsdAnyUri;
use actix_web::{client::Client, http::header::Date}; use actix_web::{client::Client, http::header::Date};
use bytes::Bytes; use bytes::Bytes;
use http_signature_normalization_actix::prelude::*; use http_signature_normalization_actix::prelude::*;

View file

@ -1,13 +1,17 @@
use crate::{ use crate::{
apub::PublicKey, apub::{PublicKey, PublicKeyInner},
config::{Config, UrlKind}, config::{Config, UrlKind},
data::State, data::State,
error::MyError, error::MyError,
routes::ok, routes::ok,
}; };
use activitystreams::{ use activitystreams_ext::Ext1;
actor::Application, context, endpoint::EndpointProperties, ext::Extensible, use activitystreams_new::{
object::properties::ObjectProperties, security, actor::{ApActor, Application, Endpoints},
context,
prelude::*,
primitives::{XsdAnyUri, XsdString},
security,
}; };
use actix_web::{web, Responder}; use actix_web::{web, Responder};
use rsa_pem::KeyExt; use rsa_pem::KeyExt;
@ -16,33 +20,42 @@ pub async fn route(
state: web::Data<State>, state: web::Data<State>,
config: web::Data<Config>, config: web::Data<Config>,
) -> Result<impl Responder, MyError> { ) -> Result<impl Responder, MyError> {
let mut application = Application::full(); let mut application = Ext1::new(
let mut endpoint = EndpointProperties::default(); ApActor::new(
config.generate_url(UrlKind::Inbox).parse()?,
endpoint.set_shared_inbox(config.generate_url(UrlKind::Inbox))?; config.generate_url(UrlKind::Outbox).parse()?,
Application::new(),
let props: &mut ObjectProperties = application.as_mut(); ),
props PublicKey {
.set_id(config.generate_url(UrlKind::Actor))? public_key: PublicKeyInner {
.set_summary_xsd_string("AodeRelay bot")?
.set_name_xsd_string("AodeRelay")?
.set_url_xsd_any_uri(config.generate_url(UrlKind::Actor))?
.set_many_context_xsd_any_uris(vec![context(), security()])?;
application
.extension
.set_preferred_username("relay")?
.set_followers(config.generate_url(UrlKind::Followers))?
.set_following(config.generate_url(UrlKind::Following))?
.set_inbox(config.generate_url(UrlKind::Inbox))?
.set_outbox(config.generate_url(UrlKind::Outbox))?
.set_endpoints(endpoint)?;
let public_key = PublicKey {
id: config.generate_url(UrlKind::MainKey).parse()?, id: config.generate_url(UrlKind::MainKey).parse()?,
owner: config.generate_url(UrlKind::Actor).parse()?, owner: config.generate_url(UrlKind::Actor).parse()?,
public_key_pem: state.public_key.to_pem_pkcs8()?, public_key_pem: state.public_key.to_pem_pkcs8()?,
}; },
},
);
Ok(ok(application.extend(public_key.into_ext()))) application
.set_id(config.generate_url(UrlKind::Actor).parse()?)
.set_summary(XsdString::from("AodeRelay bot"))
.set_name(XsdString::from("AodeRelay"))
.set_url(config.generate_url(UrlKind::Actor).parse::<XsdAnyUri>()?)
.set_many_contexts(vec![context(), security()])
.set_preferred_username("relay".into())
.set_followers(
config
.generate_url(UrlKind::Followers)
.parse::<XsdAnyUri>()?,
)
.set_following(
config
.generate_url(UrlKind::Following)
.parse::<XsdAnyUri>()?,
)
.set_endpoints(Endpoints {
shared_inbox: Some(config.generate_url(UrlKind::Inbox).parse::<XsdAnyUri>()?),
..Default::default()
});
Ok(ok(application))
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
apub::{AcceptedObjects, ValidTypes}, apub::{AcceptedActivities, AcceptedUndoObjects, UndoTypes, ValidTypes},
config::{Config, UrlKind}, config::{Config, UrlKind},
data::{Actor, ActorCache, State}, data::{Actor, ActorCache, State},
error::MyError, error::MyError,
@ -8,7 +8,13 @@ use crate::{
requests::Requests, requests::Requests,
routes::accepted, routes::accepted,
}; };
use activitystreams::{primitives::XsdAnyUri, public}; use activitystreams_new::{
activity,
base::AnyBase,
prelude::*,
primitives::{OneOrMany, XsdAnyUri},
public,
};
use actix_web::{web, HttpResponse}; use actix_web::{web, HttpResponse};
use futures::join; use futures::join;
use http_signature_normalization_actix::prelude::{DigestVerified, SignatureVerified}; use http_signature_normalization_actix::prelude::{DigestVerified, SignatureVerified};
@ -20,12 +26,18 @@ pub async fn route(
config: web::Data<Config>, config: web::Data<Config>,
client: web::Data<Requests>, client: web::Data<Requests>,
jobs: web::Data<JobServer>, jobs: web::Data<JobServer>,
input: web::Json<AcceptedObjects>, input: web::Json<AcceptedActivities>,
verified: Option<(SignatureVerified, DigestVerified)>, verified: Option<(SignatureVerified, DigestVerified)>,
) -> Result<HttpResponse, MyError> { ) -> Result<HttpResponse, MyError> {
let input = input.into_inner(); let input = input.into_inner();
let actor = actors.get(&input.actor, &client).await?.into_inner(); let actor = actors
.get(
input.actor().as_single_id().ok_or(MyError::MissingId)?,
&client,
)
.await?
.into_inner();
let (is_blocked, is_whitelisted, is_listener) = join!( let (is_blocked, is_whitelisted, is_listener) = join!(
state.is_blocked(&actor.id), state.is_blocked(&actor.id),
@ -41,7 +53,7 @@ pub async fn route(
return Err(MyError::Whitelist(actor.id.to_string())); return Err(MyError::Whitelist(actor.id.to_string()));
} }
if !is_listener && !valid_without_listener(&input) { if !is_listener && !valid_without_listener(&input)? {
return Err(MyError::NotSubscribed(actor.inbox.to_string())); return Err(MyError::NotSubscribed(actor.inbox.to_string()));
} }
@ -59,7 +71,7 @@ pub async fn route(
} }
} }
match input.kind { match input.kind().ok_or(MyError::MissingKind)? {
ValidTypes::Accept => handle_accept(&config, input).await?, ValidTypes::Accept => handle_accept(&config, input).await?,
ValidTypes::Reject => handle_reject(&config, &jobs, input, actor).await?, ValidTypes::Reject => handle_reject(&config, &jobs, input, actor).await?,
ValidTypes::Announce | ValidTypes::Create => { ValidTypes::Announce | ValidTypes::Create => {
@ -73,26 +85,39 @@ pub async fn route(
Ok(accepted(serde_json::json!({}))) Ok(accepted(serde_json::json!({})))
} }
fn valid_without_listener(input: &AcceptedObjects) -> bool { fn valid_without_listener(input: &AcceptedActivities) -> Result<bool, MyError> {
match input.kind { match input.kind() {
ValidTypes::Follow => true, Some(ValidTypes::Follow) => Ok(true),
ValidTypes::Undo if input.object.is_kind("Follow") => true, Some(ValidTypes::Undo) => Ok(single_object(input.object())?.is_kind("Follow")),
_ => false, _ => Ok(false),
} }
} }
async fn handle_accept(config: &Config, input: AcceptedObjects) -> Result<(), MyError> { fn kind_str(base: &AnyBase) -> Result<&str, MyError> {
if !input.object.is_kind("Follow") { base.kind_str().ok_or(MyError::MissingKind)
return Err(MyError::Kind( }
input.object.kind().unwrap_or("unknown").to_owned(),
));
}
if !input fn id_string(id: Option<&XsdAnyUri>) -> Result<String, MyError> {
.object id.map(|s| s.to_string()).ok_or(MyError::MissingId)
.child_actor_is(&config.generate_url(UrlKind::Actor).parse()?) }
fn single_object(o: &OneOrMany<AnyBase>) -> Result<&AnyBase, MyError> {
o.as_one().ok_or(MyError::ObjectCount)
}
async fn handle_accept(config: &Config, input: AcceptedActivities) -> Result<(), MyError> {
let follow = if let Ok(Some(follow)) =
activity::Follow::from_any_base(single_object(input.object())?.clone())
{ {
return Err(MyError::WrongActor(input.object.id().to_string())); follow
} else {
return Err(MyError::Kind(
kind_str(single_object(input.object())?)?.to_owned(),
));
};
if !follow.actor_is(&config.generate_url(UrlKind::Actor).parse()?) {
return Err(MyError::WrongActor(id_string(follow.id())?));
} }
Ok(()) Ok(())
@ -101,20 +126,23 @@ async fn handle_accept(config: &Config, input: AcceptedObjects) -> Result<(), My
async fn handle_reject( async fn handle_reject(
config: &Config, config: &Config,
jobs: &JobServer, jobs: &JobServer,
input: AcceptedObjects, input: AcceptedActivities,
actor: Actor, actor: Actor,
) -> Result<(), MyError> { ) -> Result<(), MyError> {
if !input.object.is_kind("Follow") { let follow = if let Ok(Some(follow)) =
return Err(MyError::Kind( activity::Follow::from_any_base(single_object(input.object())?.clone())
input.object.kind().unwrap_or("unknown").to_owned(),
));
}
if !input
.object
.child_actor_is(&config.generate_url(UrlKind::Actor).parse()?)
{ {
return Err(MyError::WrongActor(input.object.id().to_string())); follow
} else {
return Err(MyError::Kind(
kind_str(single_object(input.object())?)?.to_owned(),
));
};
if !follow.actor_is(&config.generate_url(UrlKind::Actor).parse()?) {
return Err(MyError::WrongActor(
follow.id().map(|s| s.to_string()).unwrap_or(String::new()),
));
} }
jobs.queue(Reject(actor))?; jobs.queue(Reject(actor))?;
@ -125,34 +153,27 @@ async fn handle_reject(
async fn handle_undo( async fn handle_undo(
config: &Config, config: &Config,
jobs: &JobServer, jobs: &JobServer,
input: AcceptedObjects, input: AcceptedActivities,
actor: Actor, actor: Actor,
is_listener: bool, is_listener: bool,
) -> Result<(), MyError> { ) -> Result<(), MyError> {
match input.object.kind() { let any_base = single_object(input.object())?.clone();
Some("Follow") | Some("Announce") | Some("Create") => (), let undone_object =
_ => { AcceptedUndoObjects::from_any_base(any_base)?.ok_or(MyError::ObjectFormat)?;
return Err(MyError::Kind(
input.object.kind().unwrap_or("unknown").to_owned(),
));
}
}
if !input.object.is_kind("Follow") { if !undone_object.is_kind(&UndoTypes::Follow) {
if is_listener { if is_listener {
jobs.queue(Forward::new(input, actor))?; jobs.queue(Forward::new(input, actor))?;
return Ok(()); return Ok(());
} else { } else {
return Err(MyError::Kind( return Err(MyError::NotSubscribed(id_string(input.id())?));
input.object.kind().unwrap_or("unknown").to_owned(),
));
} }
} }
let my_id: XsdAnyUri = config.generate_url(UrlKind::Actor).parse()?; let my_id: XsdAnyUri = config.generate_url(UrlKind::Actor).parse()?;
if !input.object.child_object_is(&my_id) && !input.object.child_object_is(&public()) { if !undone_object.object_is(&my_id) && !undone_object.object_is(&public()) {
return Err(MyError::WrongActor(input.object.id().to_string())); return Err(MyError::WrongActor(id_string(undone_object.id())?));
} }
if !is_listener { if !is_listener {
@ -165,7 +186,7 @@ async fn handle_undo(
async fn handle_forward( async fn handle_forward(
jobs: &JobServer, jobs: &JobServer,
input: AcceptedObjects, input: AcceptedActivities,
actor: Actor, actor: Actor,
) -> Result<(), MyError> { ) -> Result<(), MyError> {
jobs.queue(Forward::new(input, actor))?; jobs.queue(Forward::new(input, actor))?;
@ -176,10 +197,10 @@ async fn handle_forward(
async fn handle_announce( async fn handle_announce(
state: &State, state: &State,
jobs: &JobServer, jobs: &JobServer,
input: AcceptedObjects, input: AcceptedActivities,
actor: Actor, actor: Actor,
) -> Result<(), MyError> { ) -> Result<(), MyError> {
let object_id = input.object.id(); let object_id = input.object().as_single_id().ok_or(MyError::MissingId)?;
if state.is_cached(object_id).await { if state.is_cached(object_id).await {
return Err(MyError::Duplicate); return Err(MyError::Duplicate);
@ -193,14 +214,16 @@ async fn handle_announce(
async fn handle_follow( async fn handle_follow(
config: &Config, config: &Config,
jobs: &JobServer, jobs: &JobServer,
input: AcceptedObjects, input: AcceptedActivities,
actor: Actor, actor: Actor,
is_listener: bool, is_listener: bool,
) -> Result<(), MyError> { ) -> Result<(), MyError> {
let my_id: XsdAnyUri = config.generate_url(UrlKind::Actor).parse()?; let my_id: XsdAnyUri = config.generate_url(UrlKind::Actor).parse()?;
if !input.object.is(&my_id) && !input.object.is(&public()) { if !input.object_is(&my_id) && !input.object_is(&public()) {
return Err(MyError::WrongActor(input.object.id().to_string())); return Err(MyError::WrongActor(id_string(
input.object().as_single_id(),
)?));
} }
jobs.queue(Follow::new(is_listener, input, actor))?; jobs.queue(Follow::new(is_listener, input, actor))?;

View file

@ -1,5 +1,5 @@
@use crate::data::Contact; @use crate::data::Contact;
@use activitystreams::primitives::XsdAnyUri; @use activitystreams_new::primitives::XsdAnyUri;
@(contact: &Contact, base: &XsdAnyUri) @(contact: &Contact, base: &XsdAnyUri)

View file

@ -1,5 +1,5 @@
@use crate::data::Info; @use crate::data::Info;
@use activitystreams::primitives::XsdAnyUri; @use activitystreams_new::primitives::XsdAnyUri;
@(info: &Info, base: &XsdAnyUri) @(info: &Info, base: &XsdAnyUri)

View file

@ -1,5 +1,5 @@
@use crate::{data::{Contact, Instance}, templates::admin}; @use crate::{data::{Contact, Instance}, templates::admin};
@use activitystreams::primitives::XsdAnyUri; @use activitystreams_new::primitives::XsdAnyUri;
@(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &XsdAnyUri) @(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &XsdAnyUri)