forked from mirrors/relay
Fully lean on activitystreams-new
This commit is contained in:
parent
0b2763ab8b
commit
729e425e32
27 changed files with 514 additions and 544 deletions
483
Cargo.lock
generated
483
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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"
|
||||
|
|
181
src/apub.rs
181
src/apub.rs
|
@ -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,
|
||||
|
||||
pub object: ValidObjects,
|
||||
|
||||
#[serde(flatten)]
|
||||
ext: HashMap<String, serde_json::Value>,
|
||||
fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
|
||||
Ok(PublicKey {
|
||||
public_key: unparsed_mut.remove("publicKey")?,
|
||||
})
|
||||
}
|
||||
|
||||
#[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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
|
|
18
src/error.rs
18
src/error.rs
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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?;
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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()])?;
|
||||
|
||||
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 {
|
||||
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()?,
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
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))
|
||||
}
|
||||
|
|
|
@ -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))?;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@use crate::data::Contact;
|
||||
@use activitystreams::primitives::XsdAnyUri;
|
||||
@use activitystreams_new::primitives::XsdAnyUri;
|
||||
|
||||
@(contact: &Contact, base: &XsdAnyUri)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@use crate::data::Info;
|
||||
@use activitystreams::primitives::XsdAnyUri;
|
||||
@use activitystreams_new::primitives::XsdAnyUri;
|
||||
|
||||
@(info: &Info, base: &XsdAnyUri)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue