//! Handles incoming activities, verifying HTTP signatures and other checks //! #![doc = include_str!("../../docs/08_receiving_activities.md")] use crate::{ config::RequestData, error::Error, fetch::object_id::ObjectId, http_signatures::{verify_inbox_hash, verify_signature}, traits::{ActivityHandler, Actor, ApubObject}, }; use axum::{ async_trait, body::{Bytes, HttpBody}, extract::FromRequest, http::{Request, StatusCode}, response::{IntoResponse, Response}, }; use http::{HeaderMap, Method, Uri}; use serde::de::DeserializeOwned; use tracing::debug; /// Handles incoming activities, verifying HTTP signatures and other checks pub async fn receive_activity( activity_data: ActivityData, data: &RequestData, ) -> Result<(), ::Error> where Activity: ActivityHandler + DeserializeOwned + Send + 'static, ActorT: ApubObject + Actor + Send + 'static, for<'de2> ::ApubType: serde::Deserialize<'de2>, ::Error: From + From + From<::Error> + From, ::Error: From + From, Datatype: Clone, { verify_inbox_hash(activity_data.headers.get("Digest"), &activity_data.body)?; let activity: Activity = serde_json::from_slice(&activity_data.body)?; data.config.verify_url_and_domain(&activity).await?; let actor = ObjectId::::from(activity.actor().clone()) .dereference(data) .await?; // TODO: why do errors here not get returned over http? verify_signature( &activity_data.headers, &activity_data.method, &activity_data.uri, actor.public_key(), )?; debug!("Receiving activity {}", activity.id().to_string()); activity.receive(data).await?; Ok(()) } /// Contains all data that is necessary to receive an activity from an HTTP request #[derive(Debug)] pub struct ActivityData { headers: HeaderMap, method: Method, uri: Uri, body: Vec, } #[async_trait] impl FromRequest for ActivityData where Bytes: FromRequest, B: HttpBody + Send + 'static, S: Send + Sync, ::Error: std::fmt::Display, ::Data: Send, { type Rejection = Response; async fn from_request(req: Request, _state: &S) -> Result { let (parts, body) = req.into_parts(); // this wont work if the body is an long running stream let bytes = hyper::body::to_bytes(body) .await .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response())?; Ok(Self { headers: parts.headers, method: parts.method, uri: parts.uri, body: bytes.to_vec(), }) } } // TODO: copy tests from actix-web inbox and implement for axum as well