Add client digest support

This commit is contained in:
asonix 2019-09-11 18:49:18 -05:00
parent 73ee0c51e6
commit ef2a4e338f
2 changed files with 120 additions and 0 deletions

View file

@ -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"

View file

@ -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<u8>;
}
pub trait Verify {
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError>;
}
@ -52,6 +67,38 @@ pub trait Sign {
E: From<ToStrError> + From<InvalidHeaderValue>,
K: Display,
Self: Sized;
#[cfg(feature = "digest")]
fn authorization_signature_with_digest<F, E, K, D, V>(
self,
config: &Config,
key_id: K,
f: F,
v: V,
) -> Result<DigestClient<V>, E>
where
F: FnOnce(&str) -> Result<Vec<u8>, E>,
E: From<ToStrError> + From<InvalidHeaderValue>,
K: Display,
D: Digest,
V: AsRef<[u8]>,
Self: Sized;
#[cfg(feature = "digest")]
fn signature_with_digest<F, E, K, D, V>(
self,
config: &Config,
key_id: K,
f: F,
v: V,
) -> Result<DigestClient<V>, E>
where
F: FnOnce(&str) -> Result<Vec<u8>, E>,
E: From<ToStrError> + From<InvalidHeaderValue>,
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<V> {
req: ClientRequest,
body: V,
}
#[cfg(feature = "digest")]
impl<V> DigestClient<V>
where
V: AsRef<[u8]>,
{
pub fn new(req: ClientRequest, body: V) -> Self {
DigestClient { req, body }
}
pub fn send(
self,
) -> impl Future<Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>> {
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<F, E, K, D, V>(
self,
config: &Config,
key_id: K,
f: F,
v: V,
) -> Result<DigestClient<V>, E>
where
F: FnOnce(&str) -> Result<Vec<u8>, E>,
E: From<ToStrError> + From<InvalidHeaderValue>,
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<F, E, K, D, V>(
self,
config: &Config,
key_id: K,
f: F,
v: V,
) -> Result<DigestClient<V>, E>
where
F: FnOnce(&str) -> Result<Vec<u8>, E>,
E: From<ToStrError> + From<InvalidHeaderValue>,
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<F, E, K>(request: &ClientRequest, config: &Config, key_id: K, f: F) -> Result<Signed, E>