relay/src/routes/inbox.rs

247 lines
7.4 KiB
Rust
Raw Normal View History

2020-03-16 04:15:50 +00:00
use crate::{
2020-05-21 21:24:56 +00:00
apub::{AcceptedActivities, AcceptedUndoObjects, UndoTypes, ValidTypes},
config::{Config, UrlKind},
2021-02-10 04:05:06 +00:00
data::{ActorCache, State},
db::Actor,
2021-09-18 17:55:39 +00:00
error::{Error, ErrorKind},
2020-03-30 17:10:04 +00:00
jobs::apub::{Announce, Follow, Forward, Reject, Undo},
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-09-07 21:51:02 +00:00
use activitystreams::{
2022-01-17 22:54:45 +00:00
activity, base::AnyBase, iri_string::types::IriString, prelude::*, primitives::OneOrMany,
public,
2020-05-21 21:24:56 +00:00
};
2020-03-18 04:35:20 +00:00
use actix_web::{web, HttpResponse};
use http_signature_normalization_actix::prelude::{DigestVerified, SignatureVerified};
2020-03-18 04:58:13 +00:00
2022-11-16 03:26:13 +00:00
#[tracing::instrument(name = "Inbox", skip_all)]
2022-12-19 18:23:06 +00:00
#[allow(clippy::too_many_arguments)]
2021-02-10 04:17:20 +00:00
pub(crate) 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>,
config: web::Data<Config>,
2020-03-18 04:35:20 +00:00
client: web::Data<Requests>,
jobs: web::Data<JobServer>,
2020-05-21 21:24:56 +00:00
input: web::Json<AcceptedActivities>,
digest_verified: Option<DigestVerified>,
signature_verified: Option<SignatureVerified>,
2021-09-18 17:55:39 +00:00
) -> Result<HttpResponse, Error> {
2020-03-15 02:05:40 +00:00
let input = input.into_inner();
let kind = input.kind().ok_or(ErrorKind::MissingKind)?;
if digest_verified.is_some() && signature_verified.is_none() && *kind == ValidTypes::Delete {
return Ok(accepted(serde_json::json!({})));
} else if config.validate_signatures()
&& (digest_verified.is_none() || signature_verified.is_none())
{
return Err(ErrorKind::NoSignature(None).into());
}
let actor = actors
2020-05-21 21:24:56 +00:00
.get(
2021-09-18 17:55:39 +00:00
input.actor()?.as_single_id().ok_or(ErrorKind::MissingId)?,
2020-05-21 21:24:56 +00:00
&client,
)
.await?
.into_inner();
let is_allowed = state.db.is_allowed(actor.id.clone()).await?;
let is_connected = state.db.is_connected(actor.id.clone()).await?;
2021-02-10 04:05:06 +00:00
if !is_allowed {
2021-09-18 17:55:39 +00:00
return Err(ErrorKind::NotAllowed(actor.id.to_string()).into());
}
2021-02-10 04:05:06 +00:00
if !is_connected && !valid_without_listener(&input)? {
2021-09-18 17:55:39 +00:00
return Err(ErrorKind::NotSubscribed(actor.id.to_string()).into());
2020-03-18 04:58:13 +00:00
}
if config.validate_signatures() {
if let Some(verified) = signature_verified {
2020-03-23 22:17:53 +00:00
if actor.public_key_id.as_str() != verified.key_id() {
tracing::error!("Actor signed with wrong key");
2021-09-18 17:55:39 +00:00
return Err(ErrorKind::BadActor(
2020-03-23 22:17:53 +00:00
actor.public_key_id.to_string(),
verified.key_id().to_owned(),
2021-09-18 17:55:39 +00:00
)
.into());
}
} else {
tracing::error!("This case should never be reachable, since I handle signature checks earlier in the flow. If you see this in a log it means I did it wrong");
return Err(ErrorKind::NoSignature(Some(actor.public_key_id.to_string())).into());
}
2020-03-17 19:52:33 +00:00
}
match 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?
}
2021-02-10 06:44:48 +00:00
ValidTypes::Follow => handle_follow(&config, &jobs, input, actor).await?,
ValidTypes::Add | ValidTypes::Delete | ValidTypes::Remove | ValidTypes::Update => {
handle_forward(&jobs, input, actor).await?
}
2021-02-10 04:05:06 +00:00
ValidTypes::Undo => handle_undo(&config, &jobs, input, actor, is_connected).await?,
2020-03-30 17:10:04 +00:00
};
Ok(accepted(serde_json::json!({})))
}
2021-09-18 17:55:39 +00:00
fn valid_without_listener(input: &AcceptedActivities) -> Result<bool, Error> {
2020-05-21 21:24:56 +00:00
match input.kind() {
Some(ValidTypes::Follow) => Ok(true),
2022-01-17 22:54:45 +00:00
Some(ValidTypes::Undo) => Ok(single_object(input.object_unchecked())?.is_kind("Follow")),
2020-05-21 21:24:56 +00:00
_ => Ok(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
2021-09-18 17:55:39 +00:00
fn kind_str(base: &AnyBase) -> Result<&str, Error> {
base.kind_str()
.ok_or(ErrorKind::MissingKind)
.map_err(Into::into)
2020-05-21 21:24:56 +00:00
}
2022-01-17 22:54:45 +00:00
fn id_string(id: Option<&IriString>) -> Result<String, Error> {
2021-09-18 17:55:39 +00:00
id.map(|s| s.to_string())
.ok_or(ErrorKind::MissingId)
.map_err(Into::into)
2020-05-21 21:24:56 +00:00
}
2021-09-18 17:55:39 +00:00
fn single_object(o: &OneOrMany<AnyBase>) -> Result<&AnyBase, Error> {
o.as_one().ok_or(ErrorKind::ObjectCount).map_err(Into::into)
2020-05-21 21:24:56 +00:00
}
2021-09-18 17:55:39 +00:00
async fn handle_accept(config: &Config, input: AcceptedActivities) -> Result<(), Error> {
2022-01-17 22:54:45 +00:00
let base = single_object(input.object_unchecked())?.clone();
let follow = if let Some(follow) = activity::Follow::from_any_base(base)? {
2020-05-21 21:24:56 +00:00
follow
} else {
2022-01-17 22:54:45 +00:00
return Err(ErrorKind::Kind(
kind_str(single_object(input.object_unchecked())?)?.to_owned(),
)
.into());
2020-05-21 21:24:56 +00:00
};
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
2021-09-18 17:55:39 +00:00
return Err(ErrorKind::WrongActor(id_string(follow.actor()?.as_single_id())?).into());
}
2020-03-30 17:10:04 +00:00
Ok(())
}
async fn handle_reject(
config: &Config,
jobs: &JobServer,
2020-05-21 21:24:56 +00:00
input: AcceptedActivities,
2020-03-23 22:17:53 +00:00
actor: Actor,
2021-09-18 17:55:39 +00:00
) -> Result<(), Error> {
2022-01-17 22:54:45 +00:00
let base = single_object(input.object_unchecked())?.clone();
let follow = if let Some(follow) = activity::Follow::from_any_base(base)? {
2020-05-21 21:24:56 +00:00
follow
} else {
2022-01-17 22:54:45 +00:00
return Err(ErrorKind::Kind(
kind_str(single_object(input.object_unchecked())?)?.to_owned(),
)
.into());
2020-05-21 21:24:56 +00:00
};
if !follow.actor_is(&config.generate_url(UrlKind::Actor)) {
2021-09-18 17:55:39 +00:00
return Err(ErrorKind::WrongActor(id_string(follow.actor()?.as_single_id())?).into());
}
2021-10-11 19:19:32 +00:00
jobs.queue(Reject(actor)).await?;
2020-03-30 17:10:04 +00:00
Ok(())
}
2020-03-15 22:37:53 +00:00
async fn handle_undo(
config: &Config,
jobs: &JobServer,
2020-05-21 21:24:56 +00:00
input: AcceptedActivities,
2020-03-23 22:17:53 +00:00
actor: Actor,
is_listener: bool,
2021-09-18 17:55:39 +00:00
) -> Result<(), Error> {
2022-01-17 22:54:45 +00:00
let any_base = single_object(input.object_unchecked())?.clone();
2020-05-21 21:24:56 +00:00
let undone_object =
2021-09-18 17:55:39 +00:00
AcceptedUndoObjects::from_any_base(any_base)?.ok_or(ErrorKind::ObjectFormat)?;
2020-03-18 05:43:31 +00:00
2020-05-21 21:24:56 +00:00
if !undone_object.is_kind(&UndoTypes::Follow) {
if is_listener {
2021-10-11 19:19:32 +00:00
jobs.queue(Forward::new(input, actor)).await?;
2020-03-30 17:10:04 +00:00
return Ok(());
} else {
2021-09-18 17:55:39 +00:00
return Err(ErrorKind::NotSubscribed(actor.id.to_string()).into());
}
2020-03-15 22:37:53 +00:00
}
2022-01-17 22:54:45 +00:00
let my_id: IriString = config.generate_url(UrlKind::Actor);
2020-05-21 21:24:56 +00:00
if !undone_object.object_is(&my_id) && !undone_object.object_is(&public()) {
2022-01-17 22:54:45 +00:00
return Err(ErrorKind::WrongActor(id_string(
undone_object.object_unchecked().as_single_id(),
)?)
.into());
}
if !is_listener {
2020-03-30 17:10:04 +00:00
return Ok(());
2020-03-23 22:17:53 +00:00
}
2021-10-11 19:19:32 +00:00
jobs.queue(Undo::new(input, actor)).await?;
2020-03-30 17:10:04 +00:00
Ok(())
2020-03-15 22:37:53 +00:00
}
async fn handle_forward(
jobs: &JobServer,
2020-05-21 21:24:56 +00:00
input: AcceptedActivities,
2020-03-23 22:17:53 +00:00
actor: Actor,
2021-09-18 17:55:39 +00:00
) -> Result<(), Error> {
2021-10-11 19:19:32 +00:00
jobs.queue(Forward::new(input, actor)).await?;
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,
jobs: &JobServer,
2020-05-21 21:24:56 +00:00
input: AcceptedActivities,
2020-03-23 22:17:53 +00:00
actor: Actor,
2021-09-18 17:55:39 +00:00
) -> Result<(), Error> {
2022-01-17 22:54:45 +00:00
let object_id = input
.object_unchecked()
.as_single_id()
.ok_or(ErrorKind::MissingId)?;
2020-03-15 22:37:53 +00:00
2022-12-09 23:47:45 +00:00
if state.is_cached(object_id) {
2021-09-18 17:55:39 +00:00
return Err(ErrorKind::Duplicate.into());
2020-03-15 22:37:53 +00:00
}
2022-01-17 22:54:45 +00:00
jobs.queue(Announce::new(object_id.to_owned(), actor))
.await?;
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(
config: &Config,
jobs: &JobServer,
2020-05-21 21:24:56 +00:00
input: AcceptedActivities,
2020-03-23 22:17:53 +00:00
actor: Actor,
2021-09-18 17:55:39 +00:00
) -> Result<(), Error> {
2022-01-17 22:54:45 +00:00
let my_id: IriString = config.generate_url(UrlKind::Actor);
2020-03-15 17:49:27 +00:00
2020-05-21 21:24:56 +00:00
if !input.object_is(&my_id) && !input.object_is(&public()) {
2022-01-17 22:54:45 +00:00
return Err(
ErrorKind::WrongActor(id_string(input.object_unchecked().as_single_id())?).into(),
);
2020-03-15 17:49:27 +00:00
}
2021-10-11 19:19:32 +00:00
jobs.queue(Follow::new(input, actor)).await?;
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
}