//! Types for verifying requests with Actix Web use crate::{Config, PrepareVerifyError, SignatureVerify, Spawn}; use actix_web::{ body::MessageBody, dev::{Payload, Service, ServiceRequest, ServiceResponse, Transform}, http::StatusCode, Error, FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError, }; use futures_core::future::LocalBoxFuture; use std::{ collections::HashSet, future::{ready, Ready}, rc::Rc, task::{Context, Poll}, }; use tracing::{debug, Span}; use tracing_error::SpanTrace; use tracing_futures::Instrument; #[derive(Clone, Debug)] /// A marker type that is used to guard routes pub struct SignatureVerified(String); impl SignatureVerified { /// Return the Key ID used to verify the request /// /// It might be important for an application to verify that the payload being processed indeed /// belongs to the owner of the key used to sign the request. pub fn key_id(&self) -> &str { &self.0 } } #[derive(Clone, Debug)] /// The Verify signature middleware /// /// ```rust,ignore /// let middleware = VerifySignature::new(MyVerifier::new(), Config::default()).authorization(); /// /// HttpServer::new(move || { /// App::new() /// .wrap(middleware.clone()) /// .route("/protected", web::post().to(|_: SignatureVerified| "Verified Authorization Header")) /// .route("/unprotected", web::post().to(|| "No verification required")) /// }) /// ``` pub struct VerifySignature(T, Config, HeaderKind); #[derive(Debug)] #[doc(hidden)] pub struct VerifyMiddleware(Rc, Config, HeaderKind, T); #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] enum HeaderKind { Authorization, Signature, } impl std::fmt::Display for HeaderKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Authorization => { write!(f, "Authorization") } Self::Signature => { write!(f, "Signature") } } } } #[derive(Clone)] #[doc(hidden)] pub struct VerifyError { context: String, kind: VerifyErrorKind, } impl std::fmt::Debug for VerifyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{:?}", self.kind) } } impl std::fmt::Display for VerifyError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{}", self.kind)?; std::fmt::Display::fmt(&self.context, f) } } impl std::error::Error for VerifyError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.kind.source() } } #[derive(Clone, Debug, thiserror::Error)] enum VerifyErrorKind { #[error("Signature or Authorization header is missing")] MissingSignature, #[error("{0}")] ExpiredSignature(String), #[error("Signature field could not be parsed")] ParseField(&'static str), #[error("Signature is not a valid string")] ParseSignature, #[error("Request extension not present")] Extension, #[error("Required headers are missing")] MissingHeader(HashSet), } impl VerifyError { fn new(span: &Span, kind: VerifyErrorKind) -> Self { span.in_scope(|| VerifyError { context: SpanTrace::capture().to_string(), kind, }) } } impl VerifySignature where T: SignatureVerify, { /// Create a new middleware for verifying HTTP Signatures. A type implementing /// [`SignatureVerify`] is required, as well as a Config /// /// By default, this middleware expects to verify Signature headers, and requires the presence /// of the header pub fn new(verify_signature: T, config: Config) -> Self where Spawner: Spawn, { VerifySignature(verify_signature, config, HeaderKind::Signature) } /// Verify Authorization headers instead of Signature headers pub fn authorization(self) -> Self { VerifySignature(self.0, self.1, HeaderKind::Authorization) } } impl VerifyMiddleware where T: SignatureVerify + Clone + 'static, T::Future: 'static, S: Service, Error = Error> + 'static, B: MessageBody + 'static, { fn handle( &self, span: Span, req: ServiceRequest, ) -> LocalBoxFuture<'static, Result, Error>> { let res = self.1.begin_verify( req.method(), req.uri().path_and_query(), req.headers().clone(), ); let unverified = match res { Ok(unverified) => unverified, Err(PrepareVerifyError::Expired(reason)) => { return Box::pin(ready(Err(VerifyError::new( &span, VerifyErrorKind::ExpiredSignature(reason), ) .into()))); } Err(PrepareVerifyError::Missing) => { return Box::pin(ready(Err(VerifyError::new( &span, VerifyErrorKind::MissingSignature, ) .into()))); } Err(PrepareVerifyError::ParseField(field)) => { return Box::pin(ready(Err(VerifyError::new( &span, VerifyErrorKind::ParseField(field), ) .into()))); } Err(PrepareVerifyError::Header(_)) => { return Box::pin(ready(Err(VerifyError::new( &span, VerifyErrorKind::ParseSignature, ) .into()))); } Err(PrepareVerifyError::Required(mut req)) => { return Box::pin(ready(Err(VerifyError::new( &span, VerifyErrorKind::MissingHeader(req.take_headers()), ) .into()))); } }; let algorithm = unverified.algorithm().cloned(); let key_id = unverified.key_id().to_owned(); let verify_fut = unverified.verify(|signature, signing_string| { span.in_scope(|| { self.3.clone().signature_verify( algorithm, key_id.clone(), signature.to_string(), signing_string.to_string(), ) }) }); let service = Rc::clone(&self.0); Box::pin(async move { if verify_fut.instrument(span).await? { req.extensions_mut().insert(SignatureVerified(key_id)); } service.call(req).await }) } } impl HeaderKind { pub fn is_authorization(self) -> bool { HeaderKind::Authorization == self } pub fn is_signature(self) -> bool { HeaderKind::Signature == self } } impl FromRequest for SignatureVerified { type Error = VerifyError; type Future = Ready>; fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { let res = req .extensions() .get::() .cloned() .ok_or_else(|| VerifyError::new(&Span::current(), VerifyErrorKind::Extension)); if res.is_err() { debug!("Failed to fetch SignatureVerified from request"); } ready(res) } } impl Transform for VerifySignature where T: SignatureVerify + Clone + 'static, S: Service, Error = actix_web::Error> + 'static, S::Error: 'static, B: MessageBody + 'static, Spawner: Clone, { type Response = ServiceResponse; type Error = actix_web::Error; type Transform = VerifyMiddleware; type InitError = (); type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ready(Ok(VerifyMiddleware( Rc::new(service), self.1.clone(), self.2, self.0.clone(), ))) } } impl Service for VerifyMiddleware where T: SignatureVerify + Clone + 'static, S: Service, Error = actix_web::Error> + 'static, S::Error: 'static, B: MessageBody + 'static, { type Response = ServiceResponse; type Error = actix_web::Error; type Future = LocalBoxFuture<'static, Result>; fn poll_ready(&self, cx: &mut Context) -> Poll> { self.0.poll_ready(cx) } fn call(&self, req: ServiceRequest) -> Self::Future { let span = tracing::info_span!( "Signature Verification", signature.kind = tracing::field::Empty, signature.expected_kind = tracing::field::display(&self.2), ); let authorization = req.headers().get("Authorization").is_some(); let signature = req.headers().get("Signature").is_some(); if authorization { span.record("signature.kind", &tracing::field::display("Authorization")); if self.2.is_authorization() { return self.handle(span, req); } } else if signature { span.record("signature.kind", &tracing::field::display("Signature")); if self.2.is_signature() { return self.handle(span, req); } } else { span.record("signature.kind", &tracing::field::display("None")); } Box::pin(self.0.call(req)) } } impl ResponseError for VerifyError { fn status_code(&self) -> StatusCode { StatusCode::BAD_REQUEST } fn error_response(&self) -> HttpResponse { HttpResponse::new(self.status_code()) } }