Add support for actix-web and http

This commit is contained in:
asonix 2019-09-11 17:05:58 -05:00
parent 55c602bb02
commit 854df4a8d1
8 changed files with 458 additions and 0 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
**/target/
/target
**/*.rs.bk
Cargo.lock

View file

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

View 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 = ".." }

View 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 })
}
}

View 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)
}
}

View 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 = ".." }

View 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 })
}
}

View 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)
}
}