2020-03-16 04:15:50 +00:00
|
|
|
use crate::{
|
2020-03-23 22:17:53 +00:00
|
|
|
apub::{AcceptedObjects, ValidTypes},
|
2020-03-20 00:55:11 +00:00
|
|
|
config::{Config, UrlKind},
|
2020-03-23 22:17:53 +00:00
|
|
|
data::{Actor, ActorCache, State},
|
2020-03-16 04:15:50 +00:00
|
|
|
error::MyError,
|
2020-03-30 17:10:04 +00:00
|
|
|
jobs::apub::{Announce, Follow, Forward, Reject, Undo},
|
2020-03-21 20:24:05 +00:00
|
|
|
jobs::JobServer,
|
2020-03-18 04:35:20 +00:00
|
|
|
requests::Requests,
|
2020-03-23 17:38:39 +00:00
|
|
|
routes::accepted,
|
2020-03-16 04:15:50 +00:00
|
|
|
};
|
2020-03-30 17:10:04 +00:00
|
|
|
use activitystreams::{primitives::XsdAnyUri, public};
|
2020-03-18 04:35:20 +00:00
|
|
|
use actix_web::{web, HttpResponse};
|
2020-03-15 17:49:27 +00:00
|
|
|
use futures::join;
|
2020-03-20 03:23:10 +00:00
|
|
|
use http_signature_normalization_actix::prelude::{DigestVerified, SignatureVerified};
|
2020-03-18 04:58:13 +00:00
|
|
|
use log::error;
|
|
|
|
|
2020-03-23 17:38:39 +00:00
|
|
|
pub async fn route(
|
2020-03-15 02:05:40 +00:00
|
|
|
state: web::Data<State>,
|
2020-03-23 22:17:53 +00:00
|
|
|
actors: web::Data<ActorCache>,
|
2020-03-20 00:55:11 +00:00
|
|
|
config: web::Data<Config>,
|
2020-03-18 04:35:20 +00:00
|
|
|
client: web::Data<Requests>,
|
2020-03-21 20:24:05 +00:00
|
|
|
jobs: web::Data<JobServer>,
|
2020-03-15 02:05:40 +00:00
|
|
|
input: web::Json<AcceptedObjects>,
|
2020-03-20 03:23:10 +00:00
|
|
|
verified: Option<SignatureVerified>,
|
|
|
|
digest_verified: Option<DigestVerified>,
|
2020-03-15 22:37:53 +00:00
|
|
|
) -> Result<HttpResponse, MyError> {
|
2020-03-15 02:05:40 +00:00
|
|
|
let input = input.into_inner();
|
|
|
|
|
2020-04-22 22:41:01 +00:00
|
|
|
let actor = actors.get(&input.actor, &client).await?.into_inner();
|
2020-03-17 20:52:26 +00:00
|
|
|
|
2020-03-18 04:58:13 +00:00
|
|
|
let (is_blocked, is_whitelisted, is_listener) = join!(
|
|
|
|
state.is_blocked(&actor.id),
|
|
|
|
state.is_whitelisted(&actor.id),
|
2020-03-23 22:17:53 +00:00
|
|
|
state.is_listener(&actor.inbox)
|
2020-03-18 04:58:13 +00:00
|
|
|
);
|
2020-03-18 02:16:09 +00:00
|
|
|
|
|
|
|
if is_blocked {
|
|
|
|
return Err(MyError::Blocked(actor.id.to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if !is_whitelisted {
|
|
|
|
return Err(MyError::Whitelist(actor.id.to_string()));
|
|
|
|
}
|
|
|
|
|
2020-03-21 21:01:54 +00:00
|
|
|
if !is_listener && !valid_without_listener(&input) {
|
2020-03-23 22:17:53 +00:00
|
|
|
return Err(MyError::NotSubscribed(actor.inbox.to_string()));
|
2020-03-18 04:58:13 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 03:23:10 +00:00
|
|
|
if config.validate_signatures() && (digest_verified.is_none() || verified.is_none()) {
|
2020-03-23 22:17:53 +00:00
|
|
|
return Err(MyError::NoSignature(actor.public_key_id.to_string()));
|
2020-03-20 03:23:10 +00:00
|
|
|
} else if config.validate_signatures() {
|
|
|
|
if let Some(verified) = verified {
|
2020-03-23 22:17:53 +00:00
|
|
|
if actor.public_key_id.as_str() != verified.key_id() {
|
2020-03-20 03:23:10 +00:00
|
|
|
error!("Bad actor, more info: {:?}", input);
|
|
|
|
return Err(MyError::BadActor(
|
2020-03-23 22:17:53 +00:00
|
|
|
actor.public_key_id.to_string(),
|
2020-03-20 03:23:10 +00:00
|
|
|
verified.key_id().to_owned(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
2020-03-17 19:52:33 +00:00
|
|
|
}
|
|
|
|
|
2020-03-15 02:05:40 +00:00
|
|
|
match input.kind {
|
2020-03-30 17:10:04 +00:00
|
|
|
ValidTypes::Accept => handle_accept(&config, input).await?,
|
|
|
|
ValidTypes::Reject => handle_reject(&config, &jobs, input, actor).await?,
|
2020-03-15 22:37:53 +00:00
|
|
|
ValidTypes::Announce | ValidTypes::Create => {
|
2020-03-30 17:10:04 +00:00
|
|
|
handle_announce(&state, &jobs, input, actor).await?
|
2020-03-21 21:01:54 +00:00
|
|
|
}
|
2020-03-30 17:10:04 +00:00
|
|
|
ValidTypes::Follow => handle_follow(&config, &jobs, input, actor, is_listener).await?,
|
|
|
|
ValidTypes::Delete | ValidTypes::Update => handle_forward(&jobs, input, actor).await?,
|
|
|
|
ValidTypes::Undo => handle_undo(&config, &jobs, input, actor, is_listener).await?,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(accepted(serde_json::json!({})))
|
2020-03-21 21:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn valid_without_listener(input: &AcceptedObjects) -> bool {
|
|
|
|
match input.kind {
|
|
|
|
ValidTypes::Follow => true,
|
|
|
|
ValidTypes::Undo if input.object.is_kind("Follow") => true,
|
|
|
|
_ => false,
|
2020-03-15 02:05:40 +00:00
|
|
|
}
|
2020-03-15 22:37:53 +00:00
|
|
|
}
|
2020-03-15 02:05:40 +00:00
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
async fn handle_accept(config: &Config, input: AcceptedObjects) -> Result<(), MyError> {
|
2020-03-21 20:24:05 +00:00
|
|
|
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()?)
|
|
|
|
{
|
|
|
|
return Err(MyError::WrongActor(input.object.id().to_string()));
|
|
|
|
}
|
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
Ok(())
|
2020-03-21 20:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn handle_reject(
|
|
|
|
config: &Config,
|
|
|
|
jobs: &JobServer,
|
|
|
|
input: AcceptedObjects,
|
2020-03-23 22:17:53 +00:00
|
|
|
actor: Actor,
|
2020-03-30 17:10:04 +00:00
|
|
|
) -> Result<(), MyError> {
|
2020-03-21 20:24:05 +00:00
|
|
|
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()?)
|
|
|
|
{
|
|
|
|
return Err(MyError::WrongActor(input.object.id().to_string()));
|
|
|
|
}
|
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
jobs.queue(Reject(actor))?;
|
2020-03-21 20:24:05 +00:00
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
Ok(())
|
2020-03-21 20:24:05 +00:00
|
|
|
}
|
|
|
|
|
2020-03-15 22:37:53 +00:00
|
|
|
async fn handle_undo(
|
2020-03-20 00:55:11 +00:00
|
|
|
config: &Config,
|
2020-03-21 20:24:05 +00:00
|
|
|
jobs: &JobServer,
|
2020-03-15 22:37:53 +00:00
|
|
|
input: AcceptedObjects,
|
2020-03-23 22:17:53 +00:00
|
|
|
actor: Actor,
|
2020-03-21 21:01:54 +00:00
|
|
|
is_listener: bool,
|
2020-03-30 17:10:04 +00:00
|
|
|
) -> Result<(), MyError> {
|
2020-03-18 05:43:31 +00:00
|
|
|
match input.object.kind() {
|
|
|
|
Some("Follow") | Some("Announce") | Some("Create") => (),
|
|
|
|
_ => {
|
|
|
|
return Err(MyError::Kind(
|
|
|
|
input.object.kind().unwrap_or("unknown").to_owned(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-15 22:37:53 +00:00
|
|
|
if !input.object.is_kind("Follow") {
|
2020-03-21 21:01:54 +00:00
|
|
|
if is_listener {
|
2020-03-30 17:10:04 +00:00
|
|
|
jobs.queue(Forward::new(input, actor))?;
|
|
|
|
return Ok(());
|
2020-03-21 21:01:54 +00:00
|
|
|
} else {
|
|
|
|
return Err(MyError::Kind(
|
|
|
|
input.object.kind().unwrap_or("unknown").to_owned(),
|
|
|
|
));
|
|
|
|
}
|
2020-03-15 22:37:53 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
let my_id: XsdAnyUri = config.generate_url(UrlKind::Actor).parse()?;
|
2020-03-18 02:16:09 +00:00
|
|
|
|
2020-03-18 04:58:13 +00:00
|
|
|
if !input.object.child_object_is(&my_id) && !input.object.child_object_is(&public()) {
|
2020-03-18 02:16:09 +00:00
|
|
|
return Err(MyError::WrongActor(input.object.id().to_string()));
|
|
|
|
}
|
|
|
|
|
2020-03-21 21:01:54 +00:00
|
|
|
if !is_listener {
|
2020-03-30 17:10:04 +00:00
|
|
|
return Ok(());
|
2020-03-23 22:17:53 +00:00
|
|
|
}
|
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
jobs.queue(Undo::new(input, actor))?;
|
|
|
|
Ok(())
|
2020-03-15 22:37:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn handle_forward(
|
2020-03-21 20:24:05 +00:00
|
|
|
jobs: &JobServer,
|
2020-03-15 22:37:53 +00:00
|
|
|
input: AcceptedObjects,
|
2020-03-23 22:17:53 +00:00
|
|
|
actor: Actor,
|
2020-03-30 17:10:04 +00:00
|
|
|
) -> Result<(), MyError> {
|
|
|
|
jobs.queue(Forward::new(input, actor))?;
|
2020-03-15 22:37:53 +00:00
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
Ok(())
|
2020-03-15 22:37:53 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 05:43:31 +00:00
|
|
|
async fn handle_announce(
|
2020-03-18 04:35:20 +00:00
|
|
|
state: &State,
|
2020-03-21 20:24:05 +00:00
|
|
|
jobs: &JobServer,
|
2020-03-15 22:37:53 +00:00
|
|
|
input: AcceptedObjects,
|
2020-03-23 22:17:53 +00:00
|
|
|
actor: Actor,
|
2020-03-30 17:10:04 +00:00
|
|
|
) -> Result<(), MyError> {
|
2020-03-15 22:37:53 +00:00
|
|
|
let object_id = input.object.id();
|
|
|
|
|
|
|
|
if state.is_cached(object_id).await {
|
2020-03-16 03:36:46 +00:00
|
|
|
return Err(MyError::Duplicate);
|
2020-03-15 22:37:53 +00:00
|
|
|
}
|
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
jobs.queue(Announce::new(object_id.to_owned(), actor))?;
|
2020-03-15 22:37:53 +00:00
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
Ok(())
|
2020-03-15 17:49:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn handle_follow(
|
2020-03-20 00:55:11 +00:00
|
|
|
config: &Config,
|
2020-03-21 20:24:05 +00:00
|
|
|
jobs: &JobServer,
|
2020-03-15 17:49:27 +00:00
|
|
|
input: AcceptedObjects,
|
2020-03-23 22:17:53 +00:00
|
|
|
actor: Actor,
|
2020-03-18 05:43:31 +00:00
|
|
|
is_listener: bool,
|
2020-03-30 17:10:04 +00:00
|
|
|
) -> Result<(), MyError> {
|
2020-03-20 00:55:11 +00:00
|
|
|
let my_id: XsdAnyUri = config.generate_url(UrlKind::Actor).parse()?;
|
2020-03-15 17:49:27 +00:00
|
|
|
|
2020-03-18 05:43:31 +00:00
|
|
|
if !input.object.is(&my_id) && !input.object.is(&public()) {
|
2020-03-18 02:16:09 +00:00
|
|
|
return Err(MyError::WrongActor(input.object.id().to_string()));
|
2020-03-15 17:49:27 +00:00
|
|
|
}
|
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
jobs.queue(Follow::new(is_listener, input, actor))?;
|
2020-03-15 17:49:27 +00:00
|
|
|
|
2020-03-30 17:10:04 +00:00
|
|
|
Ok(())
|
2020-03-15 22:37:53 +00:00
|
|
|
}
|