Try async trait, web block on client signing

This commit is contained in:
asonix 2020-03-29 22:21:09 -05:00
parent dbd852156b
commit 20cad6bea8
5 changed files with 100 additions and 34 deletions

View file

@ -27,6 +27,7 @@ required-features = ["sha-2"]
[dependencies] [dependencies]
actix-web = "3.0.0-alpha.1" actix-web = "3.0.0-alpha.1"
actix-http = "2.0.0-alpha.2" actix-http = "2.0.0-alpha.2"
async-trait = "0.1.27"
base64 = { version = "0.11", optional = true } base64 = { version = "0.11", optional = true }
bytes = "0.5.4" bytes = "0.5.4"
chrono = "0.4.6" chrono = "0.4.6"

View file

@ -7,6 +7,7 @@ use actix_http::encoding::Decoder;
use actix_web::{ use actix_web::{
client::{ClientRequest, ClientResponse, SendRequestError}, client::{ClientRequest, ClientResponse, SendRequestError},
dev::Payload, dev::Payload,
error::BlockingError,
http::header::{InvalidHeaderValue, ToStrError}, http::header::{InvalidHeaderValue, ToStrError},
}; };
use std::{fmt::Display, future::Future}; use std::{fmt::Display, future::Future};
@ -43,9 +44,10 @@ pub trait DigestVerify {
/// It generates HTTP Signatures after the Digest header has been added, in order to have /// It generates HTTP Signatures after the Digest header has been added, in order to have
/// verification that the body has not been tampered with, or that the request can't be replayed by /// verification that the body has not been tampered with, or that the request can't be replayed by
/// a malicious entity /// a malicious entity
#[async_trait::async_trait(?Send)]
pub trait SignExt: Sign { pub trait SignExt: Sign {
/// Set the Digest and Authorization headers on the request /// Set the Digest and Authorization headers on the request
fn authorization_signature_with_digest<F, E, K, D, V>( async fn authorization_signature_with_digest<F, E, K, D, V>(
self, self,
config: &Config, config: &Config,
key_id: K, key_id: K,
@ -54,15 +56,20 @@ pub trait SignExt: Sign {
f: F, f: F,
) -> Result<DigestClient<V>, E> ) -> Result<DigestClient<V>, E>
where where
F: FnOnce(&str) -> Result<String, E>, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<ToStrError> + From<InvalidHeaderValue>, E: From<BlockingError<E>>
+ From<ToStrError>
+ From<InvalidHeaderValue>
+ std::fmt::Debug
+ Send
+ 'static,
K: Display, K: Display,
D: DigestCreate, D: DigestCreate,
V: AsRef<[u8]>, V: AsRef<[u8]>,
Self: Sized; Self: Sized;
/// Set the Digest and Signature headers on the request /// Set the Digest and Signature headers on the request
fn signature_with_digest<F, E, K, D, V>( async fn signature_with_digest<F, E, K, D, V>(
self, self,
config: &Config, config: &Config,
key_id: K, key_id: K,
@ -71,8 +78,13 @@ pub trait SignExt: Sign {
f: F, f: F,
) -> Result<DigestClient<V>, E> ) -> Result<DigestClient<V>, E>
where where
F: FnOnce(&str) -> Result<String, E>, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<ToStrError> + From<InvalidHeaderValue>, E: From<BlockingError<E>>
+ From<ToStrError>
+ From<InvalidHeaderValue>
+ std::fmt::Debug
+ Send
+ 'static,
K: Display, K: Display,
D: DigestCreate, D: DigestCreate,
V: AsRef<[u8]>, V: AsRef<[u8]>,

View file

@ -1,5 +1,6 @@
use actix_web::{ use actix_web::{
client::ClientRequest, client::ClientRequest,
error::BlockingError,
http::header::{InvalidHeaderValue, ToStrError}, http::header::{InvalidHeaderValue, ToStrError},
}; };
use std::fmt::Display; use std::fmt::Display;
@ -9,8 +10,9 @@ use crate::{
Config, Sign, Config, Sign,
}; };
#[async_trait::async_trait(?Send)]
impl SignExt for ClientRequest { impl SignExt for ClientRequest {
fn authorization_signature_with_digest<F, E, K, D, V>( async fn authorization_signature_with_digest<F, E, K, D, V>(
self, self,
config: &Config, config: &Config,
key_id: K, key_id: K,
@ -19,8 +21,13 @@ impl SignExt for ClientRequest {
f: F, f: F,
) -> Result<DigestClient<V>, E> ) -> Result<DigestClient<V>, E>
where where
F: FnOnce(&str) -> Result<String, E>, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<ToStrError> + From<InvalidHeaderValue>, E: From<BlockingError<E>>
+ From<ToStrError>
+ From<InvalidHeaderValue>
+ std::fmt::Debug
+ Send
+ 'static,
K: Display, K: Display,
D: DigestCreate, D: DigestCreate,
V: AsRef<[u8]>, V: AsRef<[u8]>,
@ -30,10 +37,11 @@ impl SignExt for ClientRequest {
self.set_header("Digest", format!("{}={}", D::NAME, digest)) self.set_header("Digest", format!("{}={}", D::NAME, digest))
.authorization_signature(config, key_id, f) .authorization_signature(config, key_id, f)
.await
.map(|c| DigestClient::new(c, v)) .map(|c| DigestClient::new(c, v))
} }
fn signature_with_digest<F, E, K, D, V>( async fn signature_with_digest<F, E, K, D, V>(
self, self,
config: &Config, config: &Config,
key_id: K, key_id: K,
@ -42,8 +50,13 @@ impl SignExt for ClientRequest {
f: F, f: F,
) -> Result<DigestClient<V>, E> ) -> Result<DigestClient<V>, E>
where where
F: FnOnce(&str) -> Result<String, E>, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<ToStrError> + From<InvalidHeaderValue>, E: From<BlockingError<E>>
+ From<ToStrError>
+ From<InvalidHeaderValue>
+ std::fmt::Debug
+ Send
+ 'static,
K: Display, K: Display,
D: DigestCreate, D: DigestCreate,
V: AsRef<[u8]>, V: AsRef<[u8]>,
@ -53,6 +66,7 @@ impl SignExt for ClientRequest {
self.set_header("Digest", format!("{}={}", D::NAME, digest)) self.set_header("Digest", format!("{}={}", D::NAME, digest))
.signature(config, key_id, f) .signature(config, key_id, f)
.await
.map(|c| DigestClient::new(c, v)) .map(|c| DigestClient::new(c, v))
} }
} }

View file

@ -146,10 +146,13 @@
//! } //! }
//! ``` //! ```
use actix_web::http::{ use actix_web::{
header::{HeaderMap, InvalidHeaderValue, ToStrError}, error::BlockingError,
uri::PathAndQuery, http::{
Method, header::{HeaderMap, InvalidHeaderValue, ToStrError},
uri::PathAndQuery,
Method,
},
}; };
use chrono::Duration; use chrono::Duration;
use std::{collections::BTreeMap, fmt::Display, future::Future}; use std::{collections::BTreeMap, fmt::Display, future::Future};
@ -213,20 +216,36 @@ pub trait SignatureVerify {
} }
/// A trait implemented by the Actix Web ClientRequest type to add an HTTP signature to the request /// A trait implemented by the Actix Web ClientRequest type to add an HTTP signature to the request
#[async_trait::async_trait(?Send)]
pub trait Sign { pub trait Sign {
/// Add an Authorization Signature to the request /// Add an Authorization Signature to the request
fn authorization_signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E> async fn authorization_signature<F, E, K>(
self,
config: &Config,
key_id: K,
f: F,
) -> Result<Self, E>
where where
F: FnOnce(&str) -> Result<String, E>, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<ToStrError> + From<InvalidHeaderValue>, E: From<BlockingError<E>>
+ From<ToStrError>
+ From<InvalidHeaderValue>
+ std::fmt::Debug
+ Send
+ 'static,
K: Display, K: Display,
Self: Sized; Self: Sized;
/// Add a Signature to the request /// Add a Signature to the request
fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E> async fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E>
where where
F: FnOnce(&str) -> Result<String, E>, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<ToStrError> + From<InvalidHeaderValue>, E: From<BlockingError<E>>
+ From<ToStrError>
+ From<InvalidHeaderValue>
+ std::fmt::Debug
+ Send
+ 'static,
K: Display, K: Display,
Self: Sized; Self: Sized;
} }

View file

@ -1,44 +1,64 @@
use actix_web::{ use actix_web::{
client::ClientRequest, client::ClientRequest,
error::BlockingError,
http::header::{InvalidHeaderValue, ToStrError}, http::header::{InvalidHeaderValue, ToStrError},
web,
}; };
use std::fmt::Display; use std::fmt::Display;
use crate::{create::Signed, Config, Sign}; use crate::{create::Signed, Config, Sign};
#[async_trait::async_trait(?Send)]
impl Sign for ClientRequest { impl Sign for ClientRequest {
fn authorization_signature<F, E, K>( async fn authorization_signature<F, E, K>(
mut self, mut self,
config: &Config, config: &Config,
key_id: K, key_id: K,
f: F, f: F,
) -> Result<Self, E> ) -> Result<Self, E>
where where
F: FnOnce(&str) -> Result<String, E>, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<ToStrError> + From<InvalidHeaderValue>, E: From<BlockingError<E>>
+ From<ToStrError>
+ From<InvalidHeaderValue>
+ std::fmt::Debug
+ Send
+ 'static,
K: Display, K: Display,
Self: Sized,
{ {
let signed = prepare(&self, config, key_id, f)?; let signed = prepare(&self, config, key_id, f).await?;
signed.authorization_header(self.headers_mut())?; signed.authorization_header(self.headers_mut())?;
Ok(self) Ok(self)
} }
fn signature<F, E, K>(mut self, config: &Config, key_id: K, f: F) -> Result<Self, E> async fn signature<F, E, K>(mut self, config: &Config, key_id: K, f: F) -> Result<Self, E>
where where
F: FnOnce(&str) -> Result<String, E>, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<ToStrError> + From<InvalidHeaderValue>, E: From<BlockingError<E>>
+ From<ToStrError>
+ From<InvalidHeaderValue>
+ std::fmt::Debug
+ Send
+ 'static,
K: Display, K: Display,
Self: Sized,
{ {
let signed = prepare(&self, config, key_id, f)?; let signed = prepare(&self, config, key_id, f).await?;
signed.signature_header(self.headers_mut())?; signed.signature_header(self.headers_mut())?;
Ok(self) Ok(self)
} }
} }
fn prepare<F, E, K>(request: &ClientRequest, config: &Config, key_id: K, f: F) -> Result<Signed, E> async fn prepare<F, E, K>(
request: &ClientRequest,
config: &Config,
key_id: K,
f: F,
) -> Result<Signed, E>
where where
F: FnOnce(&str) -> Result<String, E>, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<ToStrError>, E: From<BlockingError<E>> + From<ToStrError> + std::fmt::Debug + Send + 'static,
K: Display, K: Display,
{ {
let unsigned = config.begin_sign( let unsigned = config.begin_sign(
@ -49,7 +69,7 @@ where
let key_id = key_id.to_string(); let key_id = key_id.to_string();
let signed = unsigned.sign(key_id, f)?; let signed = web::block(move || unsigned.sign(key_id, f)).await?;
Ok(signed) Ok(signed)
} }