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]
anyhow = "1.0"
actix-rt = "1.0.0"
actix-web = { version = "3.0.0-alpha.1", features = ["rustls"] }
actix-rt = "1.1.1"
actix-web = { version = "3.0.0-alpha.2", features = ["rustls"] }
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"
async-trait = "0.1.24"
background-jobs = "0.8.0-alpha.2"

View file

@ -1,15 +1,14 @@
use activitystreams::{
actor::Actor,
ext::Extension,
object::{Object, ObjectBox},
use activitystreams_ext::{Ext1, UnparsedExtension};
use activitystreams_new::{
activity::ActorAndObject,
actor::{Actor, ApActor},
primitives::XsdAnyUri,
Base, BaseBox, PropRefs,
unparsed::UnparsedMutExt,
};
use std::collections::HashMap;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKey {
pub struct PublicKeyInner {
pub id: XsdAnyUri,
pub owner: XsdAnyUri,
pub public_key_pem: String,
@ -17,21 +16,8 @@ pub struct PublicKey {
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKeyExtension {
pub public_key: PublicKey,
}
#[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>,
pub struct PublicKey {
pub public_key: PublicKeyInner,
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
@ -47,141 +33,32 @@ pub enum ValidTypes {
Update,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(untagged)]
#[serde(rename_all = "camelCase")]
pub enum ValidObjects {
Id(XsdAnyUri),
Object(AnyExistingObject),
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "PascalCase")]
pub enum UndoTypes {
Follow,
Announce,
Create,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AcceptedObjects {
pub id: XsdAnyUri,
pub type AcceptedUndoObjects = ActorAndObject<UndoTypes>;
pub type AcceptedActivities = ActorAndObject<ValidTypes>;
pub type AcceptedActors = Ext1<ApActor<Actor<String>>, PublicKey>;
#[serde(rename = "type")]
pub kind: ValidTypes,
impl<U> UnparsedExtension<U> for PublicKey
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,
#[serde(flatten)]
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)
fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
unparsed_mut.insert("publicKey", self.public_key)?;
Ok(())
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
use activitystreams::primitives::XsdAnyUriError;
use activitystreams_new::primitives::XsdAnyUriError;
use actix_web::{
error::{BlockingError, ResponseError},
http::StatusCode,
@ -96,6 +96,18 @@ pub enum MyError {
#[error("Response has invalid status code, {0}")]
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")]
Domain,
@ -112,7 +124,9 @@ impl ResponseError for MyError {
| MyError::BadActor(_, _) => StatusCode::FORBIDDEN,
MyError::NotSubscribed(_) => StatusCode::UNAUTHORIZED,
MyError::Duplicate => StatusCode::ACCEPTED,
MyError::Kind(_) => StatusCode::BAD_REQUEST,
MyError::Kind(_) | MyError::MissingKind | MyError::MissingId | MyError::ObjectCount => {
StatusCode::BAD_REQUEST
}
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}

View file

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

View file

@ -1,23 +1,27 @@
use crate::{
apub::AcceptedObjects,
apub::AcceptedActivities,
config::{Config, UrlKind},
data::Actor,
error::MyError,
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 std::{future::Future, pin::Pin};
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Follow {
is_listener: bool,
input: AcceptedObjects,
input: AcceptedActivities,
actor: Actor,
}
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 {
is_listener,
input,
@ -32,7 +36,7 @@ impl Follow {
let my_id: XsdAnyUri = state.config.generate_url(UrlKind::Actor).parse()?;
// 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)?;
state
.job_server
@ -41,7 +45,12 @@ impl Follow {
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
.job_server
@ -55,13 +64,8 @@ fn generate_follow(
config: &Config,
actor_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<activitystreams::activity::Follow, MyError> {
let mut follow = activitystreams::activity::Follow::default();
follow
.follow_props
.set_object_xsd_any_uri(actor_id.clone())?
.set_actor_xsd_any_uri(my_id.clone())?;
) -> Result<AsFollow, MyError> {
let follow = AsFollow::new(my_id.clone(), actor_id.clone());
prepare_activity(
follow,
@ -76,23 +80,12 @@ fn generate_accept_follow(
actor_id: &XsdAnyUri,
input_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<activitystreams::activity::Accept, MyError> {
let mut accept = activitystreams::activity::Accept::default();
) -> Result<AsAccept, MyError> {
let mut follow = AsFollow::new(actor_id.clone(), my_id.clone());
accept
.accept_props
.set_actor_xsd_any_uri(my_id.clone())?
.set_object_base_box({
let mut follow = activitystreams::activity::Follow::default();
follow.set_id(input_id.clone());
follow.object_props.set_id(input_id.clone())?;
follow
.follow_props
.set_object_xsd_any_uri(my_id.clone())?
.set_actor_xsd_any_uri(actor_id.clone())?;
follow
})?;
let accept = AsAccept::new(my_id.clone(), follow.into_any_base()?);
prepare_activity(
accept,

View file

@ -1,24 +1,30 @@
use crate::{
apub::AcceptedObjects,
apub::AcceptedActivities,
data::Actor,
error::MyError,
jobs::{apub::get_inboxes, DeliverMany, JobState},
};
use activitystreams_new::prelude::*;
use background_jobs::ActixJob;
use std::{future::Future, pin::Pin};
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Forward {
input: AcceptedObjects,
input: AcceptedActivities,
actor: Actor,
}
impl Forward {
pub fn new(input: AcceptedObjects, actor: Actor) -> Self {
pub fn new(input: AcceptedActivities, actor: Actor) -> Self {
Forward { input, actor }
}
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?;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,13 +1,17 @@
use crate::{
apub::PublicKey,
apub::{PublicKey, PublicKeyInner},
config::{Config, UrlKind},
data::State,
error::MyError,
routes::ok,
};
use activitystreams::{
actor::Application, context, endpoint::EndpointProperties, ext::Extensible,
object::properties::ObjectProperties, security,
use activitystreams_ext::Ext1;
use activitystreams_new::{
actor::{ApActor, Application, Endpoints},
context,
prelude::*,
primitives::{XsdAnyUri, XsdString},
security,
};
use actix_web::{web, Responder};
use rsa_pem::KeyExt;
@ -16,33 +20,42 @@ pub async fn route(
state: web::Data<State>,
config: web::Data<Config>,
) -> Result<impl Responder, MyError> {
let mut application = Application::full();
let mut endpoint = EndpointProperties::default();
endpoint.set_shared_inbox(config.generate_url(UrlKind::Inbox))?;
let props: &mut ObjectProperties = application.as_mut();
props
.set_id(config.generate_url(UrlKind::Actor))?
.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()])?;
let mut application = Ext1::new(
ApActor::new(
config.generate_url(UrlKind::Inbox).parse()?,
config.generate_url(UrlKind::Outbox).parse()?,
Application::new(),
),
PublicKey {
public_key: PublicKeyInner {
id: config.generate_url(UrlKind::MainKey).parse()?,
owner: config.generate_url(UrlKind::Actor).parse()?,
public_key_pem: state.public_key.to_pem_pkcs8()?,
},
},
);
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)?;
.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()
});
let public_key = PublicKey {
id: config.generate_url(UrlKind::MainKey).parse()?,
owner: config.generate_url(UrlKind::Actor).parse()?,
public_key_pem: state.public_key.to_pem_pkcs8()?,
};
Ok(ok(application.extend(public_key.into_ext())))
Ok(ok(application))
}

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
@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)