mirror of
https://git.asonix.dog/asonix/http-signature-normalization.git
synced 2024-11-22 09:21:00 +00:00
Add support for reqwest-middleware
This commit is contained in:
parent
53be90297d
commit
361d15a126
4 changed files with 186 additions and 36 deletions
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "http-signature-normalization-reqwest"
|
name = "http-signature-normalization-reqwest"
|
||||||
description = "An HTTP Signatures library that leaves the signing to you"
|
description = "An HTTP Signatures library that leaves the signing to you"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
authors = ["asonix <asonix@asonix.dog>"]
|
authors = ["asonix <asonix@asonix.dog>"]
|
||||||
license-file = "LICENSE"
|
license-file = "LICENSE"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -12,6 +12,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[features]
|
[features]
|
||||||
default = ["sha-2", "sha-3"]
|
default = ["sha-2", "sha-3"]
|
||||||
|
middleware = ["reqwest-middleware"]
|
||||||
digest = ["base64", "tokio"]
|
digest = ["base64", "tokio"]
|
||||||
sha-2 = ["digest", "sha2"]
|
sha-2 = ["digest", "sha2"]
|
||||||
sha-3 = ["digest", "sha3"]
|
sha-3 = ["digest", "sha3"]
|
||||||
|
@ -28,12 +29,13 @@ chrono = "0.4.10"
|
||||||
http = "0.2.0"
|
http = "0.2.0"
|
||||||
http-signature-normalization = { version = "0.5.1", path = ".." }
|
http-signature-normalization = { version = "0.5.1", path = ".." }
|
||||||
reqwest = { version = "0.11", default-features = false, features = ["json"] }
|
reqwest = { version = "0.11", default-features = false, features = ["json"] }
|
||||||
|
reqwest-middleware = { version = "0.1.2", optional = true }
|
||||||
sha2 = { version = "0.9", optional = true }
|
sha2 = { version = "0.9", optional = true }
|
||||||
sha3 = { version = "0.9", optional = true }
|
sha3 = { version = "0.9", optional = true }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tokio = { version = "1", default-features = false, features = ["rt"], optional = true }
|
tokio = { version = "1", default-features = false, features = ["rt"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
httpdate = "1.0.2"
|
||||||
pretty_env_logger = "0.4"
|
pretty_env_logger = "0.4"
|
||||||
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros"] }
|
||||||
time = "0.2"
|
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use http_signature_normalization_reqwest::prelude::*;
|
use http_signature_normalization_reqwest::prelude::*;
|
||||||
|
use httpdate::HttpDate;
|
||||||
use reqwest::{header::DATE, Client};
|
use reqwest::{header::DATE, Client};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use time::OffsetDateTime;
|
|
||||||
|
|
||||||
async fn request(config: Config) -> Result<(), Box<dyn std::error::Error>> {
|
async fn request(config: Config) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let digest = Sha256::new();
|
let digest = Sha256::new();
|
||||||
|
|
||||||
let response = Client::new()
|
let client = Client::new();
|
||||||
|
|
||||||
|
let request = client
|
||||||
.post("http://127.0.0.1:8010/")
|
.post("http://127.0.0.1:8010/")
|
||||||
.header("User-Agent", "Reqwest")
|
.header("User-Agent", "Reqwest")
|
||||||
.header("Accept", "text/plain")
|
.header("Accept", "text/plain")
|
||||||
.header(
|
.header(DATE, HttpDate::from(SystemTime::now()).to_string())
|
||||||
DATE,
|
|
||||||
OffsetDateTime::now_utc().format("%a, %d %b %Y %H:%M:%S GMT"),
|
|
||||||
)
|
|
||||||
.signature_with_digest(config, "my-key-id", digest, "Hewwo-owo", |s| {
|
.signature_with_digest(config, "my-key-id", digest, "Hewwo-owo", |s| {
|
||||||
println!("Signing String\n{}", s);
|
println!("Signing String\n{}", s);
|
||||||
Ok(base64::encode(s)) as Result<_, MyError>
|
Ok(base64::encode(s)) as Result<_, MyError>
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let body = response.bytes().await.map_err(MyError::Body)?;
|
let body = client.execute(request).await?.bytes().await?;
|
||||||
|
|
||||||
println!("{:?}", body);
|
println!("{:?}", body);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{Config, Sign, SignError};
|
use crate::{Config, Sign, SignError};
|
||||||
use reqwest::{Body, RequestBuilder};
|
use reqwest::{Body, Request, RequestBuilder};
|
||||||
use std::{fmt::Display, future::Future, pin::Pin};
|
use std::{fmt::Display, future::Future, pin::Pin};
|
||||||
|
|
||||||
#[cfg(feature = "sha-2")]
|
#[cfg(feature = "sha-2")]
|
||||||
|
@ -29,7 +29,7 @@ pub trait SignExt: Sign {
|
||||||
digest: D,
|
digest: D,
|
||||||
v: V,
|
v: V,
|
||||||
f: F,
|
f: F,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<reqwest::Response, E>>>>
|
) -> Pin<Box<dyn Future<Output = Result<Request, E>>>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
||||||
E: From<SignError> + From<reqwest::Error>,
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
|
@ -45,7 +45,7 @@ pub trait SignExt: Sign {
|
||||||
digest: D,
|
digest: D,
|
||||||
v: V,
|
v: V,
|
||||||
f: F,
|
f: F,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<reqwest::Response, E>>>>
|
) -> Pin<Box<dyn Future<Output = Result<Request, E>>>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
||||||
E: From<SignError> + From<reqwest::Error>,
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
|
@ -63,7 +63,7 @@ impl SignExt for RequestBuilder {
|
||||||
mut digest: D,
|
mut digest: D,
|
||||||
v: V,
|
v: V,
|
||||||
f: F,
|
f: F,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<reqwest::Response, E>>>>
|
) -> Pin<Box<dyn Future<Output = Result<Request, E>>>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
||||||
E: From<SignError> + From<reqwest::Error>,
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
|
@ -80,11 +80,13 @@ impl SignExt for RequestBuilder {
|
||||||
.await
|
.await
|
||||||
.map_err(|_| SignError::Canceled)?;
|
.map_err(|_| SignError::Canceled)?;
|
||||||
|
|
||||||
let c = self
|
let mut req = self
|
||||||
.header("Digest", format!("{}={}", D::NAME, digest))
|
.header("Digest", format!("{}={}", D::NAME, digest))
|
||||||
.authorization_signature(&config, key_id, f)?;
|
.authorization_signature(&config, key_id, f)?;
|
||||||
|
|
||||||
c.body(v).send().await.map_err(E::from)
|
*req.body_mut() = Some(Body::from(v.as_ref().to_vec()));
|
||||||
|
|
||||||
|
Ok(req)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +97,7 @@ impl SignExt for RequestBuilder {
|
||||||
mut digest: D,
|
mut digest: D,
|
||||||
v: V,
|
v: V,
|
||||||
f: F,
|
f: F,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<reqwest::Response, E>>>>
|
) -> Pin<Box<dyn Future<Output = Result<Request, E>>>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
||||||
E: From<SignError> + From<reqwest::Error>,
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
|
@ -112,11 +114,91 @@ impl SignExt for RequestBuilder {
|
||||||
.await
|
.await
|
||||||
.map_err(|_| SignError::Canceled)?;
|
.map_err(|_| SignError::Canceled)?;
|
||||||
|
|
||||||
let c = self
|
let mut req = self
|
||||||
.header("Digest", format!("{}={}", D::NAME, digest))
|
.header("Digest", format!("{}={}", D::NAME, digest))
|
||||||
.signature(&config, key_id, f)?;
|
.signature(&config, key_id, f)?;
|
||||||
|
|
||||||
c.body(v).send().await.map_err(E::from)
|
*req.body_mut() = Some(Body::from(v.as_ref().to_vec()));
|
||||||
|
|
||||||
|
Ok(req)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "middleware")]
|
||||||
|
mod middleware {
|
||||||
|
use super::{Config, DigestCreate, Sign, SignError, SignExt};
|
||||||
|
use reqwest::{Body, Request};
|
||||||
|
use reqwest_middleware::RequestBuilder;
|
||||||
|
use std::{fmt::Display, future::Future, pin::Pin};
|
||||||
|
|
||||||
|
impl SignExt for RequestBuilder {
|
||||||
|
fn authorization_signature_with_digest<F, E, K, D, V>(
|
||||||
|
self,
|
||||||
|
config: Config,
|
||||||
|
key_id: K,
|
||||||
|
mut digest: D,
|
||||||
|
v: V,
|
||||||
|
f: F,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<Request, E>>>>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
||||||
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
|
K: Display + 'static,
|
||||||
|
D: DigestCreate + Send + 'static,
|
||||||
|
V: AsRef<[u8]> + Into<Body> + Send + 'static,
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Box::pin(async move {
|
||||||
|
let (v, digest) = tokio::task::spawn_blocking(move || {
|
||||||
|
let digest = digest.compute(v.as_ref());
|
||||||
|
(v, digest)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|_| SignError::Canceled)?;
|
||||||
|
|
||||||
|
let mut req = self
|
||||||
|
.header("Digest", format!("{}={}", D::NAME, digest))
|
||||||
|
.authorization_signature(&config, key_id, f)?;
|
||||||
|
|
||||||
|
*req.body_mut() = Some(Body::from(v.as_ref().to_vec()));
|
||||||
|
|
||||||
|
Ok(req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature_with_digest<F, E, K, D, V>(
|
||||||
|
self,
|
||||||
|
config: Config,
|
||||||
|
key_id: K,
|
||||||
|
mut digest: D,
|
||||||
|
v: V,
|
||||||
|
f: F,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<Request, E>>>>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
|
||||||
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
|
K: Display + 'static,
|
||||||
|
D: DigestCreate + Send + 'static,
|
||||||
|
V: AsRef<[u8]> + Into<Body> + Send + 'static,
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Box::pin(async move {
|
||||||
|
let (v, digest) = tokio::task::spawn_blocking(move || {
|
||||||
|
let digest = digest.compute(v.as_ref());
|
||||||
|
(v, digest)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|_| SignError::Canceled)?;
|
||||||
|
|
||||||
|
let mut req = self
|
||||||
|
.header("Digest", format!("{}={}", D::NAME, digest))
|
||||||
|
.signature(&config, key_id, f)?;
|
||||||
|
|
||||||
|
*req.body_mut() = Some(Body::from(v.as_ref().to_vec()));
|
||||||
|
|
||||||
|
Ok(req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,7 +34,12 @@ pub struct Config {
|
||||||
/// A trait implemented by the reqwest RequestBuilder type to add an HTTP Signature to the request
|
/// A trait implemented by the reqwest RequestBuilder type to add an HTTP Signature to the request
|
||||||
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>
|
fn authorization_signature<F, E, K>(
|
||||||
|
self,
|
||||||
|
config: &Config,
|
||||||
|
key_id: K,
|
||||||
|
f: F,
|
||||||
|
) -> Result<Request, E>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
F: FnOnce(&str) -> Result<String, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
|
@ -42,7 +47,7 @@ pub trait Sign {
|
||||||
K: Display;
|
K: Display;
|
||||||
|
|
||||||
/// 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>
|
fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Request, E>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
F: FnOnce(&str) -> Result<String, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
|
@ -139,38 +144,46 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sign for RequestBuilder {
|
impl Sign for RequestBuilder {
|
||||||
fn authorization_signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
fn authorization_signature<F, E, K>(
|
||||||
|
self,
|
||||||
|
config: &Config,
|
||||||
|
key_id: K,
|
||||||
|
f: F,
|
||||||
|
) -> Result<Request, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<String, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<SignError> + From<reqwest::Error>,
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
K: Display,
|
K: Display,
|
||||||
{
|
{
|
||||||
if let Some(builder) = self.try_clone() {
|
let mut request = self.build()?;
|
||||||
let request = builder.build()?;
|
|
||||||
let signed = prepare(&request, config, key_id, f)?;
|
let signed = prepare(&request, config, key_id, f)?;
|
||||||
|
|
||||||
let auth_header = signed.authorization_header();
|
let auth_header = signed.authorization_header();
|
||||||
return Ok(self.header("Authorization", auth_header));
|
request.headers_mut().insert(
|
||||||
|
"Authorization",
|
||||||
|
auth_header.parse().map_err(SignError::NewHeader)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(SignError::BodyPresent.into())
|
fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Request, E>
|
||||||
}
|
|
||||||
|
|
||||||
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>,
|
||||||
E: From<SignError> + From<reqwest::Error>,
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
K: Display,
|
K: Display,
|
||||||
{
|
{
|
||||||
if let Some(builder) = self.try_clone() {
|
let mut request = self.build()?;
|
||||||
let request = builder.build()?;
|
|
||||||
let signed = prepare(&request, config, key_id, f)?;
|
let signed = prepare(&request, config, key_id, f)?;
|
||||||
|
|
||||||
let sig_header = signed.signature_header();
|
let sig_header = signed.signature_header();
|
||||||
return Ok(self.header("Signature", sig_header));
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(SignError::BodyPresent.into())
|
request.headers_mut().insert(
|
||||||
|
"Signature",
|
||||||
|
sig_header.parse().map_err(SignError::NewHeader)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,3 +227,55 @@ where
|
||||||
let signed = unsigned.sign(key_id.to_string(), f)?;
|
let signed = unsigned.sign(key_id.to_string(), f)?;
|
||||||
Ok(signed)
|
Ok(signed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "middleware")]
|
||||||
|
mod middleware {
|
||||||
|
use super::{prepare, Config, Sign, SignError};
|
||||||
|
use reqwest::Request;
|
||||||
|
use reqwest_middleware::RequestBuilder;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
impl Sign for RequestBuilder {
|
||||||
|
fn authorization_signature<F, E, K>(
|
||||||
|
self,
|
||||||
|
config: &Config,
|
||||||
|
key_id: K,
|
||||||
|
f: F,
|
||||||
|
) -> Result<Request, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
|
K: Display,
|
||||||
|
{
|
||||||
|
let mut request = self.build()?;
|
||||||
|
let signed = prepare(&request, config, key_id, f)?;
|
||||||
|
|
||||||
|
let auth_header = signed.authorization_header();
|
||||||
|
request.headers_mut().insert(
|
||||||
|
"Authorization",
|
||||||
|
auth_header.parse().map_err(SignError::NewHeader)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Request, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
|
E: From<SignError> + From<reqwest::Error>,
|
||||||
|
K: Display,
|
||||||
|
{
|
||||||
|
let mut request = self.build()?;
|
||||||
|
let signed = prepare(&request, config, key_id, f)?;
|
||||||
|
|
||||||
|
let sig_header = signed.signature_header();
|
||||||
|
|
||||||
|
request.headers_mut().insert(
|
||||||
|
"Signature",
|
||||||
|
sig_header.parse().map_err(SignError::NewHeader)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue