relay/src/inbox.rs

331 lines
9.1 KiB
Rust
Raw Normal View History

2020-03-16 04:15:50 +00:00
use crate::{
apub::{AcceptedActors, AcceptedObjects, ValidTypes},
config::{Config, UrlKind},
2020-03-19 22:19:05 +00:00
db::Db,
2020-03-16 04:15:50 +00:00
error::MyError,
2020-03-18 04:35:20 +00:00
requests::Requests,
responses::accepted,
state::State,
2020-03-16 04:15:50 +00:00
};
2020-03-15 17:49:27 +00:00
use activitystreams::{
2020-03-18 22:49:56 +00:00
activity::{Accept, Announce, Follow, Undo},
2020-03-15 22:37:53 +00:00
context,
2020-03-18 17:06:08 +00:00
object::properties::ObjectProperties,
2020-03-15 17:49:27 +00:00
primitives::XsdAnyUri,
2020-03-18 22:49:56 +00:00
public, security,
2020-03-15 17:49:27 +00:00
};
2020-03-18 04:35:20 +00:00
use actix_web::{web, HttpResponse};
2020-03-15 17:49:27 +00:00
use futures::join;
use http_signature_normalization_actix::prelude::{DigestVerified, SignatureVerified};
2020-03-18 04:58:13 +00:00
use log::error;
2020-03-18 17:06:08 +00:00
use std::convert::TryInto;
2020-03-18 04:58:13 +00:00
2020-03-15 02:05:40 +00:00
pub async fn inbox(
2020-03-18 04:35:20 +00:00
db: web::Data<Db>,
2020-03-15 02:05:40 +00:00
state: web::Data<State>,
config: web::Data<Config>,
2020-03-18 04:35:20 +00:00
client: web::Data<Requests>,
2020-03-15 02:05:40 +00:00
input: web::Json<AcceptedObjects>,
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-03-18 04:35:20 +00:00
let actor = client.fetch_actor(&input.actor).await?;
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-18 17:42:48 +00:00
state.is_listener(actor.inbox())
2020-03-18 04:58:13 +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-18 04:58:13 +00:00
if input.kind != ValidTypes::Follow && !is_listener {
2020-03-18 17:42:48 +00:00
return Err(MyError::NotSubscribed(actor.inbox().to_string()));
2020-03-18 04:58:13 +00:00
}
if config.validate_signatures() && (digest_verified.is_none() || verified.is_none()) {
return Err(MyError::NoSignature(actor.public_key.id.to_string()));
} else if config.validate_signatures() {
if let Some(verified) = verified {
if actor.public_key.id.as_str() != verified.key_id() {
error!("Bad actor, more info: {:?}", input);
return Err(MyError::BadActor(
actor.public_key.id.to_string(),
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-15 22:37:53 +00:00
ValidTypes::Announce | ValidTypes::Create => {
handle_announce(&state, &config, &client, input, actor).await
2020-03-15 22:37:53 +00:00
}
ValidTypes::Follow => handle_follow(&db, &config, &client, input, actor, is_listener).await,
2020-03-15 22:37:53 +00:00
ValidTypes::Delete | ValidTypes::Update => {
2020-03-18 04:35:20 +00:00
handle_forward(&state, &client, input, actor).await
2020-03-15 22:37:53 +00:00
}
ValidTypes::Undo => handle_undo(&db, &state, &config, &client, input, actor).await,
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-15 22:37:53 +00:00
async fn handle_undo(
2020-03-18 04:35:20 +00:00
db: &Db,
state: &State,
config: &Config,
2020-03-18 04:35:20 +00:00
client: &Requests,
2020-03-15 22:37:53 +00:00
input: AcceptedObjects,
actor: AcceptedActors,
) -> Result<HttpResponse, 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-18 05:43:31 +00:00
return handle_forward(state, client, input, actor).await;
2020-03-15 22:37:53 +00:00
}
let my_id: XsdAnyUri = config.generate_url(UrlKind::Actor).parse()?;
2020-03-18 04:58:13 +00:00
if !input.object.child_object_is(&my_id) && !input.object.child_object_is(&public()) {
return Err(MyError::WrongActor(input.object.id().to_string()));
}
2020-03-15 22:37:53 +00:00
let inbox = actor.inbox().to_owned();
2020-03-18 18:25:43 +00:00
db.remove_listener(inbox).await?;
2020-03-15 22:37:53 +00:00
let undo = generate_undo_follow(config, &actor.id, &my_id)?;
2020-03-15 22:37:53 +00:00
2020-03-18 04:35:20 +00:00
let client2 = client.clone();
let inbox = actor.inbox().clone();
let undo2 = undo.clone();
actix::Arbiter::spawn(async move {
2020-03-18 04:35:20 +00:00
let _ = client2.deliver(inbox, &undo2).await;
});
2020-03-15 22:37:53 +00:00
2020-03-18 05:01:14 +00:00
Ok(accepted(undo))
2020-03-15 22:37:53 +00:00
}
async fn handle_forward(
2020-03-18 04:35:20 +00:00
state: &State,
client: &Requests,
2020-03-15 22:37:53 +00:00
input: AcceptedObjects,
actor: AcceptedActors,
) -> Result<HttpResponse, MyError> {
let object_id = input.object.id();
2020-03-18 04:35:20 +00:00
let inboxes = get_inboxes(state, &actor, &object_id).await?;
client.deliver_many(inboxes, input.clone());
2020-03-15 22:37:53 +00:00
2020-03-18 05:01:14 +00:00
Ok(accepted(input))
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,
config: &Config,
2020-03-18 04:35:20 +00:00
client: &Requests,
2020-03-15 22:37:53 +00:00
input: AcceptedObjects,
actor: AcceptedActors,
) -> Result<HttpResponse, MyError> {
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
}
let activity_id: XsdAnyUri = config.generate_url(UrlKind::Activity).parse()?;
2020-03-15 22:37:53 +00:00
let announce = generate_announce(config, &activity_id, object_id)?;
2020-03-18 04:35:20 +00:00
let inboxes = get_inboxes(state, &actor, &object_id).await?;
client.deliver_many(inboxes, announce.clone());
2020-03-15 22:37:53 +00:00
state.cache(object_id.to_owned(), activity_id).await;
2020-03-18 05:01:14 +00:00
Ok(accepted(announce))
2020-03-15 17:49:27 +00:00
}
async fn handle_follow(
2020-03-18 04:35:20 +00:00
db: &Db,
config: &Config,
2020-03-18 04:35:20 +00:00
client: &Requests,
2020-03-15 17:49:27 +00:00
input: AcceptedObjects,
actor: AcceptedActors,
2020-03-18 05:43:31 +00:00
is_listener: bool,
2020-03-15 22:37:53 +00:00
) -> Result<HttpResponse, MyError> {
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()) {
return Err(MyError::WrongActor(input.object.id().to_string()));
2020-03-15 17:49:27 +00:00
}
2020-03-18 18:56:13 +00:00
if !is_listener {
2020-03-15 22:37:53 +00:00
let inbox = actor.inbox().to_owned();
2020-03-18 18:25:43 +00:00
db.add_listener(inbox).await?;
2020-03-18 04:35:20 +00:00
2020-03-18 18:56:13 +00:00
// if following relay directly, not just following 'public', followback
if input.object.is(&my_id) {
let follow = generate_follow(config, &actor.id, &my_id)?;
2020-03-18 18:56:13 +00:00
let client2 = client.clone();
let inbox = actor.inbox().clone();
let follow2 = follow.clone();
actix::Arbiter::spawn(async move {
let _ = client2.deliver(inbox, &follow2).await;
});
}
2020-03-15 17:49:27 +00:00
}
let accept = generate_accept_follow(config, &actor.id, &input.id, &my_id)?;
2020-03-18 04:35:20 +00:00
let client2 = client.clone();
let inbox = actor.inbox().clone();
let accept2 = accept.clone();
actix::Arbiter::spawn(async move {
2020-03-18 04:35:20 +00:00
let _ = client2.deliver(inbox, &accept2).await;
});
2020-03-18 05:01:14 +00:00
Ok(accepted(accept))
}
// Generate a type that says "I want to stop following you"
fn generate_undo_follow(
config: &Config,
actor_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<Undo, MyError> {
let mut undo = Undo::default();
undo.undo_props
2020-03-18 17:06:08 +00:00
.set_actor_xsd_any_uri(my_id.clone())?
2020-03-18 22:49:56 +00:00
.set_object_base_box({
2020-03-18 17:06:08 +00:00
let mut follow = Follow::default();
follow
.object_props
.set_id(config.generate_url(UrlKind::Activity))?;
2020-03-18 17:06:08 +00:00
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())
}
// Generate a type that says "Look at this object"
fn generate_announce(
config: &Config,
activity_id: &XsdAnyUri,
object_id: &XsdAnyUri,
) -> Result<Announce, MyError> {
let mut announce = Announce::default();
announce
.announce_props
.set_object_xsd_any_uri(object_id.clone())?
.set_actor_xsd_any_uri(config.generate_url(UrlKind::Actor))?;
2020-03-18 17:06:08 +00:00
prepare_activity(
announce,
activity_id.clone(),
config.generate_url(UrlKind::Followers),
2020-03-18 17:06:08 +00:00
)
}
// Generate a type that says "I want to follow you"
fn generate_follow(
config: &Config,
actor_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<Follow, MyError> {
let mut follow = Follow::default();
follow
.follow_props
.set_object_xsd_any_uri(actor_id.clone())?
.set_actor_xsd_any_uri(my_id.clone())?;
2020-03-18 17:06:08 +00:00
prepare_activity(
follow,
config.generate_url(UrlKind::Activity),
2020-03-18 17:06:08 +00:00
actor_id.clone(),
)
}
// Generate a type that says "I accept your follow request"
fn generate_accept_follow(
config: &Config,
actor_id: &XsdAnyUri,
input_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<Accept, MyError> {
2020-03-15 17:49:27 +00:00
let mut accept = Accept::default();
accept
.accept_props
2020-03-18 17:06:08 +00:00
.set_actor_xsd_any_uri(my_id.clone())?
2020-03-18 22:49:56 +00:00
.set_object_base_box({
2020-03-18 17:06:08 +00:00
let mut follow = Follow::default();
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
})?;
prepare_activity(
accept,
config.generate_url(UrlKind::Activity),
2020-03-18 17:06:08 +00:00
actor_id.clone(),
)
}
2020-03-15 17:49:27 +00:00
2020-03-18 17:06:08 +00:00
fn prepare_activity<T, U, V>(
mut t: T,
id: impl TryInto<XsdAnyUri, Error = U>,
to: impl TryInto<XsdAnyUri, Error = V>,
) -> Result<T, MyError>
where
T: AsMut<ObjectProperties>,
MyError: From<U> + From<V>,
{
t.as_mut()
.set_id(id.try_into()?)?
.set_many_to_xsd_any_uris(vec![to.try_into()?])?
2020-03-18 22:49:56 +00:00
.set_many_context_xsd_any_uris(vec![context(), security()])?;
2020-03-18 17:06:08 +00:00
Ok(t)
2020-03-15 02:05:40 +00:00
}
2020-03-15 22:37:53 +00:00
async fn get_inboxes(
2020-03-18 04:35:20 +00:00
state: &State,
2020-03-15 22:37:53 +00:00
actor: &AcceptedActors,
object_id: &XsdAnyUri,
) -> Result<Vec<XsdAnyUri>, MyError> {
2020-03-16 03:36:46 +00:00
let domain = object_id
.as_url()
.host()
.ok_or(MyError::Domain)?
.to_string();
2020-03-15 22:37:53 +00:00
let inbox = actor.inbox();
Ok(state.listeners_without(&inbox, &domain).await)
}