diff --git a/http-signature-normalization-actix/Cargo.toml b/http-signature-normalization-actix/Cargo.toml index 18e8ae2..83bcffe 100644 --- a/http-signature-normalization-actix/Cargo.toml +++ b/http-signature-normalization-actix/Cargo.toml @@ -8,10 +8,15 @@ readme = "../README.md" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +digest = ["base64", "futures"] [dependencies] actix-web = "1.0" +base64 = { version = "0.10", optional = true } http-signature-normalization = { version = "0.1.0", path = ".." } +futures = { version = "0.1", optional = true } [dev-dependencies] actix = "0.8" diff --git a/http-signature-normalization-actix/src/lib.rs b/http-signature-normalization-actix/src/lib.rs index 820b401..a25cd3c 100644 --- a/http-signature-normalization-actix/src/lib.rs +++ b/http-signature-normalization-actix/src/lib.rs @@ -14,9 +14,17 @@ use std::{ fmt::{self, Display}, }; +#[cfg(feature = "digest")] +use actix_web::{client::ClientResponse, error::PayloadError, web::Bytes}; +#[cfg(feature = "digest")] +use futures::{Future, Stream}; + pub mod prelude { pub use crate::{verify::Unverified, Config, Sign, Verify, VerifyError}; + #[cfg(feature = "digest")] + pub use crate::Digest; + pub use actix_web::http::header::{InvalidHeaderValue, ToStrError}; } @@ -34,6 +42,13 @@ use self::{ verify::Unverified, }; +#[cfg(feature = "digest")] +pub trait Digest { + const NAME: &'static str; + + fn compute(input: &[u8]) -> Vec; +} + pub trait Verify { fn begin_verify(&self, config: &Config) -> Result; } @@ -52,6 +67,38 @@ pub trait Sign { E: From + From, K: Display, Self: Sized; + + #[cfg(feature = "digest")] + fn authorization_signature_with_digest( + self, + config: &Config, + key_id: K, + f: F, + v: V, + ) -> Result, E> + where + F: FnOnce(&str) -> Result, E>, + E: From + From, + K: Display, + D: Digest, + V: AsRef<[u8]>, + Self: Sized; + + #[cfg(feature = "digest")] + fn signature_with_digest( + self, + config: &Config, + key_id: K, + f: F, + v: V, + ) -> Result, E> + where + F: FnOnce(&str) -> Result, E>, + E: From + From, + K: Display, + D: Digest, + V: AsRef<[u8]>, + Self: Sized; } #[derive(Clone, Default)] @@ -59,6 +106,28 @@ pub struct Config { pub config: http_signature_normalization::Config, } +#[cfg(feature = "digest")] +pub struct DigestClient { + req: ClientRequest, + body: V, +} + +#[cfg(feature = "digest")] +impl DigestClient +where + V: AsRef<[u8]>, +{ + pub fn new(req: ClientRequest, body: V) -> Self { + DigestClient { req, body } + } + + pub fn send( + self, + ) -> impl Future>> { + self.req.send_body(self.body.as_ref().to_vec()) + } +} + #[derive(Debug)] pub enum VerifyError { Sig(http_signature_normalization::VerifyError), @@ -158,6 +227,52 @@ impl Sign for ClientRequest { signed.signature_header(self.headers_mut())?; Ok(self) } + + #[cfg(feature = "digest")] + fn authorization_signature_with_digest( + self, + config: &Config, + key_id: K, + f: F, + v: V, + ) -> Result, E> + where + F: FnOnce(&str) -> Result, E>, + E: From + From, + K: Display, + D: Digest, + V: AsRef<[u8]>, + Self: Sized, + { + let digest = base64::encode(D::compute(v.as_ref()).as_slice()); + + self.set_header("Digest", format!("{}={}", D::NAME, digest)) + .authorization_signature(config, key_id, f) + .map(|c| DigestClient::new(c, v)) + } + + #[cfg(feature = "digest")] + fn signature_with_digest( + self, + config: &Config, + key_id: K, + f: F, + v: V, + ) -> Result, E> + where + F: FnOnce(&str) -> Result, E>, + E: From + From, + K: Display, + D: Digest, + V: AsRef<[u8]>, + Self: Sized, + { + let digest = base64::encode(D::compute(v.as_ref()).as_slice()); + + self.set_header("Digest", format!("{}={}", D::NAME, digest)) + .signature(config, key_id, f) + .map(|c| DigestClient::new(c, v)) + } } fn prepare(request: &ClientRequest, config: &Config, key_id: K, f: F) -> Result