mirror of
https://git.asonix.dog/asonix/http-signature-normalization.git
synced 2024-11-25 10:51:01 +00:00
Fix header strings
This commit is contained in:
parent
1202b7ff43
commit
421eb3520e
8 changed files with 122 additions and 15 deletions
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "http-signature-normalization"
|
name = "http-signature-normalization"
|
||||||
description = "An HTTP Signatures library that leaves the signing to you"
|
description = "An HTTP Signatures library that leaves the signing to you"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
authors = ["asonix <asonix@asonix.dog>"]
|
authors = ["asonix <asonix@asonix.dog>"]
|
||||||
license-file = "LICENSE"
|
license-file = "LICENSE"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -15,6 +15,7 @@ edition = "2018"
|
||||||
members = [
|
members = [
|
||||||
"http-signature-normalization-actix",
|
"http-signature-normalization-actix",
|
||||||
"http-signature-normalization-http",
|
"http-signature-normalization-http",
|
||||||
|
"http-signature-normalization-reqwest",
|
||||||
"http-signature-normalization-warp",
|
"http-signature-normalization-warp",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "http-signature-normalization-actix"
|
name = "http-signature-normalization-actix"
|
||||||
description = "An HTTP Signatures library that leaves the signing to you"
|
description = "An HTTP Signatures library that leaves the signing to you"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["asonix <asonix@asonix.dog>"]
|
authors = ["asonix <asonix@asonix.dog>"]
|
||||||
license-file = "LICENSE"
|
license-file = "LICENSE"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -29,7 +29,7 @@ actix-web = "1.0"
|
||||||
base64 = { version = "0.10", optional = true }
|
base64 = { version = "0.10", optional = true }
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
http-signature-normalization = { version = "0.1.0", path = ".." }
|
http-signature-normalization = { version = "0.2.0", path = ".." }
|
||||||
sha2 = { version = "0.8", optional = true }
|
sha2 = { version = "0.8", optional = true }
|
||||||
sha3 = { version = "0.8", optional = true }
|
sha3 = { version = "0.8", optional = true }
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "http-signature-normalization-http"
|
name = "http-signature-normalization-http"
|
||||||
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.0"
|
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"
|
||||||
|
@ -13,4 +13,4 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
http = "0.2"
|
http = "0.2"
|
||||||
http-signature-normalization = { version = "0.1.0", path = ".." }
|
http-signature-normalization = { version = "0.2.0", path = ".." }
|
||||||
|
|
13
http-signature-normalization-reqwest/Cargo.toml
Normal file
13
http-signature-normalization-reqwest/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "http-signature-normalization-reqwest"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["asonix <asonix@asonix.dog>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = "0.4.10"
|
||||||
|
http = "0.2.0"
|
||||||
|
http-signature-normalization = { version = "0.2.0", path = ".." }
|
||||||
|
reqwest = "0.10.1"
|
88
http-signature-normalization-reqwest/src/lib.rs
Normal file
88
http-signature-normalization-reqwest/src/lib.rs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
use chrono::Duration;
|
||||||
|
use http_signature_normalization::create::Signed;
|
||||||
|
use reqwest::{
|
||||||
|
header::{InvalidHeaderValue, ToStrError},
|
||||||
|
Request,
|
||||||
|
};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
pub struct Config(http_signature_normalization::Config);
|
||||||
|
|
||||||
|
pub trait Sign {
|
||||||
|
fn authorization_signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display;
|
||||||
|
|
||||||
|
fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new(expires_after: Duration) -> Self {
|
||||||
|
Config(http_signature_normalization::Config { expires_after })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sign for Request {
|
||||||
|
fn authorization_signature<F, E, K>(
|
||||||
|
mut self,
|
||||||
|
config: &Config,
|
||||||
|
key_id: K,
|
||||||
|
f: F,
|
||||||
|
) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display,
|
||||||
|
{
|
||||||
|
let signed = prepare(&self, config, key_id, f)?;
|
||||||
|
|
||||||
|
let auth_header = signed.authorization_header();
|
||||||
|
self.headers_mut()
|
||||||
|
.insert("Authorization", auth_header.parse()?);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature<F, E, K>(mut self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display,
|
||||||
|
{
|
||||||
|
let signed = prepare(&self, config, key_id, f)?;
|
||||||
|
|
||||||
|
let sig_header = signed.signature_header();
|
||||||
|
self.headers_mut().insert("Signature", sig_header.parse()?);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare<F, E, K>(req: &Request, config: &Config, key_id: K, f: F) -> Result<Signed, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
|
K: Display,
|
||||||
|
{
|
||||||
|
let mut bt = std::collections::BTreeMap::new();
|
||||||
|
for (k, v) in req.headers().iter() {
|
||||||
|
bt.insert(k.as_str().to_owned(), v.to_str()?.to_owned());
|
||||||
|
}
|
||||||
|
let path_and_query = if let Some(query) = req.url().query() {
|
||||||
|
format!("{}?{}", req.url().path(), query)
|
||||||
|
} else {
|
||||||
|
req.url().path().to_string()
|
||||||
|
};
|
||||||
|
let unsigned = config
|
||||||
|
.0
|
||||||
|
.begin_sign(req.method().as_str(), &path_and_query, bt);
|
||||||
|
|
||||||
|
let signed = unsigned.sign(key_id.to_string(), f)?;
|
||||||
|
Ok(signed)
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ base64 = { version = "0.11.0", optional = true }
|
||||||
bytes = "0.5.3"
|
bytes = "0.5.3"
|
||||||
futures = "0.3.1"
|
futures = "0.3.1"
|
||||||
http = "0.2.0"
|
http = "0.2.0"
|
||||||
http-signature-normalization-http = { version = "0.2.0", path = "../http-signature-normalization-http" }
|
http-signature-normalization-http = { version = "0.3.0", path = "../http-signature-normalization-http" }
|
||||||
serde = { version = "1.0.104", features = ["derive"], optional = true }
|
serde = { version = "1.0.104", features = ["derive"], optional = true }
|
||||||
serde_json = { version = "1.0.44", optional = true }
|
serde_json = { version = "1.0.44", optional = true }
|
||||||
serde_urlencoded = { version = "0.6.1", optional = true }
|
serde_urlencoded = { version = "0.6.1", optional = true }
|
||||||
|
|
|
@ -38,9 +38,8 @@ pub struct ParseBodyError;
|
||||||
pub fn verify_bytes(
|
pub fn verify_bytes(
|
||||||
verifier: impl DigestVerify + Clone + Send,
|
verifier: impl DigestVerify + Clone + Send,
|
||||||
) -> impl Filter<Extract = (Bytes,), Error = Rejection> + Clone {
|
) -> impl Filter<Extract = (Bytes,), Error = Rejection> + Clone {
|
||||||
parse_digest_header()
|
parse_digest_header().and(body::bytes()).and_then(
|
||||||
.and(body::bytes())
|
move |parts: Vec<DigestPart>, bytes: Bytes| {
|
||||||
.and_then(move |parts: Vec<DigestPart>, bytes: Bytes| {
|
|
||||||
let mut verifier = verifier.clone();
|
let mut verifier = verifier.clone();
|
||||||
async move {
|
async move {
|
||||||
if verifier.verify(&parts, &bytes) {
|
if verifier.verify(&parts, &bytes) {
|
||||||
|
@ -49,11 +48,12 @@ pub fn verify_bytes(
|
||||||
Err(warp::reject::custom(VerifyError))
|
Err(warp::reject::custom(VerifyError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_json<T>(
|
pub fn verify_json<T>(
|
||||||
verifier: impl DigestVerify + Clone + Send
|
verifier: impl DigestVerify + Clone + Send,
|
||||||
) -> impl Filter<Extract = (T,), Error = Rejection> + Clone
|
) -> impl Filter<Extract = (T,), Error = Rejection> + Clone
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
||||||
|
@ -117,7 +117,9 @@ where
|
||||||
V: DigestVerify,
|
V: DigestVerify,
|
||||||
{
|
{
|
||||||
fn verify(&mut self, parts: &[DigestPart], payload: &[u8]) -> bool {
|
fn verify(&mut self, parts: &[DigestPart], payload: &[u8]) -> bool {
|
||||||
self.0.verify(parts, payload) || self.1.verify(parts, payload) || self.2.verify(parts, payload)
|
self.0.verify(parts, payload)
|
||||||
|
|| self.1.verify(parts, payload)
|
||||||
|
|| self.2.verify(parts, payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +131,10 @@ where
|
||||||
W: DigestVerify,
|
W: DigestVerify,
|
||||||
{
|
{
|
||||||
fn verify(&mut self, parts: &[DigestPart], payload: &[u8]) -> bool {
|
fn verify(&mut self, parts: &[DigestPart], payload: &[u8]) -> bool {
|
||||||
self.0.verify(parts, payload) || self.1.verify(parts, payload) || self.2.verify(parts, payload) || self.3.verify(parts, payload)
|
self.0.verify(parts, payload)
|
||||||
|
|| self.1.verify(parts, payload)
|
||||||
|
|| self.2.verify(parts, payload)
|
||||||
|
|| self.3.verify(parts, payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,14 +35,14 @@ impl Signed {
|
||||||
///
|
///
|
||||||
/// Done manually, it would look like `format!("Signature: {}", signed.signature_header())`
|
/// Done manually, it would look like `format!("Signature: {}", signed.signature_header())`
|
||||||
pub fn signature_header(self) -> String {
|
pub fn signature_header(self) -> String {
|
||||||
format!("Signature {}", self.into_header())
|
self.into_header()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn the Signed type into a String that can be used as the Authorization Header
|
/// Turn the Signed type into a String that can be used as the Authorization Header
|
||||||
///
|
///
|
||||||
/// Done manually, it would look like `format!("Authorization: {}", signed.authorization_header())`
|
/// Done manually, it would look like `format!("Authorization: {}", signed.authorization_header())`
|
||||||
pub fn authorization_header(self) -> String {
|
pub fn authorization_header(self) -> String {
|
||||||
self.into_header()
|
format!("Signature {}", self.into_header())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_header(self) -> String {
|
fn into_header(self) -> String {
|
||||||
|
|
Loading…
Reference in a new issue