mirror of
https://git.asonix.dog/asonix/http-signature-normalization.git
synced 2024-11-21 17:00:59 +00:00
Add support for actix-web and http
This commit is contained in:
parent
55c602bb02
commit
854df4a8d1
8 changed files with 458 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
**/target/
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
|
|
|
@ -9,6 +9,12 @@ edition = "2018"
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"http-signature-normalization-http",
|
||||
"http-signature-normalization-actix",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.10"
|
||||
chrono = "0.4"
|
||||
|
|
14
http-signature-normalization-actix/Cargo.toml
Normal file
14
http-signature-normalization-actix/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "http-signature-normalization-actix"
|
||||
description = "An HTTP Signatures library that leaves the signing to you"
|
||||
version = "0.1.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
license-file = "../LICENSE"
|
||||
readme = "../README.md"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix-web = "1.0"
|
||||
http-signature-normalization = { version = "0.1.0", path = ".." }
|
40
http-signature-normalization-actix/src/create.rs
Normal file
40
http-signature-normalization-actix/src/create.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use actix_web::http::header::{
|
||||
HeaderMap, HeaderName, HeaderValue, InvalidHeaderValue, AUTHORIZATION,
|
||||
};
|
||||
|
||||
pub struct Signed {
|
||||
pub signed: http_signature_normalization::create::Signed,
|
||||
}
|
||||
|
||||
pub struct Unsigned {
|
||||
pub unsigned: http_signature_normalization::create::Unsigned,
|
||||
}
|
||||
|
||||
impl Signed {
|
||||
pub fn signature_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
||||
hm.insert(
|
||||
HeaderName::from_static("Signature"),
|
||||
HeaderValue::from_str(&self.signed.signature_header())?,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn authorization_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
||||
hm.insert(
|
||||
AUTHORIZATION,
|
||||
HeaderValue::from_str(&self.signed.authorization_header())?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Unsigned {
|
||||
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
{
|
||||
let signed = self.unsigned.sign(key_id, f)?;
|
||||
Ok(Signed { signed })
|
||||
}
|
||||
}
|
217
http-signature-normalization-actix/src/lib.rs
Normal file
217
http-signature-normalization-actix/src/lib.rs
Normal file
|
@ -0,0 +1,217 @@
|
|||
use actix_web::{
|
||||
client::ClientRequest,
|
||||
dev::ServiceRequest,
|
||||
http::{
|
||||
header::{HeaderMap, InvalidHeaderValue, ToStrError},
|
||||
uri::PathAndQuery,
|
||||
Method,
|
||||
},
|
||||
HttpRequest,
|
||||
};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
error::Error,
|
||||
fmt::{self, Display},
|
||||
};
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{verify::Unverified, Config, Sign, Verify, VerifyError};
|
||||
|
||||
pub use actix_web::http::header::{InvalidHeaderValue, ToStrError};
|
||||
}
|
||||
|
||||
pub mod create;
|
||||
|
||||
pub mod verify {
|
||||
pub use http_signature_normalization::verify::{
|
||||
Algorithm, DeprecatedAlgorithm, ParseSignatureError, ParsedHeader, Unvalidated, Unverified,
|
||||
ValidateError,
|
||||
};
|
||||
}
|
||||
|
||||
use self::{
|
||||
create::{Signed, Unsigned},
|
||||
verify::Unverified,
|
||||
};
|
||||
|
||||
pub trait Verify {
|
||||
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError>;
|
||||
}
|
||||
|
||||
pub trait Sign {
|
||||
fn authorization_signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
Self: Sized;
|
||||
|
||||
fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Config {
|
||||
pub config: http_signature_normalization::Config,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VerifyError {
|
||||
Sig(http_signature_normalization::VerifyError),
|
||||
Header(ToStrError),
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn begin_sign(
|
||||
&self,
|
||||
method: &Method,
|
||||
path_and_query: Option<&PathAndQuery>,
|
||||
headers: HeaderMap,
|
||||
) -> Result<Unsigned, ToStrError> {
|
||||
let headers = headers
|
||||
.iter()
|
||||
.map(|(k, v)| v.to_str().map(|v| (k.to_string(), v.to_string())))
|
||||
.collect::<Result<BTreeMap<_, _>, ToStrError>>()?;
|
||||
|
||||
let path_and_query = path_and_query
|
||||
.map(|p| p.to_string())
|
||||
.unwrap_or(String::from("/"));
|
||||
|
||||
let unsigned = self
|
||||
.config
|
||||
.begin_sign(&method.to_string(), &path_and_query, headers);
|
||||
|
||||
Ok(Unsigned { unsigned })
|
||||
}
|
||||
|
||||
pub fn begin_verify(
|
||||
&self,
|
||||
method: &Method,
|
||||
path_and_query: Option<&PathAndQuery>,
|
||||
headers: HeaderMap,
|
||||
) -> Result<Unverified, VerifyError> {
|
||||
let headers = headers
|
||||
.iter()
|
||||
.map(|(k, v)| v.to_str().map(|v| (k.to_string(), v.to_string())))
|
||||
.collect::<Result<BTreeMap<_, _>, ToStrError>>()?;
|
||||
|
||||
let path_and_query = path_and_query
|
||||
.map(|p| p.to_string())
|
||||
.unwrap_or(String::from("/"));
|
||||
|
||||
let unverified = self
|
||||
.config
|
||||
.begin_verify(&method.to_string(), &path_and_query, headers)?;
|
||||
|
||||
Ok(unverified)
|
||||
}
|
||||
}
|
||||
|
||||
impl Verify for HttpRequest {
|
||||
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError> {
|
||||
config.begin_verify(
|
||||
self.method(),
|
||||
self.uri().path_and_query(),
|
||||
self.headers().clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Verify for ServiceRequest {
|
||||
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError> {
|
||||
config.begin_verify(
|
||||
self.method(),
|
||||
self.uri().path_and_query(),
|
||||
self.headers().clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sign for ClientRequest {
|
||||
fn authorization_signature<F, E, K>(
|
||||
mut self,
|
||||
config: &Config,
|
||||
key_id: K,
|
||||
f: F,
|
||||
) -> Result<Self, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
{
|
||||
let signed = prepare(&self, config, key_id, f)?;
|
||||
signed.authorization_header(self.headers_mut())?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn signature<F, E, K>(mut self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||
K: Display,
|
||||
{
|
||||
let signed = prepare(&self, config, key_id, f)?;
|
||||
signed.signature_header(self.headers_mut())?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare<F, E, K>(request: &ClientRequest, config: &Config, key_id: K, f: F) -> Result<Signed, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
E: From<ToStrError>,
|
||||
K: Display,
|
||||
{
|
||||
let unsigned = config.begin_sign(
|
||||
request.get_method(),
|
||||
request.get_uri().path_and_query(),
|
||||
request.headers().clone(),
|
||||
)?;
|
||||
|
||||
let key_id = key_id.to_string();
|
||||
|
||||
let signed = unsigned.sign(key_id, f)?;
|
||||
|
||||
Ok(signed)
|
||||
}
|
||||
|
||||
impl fmt::Display for VerifyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
VerifyError::Sig(ref e) => write!(f, "Sig error, {}", e),
|
||||
VerifyError::Header(ref e) => write!(f, "Header error, {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for VerifyError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
VerifyError::Sig(ref e) => e.description(),
|
||||
VerifyError::Header(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match *self {
|
||||
VerifyError::Sig(ref e) => Some(e),
|
||||
VerifyError::Header(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<http_signature_normalization::VerifyError> for VerifyError {
|
||||
fn from(e: http_signature_normalization::VerifyError) -> Self {
|
||||
VerifyError::Sig(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ToStrError> for VerifyError {
|
||||
fn from(e: ToStrError) -> Self {
|
||||
VerifyError::Header(e)
|
||||
}
|
||||
}
|
14
http-signature-normalization-http/Cargo.toml
Normal file
14
http-signature-normalization-http/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "http-signature-normalization-http"
|
||||
description = "An HTTP Signatures library that leaves the signing to you"
|
||||
version = "0.1.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
license-file = "../LICENSE"
|
||||
readme = "../README.md"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
http = "0.1"
|
||||
http-signature-normalization = { version = "0.1.0", path = ".." }
|
38
http-signature-normalization-http/src/create.rs
Normal file
38
http-signature-normalization-http/src/create.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
use http::header::{HeaderMap, HeaderName, HeaderValue, InvalidHeaderValue, AUTHORIZATION};
|
||||
|
||||
pub struct Signed {
|
||||
pub signed: http_signature_normalization::create::Signed,
|
||||
}
|
||||
|
||||
pub struct Unsigned {
|
||||
pub unsigned: http_signature_normalization::create::Unsigned,
|
||||
}
|
||||
|
||||
impl Signed {
|
||||
pub fn signature_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
||||
hm.insert(
|
||||
HeaderName::from_static("Signature"),
|
||||
HeaderValue::from_str(&self.signed.signature_header())?,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn authorization_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
||||
hm.insert(
|
||||
AUTHORIZATION,
|
||||
HeaderValue::from_str(&self.signed.authorization_header())?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Unsigned {
|
||||
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
|
||||
where
|
||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
||||
{
|
||||
let signed = self.unsigned.sign(key_id, f)?;
|
||||
Ok(Signed { signed })
|
||||
}
|
||||
}
|
128
http-signature-normalization-http/src/lib.rs
Normal file
128
http-signature-normalization-http/src/lib.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
use http::{
|
||||
header::{HeaderMap, ToStrError},
|
||||
method::Method,
|
||||
uri::PathAndQuery,
|
||||
};
|
||||
use std::{collections::BTreeMap, error::Error, fmt};
|
||||
|
||||
use self::{create::Unsigned, verify::Unverified};
|
||||
|
||||
pub mod prelude {
|
||||
pub use http::{
|
||||
header::{HeaderMap, InvalidHeaderValue, ToStrError},
|
||||
method::Method,
|
||||
uri::PathAndQuery,
|
||||
};
|
||||
|
||||
pub use crate::create::{Signed, Unsigned};
|
||||
|
||||
pub use crate::verify::{
|
||||
Algorithm, DeprecatedAlgorithm, ParseSignatureError, ParsedHeader, Unvalidated, Unverified,
|
||||
ValidateError,
|
||||
};
|
||||
|
||||
pub use crate::{Config, VerifyError};
|
||||
}
|
||||
|
||||
pub mod create;
|
||||
|
||||
pub mod verify {
|
||||
pub use http_signature_normalization::verify::{
|
||||
Algorithm, DeprecatedAlgorithm, ParseSignatureError, ParsedHeader, Unvalidated, Unverified,
|
||||
ValidateError,
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Config {
|
||||
pub config: http_signature_normalization::Config,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VerifyError {
|
||||
Sig(http_signature_normalization::VerifyError),
|
||||
Header(ToStrError),
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn begin_sign(
|
||||
&self,
|
||||
method: &Method,
|
||||
path_and_query: Option<&PathAndQuery>,
|
||||
headers: HeaderMap,
|
||||
) -> Result<Unsigned, ToStrError> {
|
||||
let headers = headers
|
||||
.iter()
|
||||
.map(|(k, v)| v.to_str().map(|v| (k.to_string(), v.to_string())))
|
||||
.collect::<Result<BTreeMap<_, _>, ToStrError>>()?;
|
||||
|
||||
let path_and_query = path_and_query
|
||||
.map(|p| p.to_string())
|
||||
.unwrap_or(String::from("/"));
|
||||
|
||||
let unsigned = self
|
||||
.config
|
||||
.begin_sign(&method.to_string(), &path_and_query, headers);
|
||||
|
||||
Ok(Unsigned { unsigned })
|
||||
}
|
||||
|
||||
pub fn begin_verify(
|
||||
&self,
|
||||
method: &Method,
|
||||
path_and_query: Option<&PathAndQuery>,
|
||||
headers: HeaderMap,
|
||||
) -> Result<Unverified, VerifyError> {
|
||||
let headers = headers
|
||||
.iter()
|
||||
.map(|(k, v)| v.to_str().map(|v| (k.to_string(), v.to_string())))
|
||||
.collect::<Result<BTreeMap<_, _>, ToStrError>>()?;
|
||||
|
||||
let path_and_query = path_and_query
|
||||
.map(|p| p.to_string())
|
||||
.unwrap_or(String::from("/"));
|
||||
|
||||
let unverified = self
|
||||
.config
|
||||
.begin_verify(&method.to_string(), &path_and_query, headers)?;
|
||||
|
||||
Ok(unverified)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VerifyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
VerifyError::Sig(ref e) => write!(f, "Sig error, {}", e),
|
||||
VerifyError::Header(ref e) => write!(f, "Header error, {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for VerifyError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
VerifyError::Sig(ref e) => e.description(),
|
||||
VerifyError::Header(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match *self {
|
||||
VerifyError::Sig(ref e) => Some(e),
|
||||
VerifyError::Header(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<http_signature_normalization::VerifyError> for VerifyError {
|
||||
fn from(e: http_signature_normalization::VerifyError) -> Self {
|
||||
VerifyError::Sig(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ToStrError> for VerifyError {
|
||||
fn from(e: ToStrError) -> Self {
|
||||
VerifyError::Header(e)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue