mirror of
https://git.asonix.dog/asonix/http-signature-normalization.git
synced 2024-11-25 10:51:01 +00:00
Add Http Signature verification middleware and...
Move base64 out of base lib
This commit is contained in:
parent
c468513d5a
commit
686e52213e
14 changed files with 276 additions and 59 deletions
|
@ -16,5 +16,4 @@ members = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.10"
|
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
|
|
@ -10,7 +10,7 @@ edition = "2018"
|
||||||
# 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 = []
|
default = []
|
||||||
digest = ["base64", "futures"]
|
digest = ["base64"]
|
||||||
sha-2 = ["digest", "sha2"]
|
sha-2 = ["digest", "sha2"]
|
||||||
sha-3 = ["digest", "sha3"]
|
sha-3 = ["digest", "sha3"]
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ required-features = ["sha-2"]
|
||||||
actix-web = "1.0"
|
actix-web = "1.0"
|
||||||
base64 = { version = "0.10", optional = true }
|
base64 = { version = "0.10", optional = true }
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
futures = { version = "0.1", optional = true }
|
futures = "0.1"
|
||||||
http-signature-normalization = { version = "0.1.0", path = ".." }
|
http-signature-normalization = { version = "0.1.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 }
|
||||||
|
|
|
@ -18,8 +18,8 @@ fn main() {
|
||||||
&config,
|
&config,
|
||||||
"my-key-id",
|
"my-key-id",
|
||||||
&mut digest,
|
&mut digest,
|
||||||
"Hewwo owo",
|
"Hewwo-owo",
|
||||||
|s| Ok(s.as_bytes().to_vec()),
|
|s| Ok(base64::encode(s)),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.send()
|
.send()
|
||||||
|
|
|
@ -1,33 +1,47 @@
|
||||||
use actix::System;
|
use actix::System;
|
||||||
use actix_web::{web, App, HttpRequest, HttpServer, ResponseError};
|
use actix_web::{web, App, HttpResponse, HttpServer, ResponseError};
|
||||||
use failure::Fail;
|
use failure::Fail;
|
||||||
use http_signature_normalization_actix::{prelude::*, verify::Algorithm};
|
use http_signature_normalization_actix::{prelude::*, verify::Algorithm};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
fn index((req, config): (HttpRequest, web::Data<Config>)) -> Result<&'static str, MyError> {
|
#[derive(Clone, Debug)]
|
||||||
let unverified = req.begin_verify(&config)?;
|
struct MyVerify;
|
||||||
|
|
||||||
if let Some(a) = unverified.algorithm() {
|
impl SignatureVerify for MyVerify {
|
||||||
match *a {
|
type Error = MyError;
|
||||||
Algorithm::Hs2019 => (),
|
type Future = Result<bool, Self::Error>;
|
||||||
|
|
||||||
|
fn signature_verify(
|
||||||
|
&mut self,
|
||||||
|
algorithm: Option<Algorithm>,
|
||||||
|
_key_id: &str,
|
||||||
|
signature: &str,
|
||||||
|
signing_string: &str,
|
||||||
|
) -> Self::Future {
|
||||||
|
match algorithm {
|
||||||
|
Some(Algorithm::Hs2019) => (),
|
||||||
_ => return Err(MyError::Algorithm),
|
_ => return Err(MyError::Algorithm),
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if unverified.verify(|bytes, string| bytes == string.as_bytes()) {
|
let decoded = base64::decode(signature).map_err(|_| MyError::Decode)?;
|
||||||
Ok("Eyyyyup")
|
|
||||||
} else {
|
Ok(decoded == signing_string.as_bytes())
|
||||||
Ok("Nope")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn index() -> &'static str {
|
||||||
|
"Eyyyyup"
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let sys = System::new("server-example");
|
let sys = System::new("server-example");
|
||||||
|
|
||||||
|
let config = Config::default();
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.data(Config::default())
|
|
||||||
.wrap(VerifyDigest::new(Sha256::new()))
|
.wrap(VerifyDigest::new(Sha256::new()))
|
||||||
|
.wrap(VerifySignature::new(MyVerify, config.clone()).authorization())
|
||||||
.route("/", web::post().to(index))
|
.route("/", web::post().to(index))
|
||||||
})
|
})
|
||||||
.bind("127.0.0.1:8010")?
|
.bind("127.0.0.1:8010")?
|
||||||
|
@ -44,10 +58,19 @@ enum MyError {
|
||||||
|
|
||||||
#[fail(display = "Unsupported algorithm")]
|
#[fail(display = "Unsupported algorithm")]
|
||||||
Algorithm,
|
Algorithm,
|
||||||
|
|
||||||
|
#[fail(display = "Couldn't decode signature")]
|
||||||
|
Decode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for MyError {
|
impl ResponseError for MyError {
|
||||||
// default 500
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
HttpResponse::BadRequest().finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_response(&self) -> HttpResponse {
|
||||||
|
self.error_response()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<VerifyError> for MyError {
|
impl From<VerifyError> for MyError {
|
||||||
|
|
|
@ -12,19 +12,18 @@ pub struct Unsigned {
|
||||||
|
|
||||||
impl Signed {
|
impl Signed {
|
||||||
pub fn signature_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
pub fn signature_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
||||||
|
let sig_header = self.signed.signature_header();
|
||||||
hm.insert(
|
hm.insert(
|
||||||
HeaderName::from_static("Signature"),
|
HeaderName::from_static("Signature"),
|
||||||
HeaderValue::from_str(&self.signed.signature_header())?,
|
HeaderValue::from_str(&sig_header)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authorization_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
pub fn authorization_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> {
|
||||||
hm.insert(
|
let auth_header = self.signed.authorization_header();
|
||||||
AUTHORIZATION,
|
hm.insert(AUTHORIZATION, HeaderValue::from_str(&auth_header)?);
|
||||||
HeaderValue::from_str(&self.signed.authorization_header())?,
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +31,7 @@ impl Signed {
|
||||||
impl Unsigned {
|
impl Unsigned {
|
||||||
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
|
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
{
|
{
|
||||||
let signed = self.unsigned.sign(key_id, f)?;
|
let signed = self.unsigned.sign(key_id, f)?;
|
||||||
Ok(Signed { signed })
|
Ok(Signed { signed })
|
||||||
|
|
|
@ -81,7 +81,6 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
||||||
let mut verify_digest = self.2.clone();
|
|
||||||
if let Some(digest) = req.headers().get("Digest") {
|
if let Some(digest) = req.headers().get("Digest") {
|
||||||
let vec = match parse_digest(digest) {
|
let vec = match parse_digest(digest) {
|
||||||
Some(vec) => vec,
|
Some(vec) => vec,
|
||||||
|
@ -89,6 +88,7 @@ where
|
||||||
};
|
};
|
||||||
let payload = req.take_payload();
|
let payload = req.take_payload();
|
||||||
let service = self.0.clone();
|
let service = self.0.clone();
|
||||||
|
let mut verify_digest = self.2.clone();
|
||||||
|
|
||||||
Box::new(payload.concat2().from_err().and_then(move |bytes| {
|
Box::new(payload.concat2().from_err().and_then(move |bytes| {
|
||||||
if verify_digest.verify(&vec, &bytes.as_ref()) {
|
if verify_digest.verify(&vec, &bytes.as_ref()) {
|
||||||
|
@ -103,12 +103,10 @@ where
|
||||||
Either::B(err(VerifyError.into()))
|
Either::B(err(VerifyError.into()))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
} else if self.1 {
|
||||||
|
Box::new(err(VerifyError.into()))
|
||||||
} else {
|
} else {
|
||||||
if self.1 {
|
Box::new(self.0.borrow_mut().call(req))
|
||||||
Box::new(err(VerifyError.into()))
|
|
||||||
} else {
|
|
||||||
Box::new(self.0.borrow_mut().call(req))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ pub trait SignExt: Sign {
|
||||||
f: F,
|
f: F,
|
||||||
) -> Result<DigestClient<V>, E>
|
) -> Result<DigestClient<V>, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
K: Display,
|
K: Display,
|
||||||
D: DigestCreate,
|
D: DigestCreate,
|
||||||
|
@ -53,7 +53,7 @@ pub trait SignExt: Sign {
|
||||||
f: F,
|
f: F,
|
||||||
) -> Result<DigestClient<V>, E>
|
) -> Result<DigestClient<V>, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
K: Display,
|
K: Display,
|
||||||
D: DigestCreate,
|
D: DigestCreate,
|
||||||
|
|
|
@ -19,7 +19,7 @@ impl SignExt for ClientRequest {
|
||||||
f: F,
|
f: F,
|
||||||
) -> Result<DigestClient<V>, E>
|
) -> Result<DigestClient<V>, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
K: Display,
|
K: Display,
|
||||||
D: DigestCreate,
|
D: DigestCreate,
|
||||||
|
@ -42,7 +42,7 @@ impl SignExt for ClientRequest {
|
||||||
f: F,
|
f: F,
|
||||||
) -> Result<DigestClient<V>, E>
|
) -> Result<DigestClient<V>, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
K: Display,
|
K: Display,
|
||||||
D: DigestCreate,
|
D: DigestCreate,
|
||||||
|
|
|
@ -4,6 +4,7 @@ use actix_web::http::{
|
||||||
Method,
|
Method,
|
||||||
};
|
};
|
||||||
use failure::Fail;
|
use failure::Fail;
|
||||||
|
use futures::future::IntoFuture;
|
||||||
use std::{collections::BTreeMap, fmt::Display};
|
use std::{collections::BTreeMap, fmt::Display};
|
||||||
|
|
||||||
mod sign;
|
mod sign;
|
||||||
|
@ -11,9 +12,13 @@ mod sign;
|
||||||
#[cfg(feature = "digest")]
|
#[cfg(feature = "digest")]
|
||||||
pub mod digest;
|
pub mod digest;
|
||||||
|
|
||||||
pub mod verify;
|
pub mod create;
|
||||||
|
pub mod middleware;
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{verify::Unverified, Config, Sign, Verify, VerifyError};
|
pub use crate::{
|
||||||
|
middleware::VerifySignature, verify::Unverified, Config, Sign, SignatureVerify, Verify,
|
||||||
|
VerifyError,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "digest")]
|
#[cfg(feature = "digest")]
|
||||||
pub use crate::digest::{
|
pub use crate::digest::{
|
||||||
|
@ -22,32 +27,47 @@ pub mod prelude {
|
||||||
|
|
||||||
pub use actix_web::http::header::{InvalidHeaderValue, ToStrError};
|
pub use actix_web::http::header::{InvalidHeaderValue, ToStrError};
|
||||||
}
|
}
|
||||||
|
pub mod verify;
|
||||||
|
|
||||||
pub mod create;
|
use self::{
|
||||||
|
create::Unsigned,
|
||||||
use self::{create::Unsigned, verify::Unverified};
|
verify::{Algorithm, Unverified},
|
||||||
|
};
|
||||||
|
|
||||||
pub trait Verify {
|
pub trait Verify {
|
||||||
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError>;
|
fn begin_verify(&self, config: &Config) -> Result<Unverified, VerifyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait SignatureVerify {
|
||||||
|
type Error: actix_web::ResponseError;
|
||||||
|
type Future: IntoFuture<Item = bool, Error = Self::Error>;
|
||||||
|
|
||||||
|
fn signature_verify(
|
||||||
|
&mut self,
|
||||||
|
algorithm: Option<Algorithm>,
|
||||||
|
key_id: &str,
|
||||||
|
signature: &str,
|
||||||
|
signing_string: &str,
|
||||||
|
) -> Self::Future;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Sign {
|
pub trait Sign {
|
||||||
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<Self, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
K: Display,
|
K: Display,
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
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<Self, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
K: Display,
|
K: Display,
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub config: http_signature_normalization::Config,
|
pub config: http_signature_normalization::Config,
|
||||||
}
|
}
|
||||||
|
|
183
http-signature-normalization-actix/src/middleware.rs
Normal file
183
http-signature-normalization-actix/src/middleware.rs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
use actix_web::{
|
||||||
|
body::Body,
|
||||||
|
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
||||||
|
HttpResponse, ResponseError,
|
||||||
|
};
|
||||||
|
use failure::Fail;
|
||||||
|
use futures::{
|
||||||
|
future::{err, ok, Either, FutureResult, IntoFuture},
|
||||||
|
Future, Poll,
|
||||||
|
};
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{Config, SignatureVerify};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct VerifySignature<T>(T, Config, HeaderKind, bool);
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct VerifyMiddleware<T, S>(Rc<RefCell<S>>, Config, HeaderKind, bool, T);
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub enum HeaderKind {
|
||||||
|
Authorization,
|
||||||
|
Signature,
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug, Fail)]
|
||||||
|
#[fail(display = "Failed to verify http signature")]
|
||||||
|
pub struct VerifyError;
|
||||||
|
|
||||||
|
impl<T> VerifySignature<T>
|
||||||
|
where
|
||||||
|
T: SignatureVerify,
|
||||||
|
{
|
||||||
|
pub fn new(verify_signature: T, config: Config) -> Self {
|
||||||
|
VerifySignature(verify_signature, config, HeaderKind::Signature, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn authorization(self) -> Self {
|
||||||
|
VerifySignature(self.0, self.1, HeaderKind::Authorization, self.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optional(self) -> Self {
|
||||||
|
VerifySignature(self.0, self.1, self.2, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S> VerifyMiddleware<T, S>
|
||||||
|
where
|
||||||
|
T: SignatureVerify + 'static,
|
||||||
|
T::Future: 'static,
|
||||||
|
S: Service<
|
||||||
|
Request = ServiceRequest,
|
||||||
|
Response = ServiceResponse<Body>,
|
||||||
|
Error = actix_web::Error,
|
||||||
|
> + 'static,
|
||||||
|
S::Error: 'static,
|
||||||
|
{
|
||||||
|
fn handle(
|
||||||
|
&mut self,
|
||||||
|
req: ServiceRequest,
|
||||||
|
) -> Box<dyn Future<Item = ServiceResponse<Body>, Error = actix_web::Error>> {
|
||||||
|
let res = self.1.begin_verify(
|
||||||
|
req.method(),
|
||||||
|
req.uri().path_and_query(),
|
||||||
|
req.headers().clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let unverified = match res {
|
||||||
|
Ok(unverified) => unverified,
|
||||||
|
Err(_) => return Box::new(err(VerifyError.into())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let algorithm = unverified.algorithm().map(|a| a.clone());
|
||||||
|
let key_id = unverified.key_id().to_owned();
|
||||||
|
|
||||||
|
let verified = unverified.verify(|signature, signing_string| {
|
||||||
|
self.4
|
||||||
|
.signature_verify(algorithm, &key_id, signature, signing_string)
|
||||||
|
});
|
||||||
|
|
||||||
|
let service = self.0.clone();
|
||||||
|
|
||||||
|
Box::new(
|
||||||
|
verified
|
||||||
|
.into_future()
|
||||||
|
.from_err::<actix_web::Error>()
|
||||||
|
.and_then(move |verified| {
|
||||||
|
if verified {
|
||||||
|
Either::A(service.borrow_mut().call(req))
|
||||||
|
} else {
|
||||||
|
Either::B(err(VerifyError.into()))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeaderKind {
|
||||||
|
pub fn is_authorization(&self) -> bool {
|
||||||
|
HeaderKind::Authorization == *self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_signature(&self) -> bool {
|
||||||
|
HeaderKind::Signature == *self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S> Transform<S> for VerifySignature<T>
|
||||||
|
where
|
||||||
|
T: SignatureVerify + Clone + 'static,
|
||||||
|
S: Service<
|
||||||
|
Request = ServiceRequest,
|
||||||
|
Response = ServiceResponse<Body>,
|
||||||
|
Error = actix_web::Error,
|
||||||
|
> + 'static,
|
||||||
|
S::Error: 'static,
|
||||||
|
{
|
||||||
|
type Request = ServiceRequest;
|
||||||
|
type Response = ServiceResponse<Body>;
|
||||||
|
type Error = actix_web::Error;
|
||||||
|
type Transform = VerifyMiddleware<T, S>;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = FutureResult<Self::Transform, Self::InitError>;
|
||||||
|
|
||||||
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
|
ok(VerifyMiddleware(
|
||||||
|
Rc::new(RefCell::new(service)),
|
||||||
|
self.1.clone(),
|
||||||
|
self.2,
|
||||||
|
self.3,
|
||||||
|
self.0.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S> Service for VerifyMiddleware<T, S>
|
||||||
|
where
|
||||||
|
T: SignatureVerify + Clone + 'static,
|
||||||
|
S: Service<
|
||||||
|
Request = ServiceRequest,
|
||||||
|
Response = ServiceResponse<Body>,
|
||||||
|
Error = actix_web::Error,
|
||||||
|
> + 'static,
|
||||||
|
S::Error: 'static,
|
||||||
|
{
|
||||||
|
type Request = ServiceRequest;
|
||||||
|
type Response = ServiceResponse<Body>;
|
||||||
|
type Error = actix_web::Error;
|
||||||
|
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||||
|
self.0.borrow_mut().poll_ready()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||||
|
let authorization = req.headers().get("Authorization").is_some();
|
||||||
|
let signature = req.headers().get("Signature").is_some();
|
||||||
|
|
||||||
|
if authorization || signature {
|
||||||
|
if self.2.is_authorization() && authorization {
|
||||||
|
return self.handle(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.2.is_signature() && signature {
|
||||||
|
return self.handle(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
Box::new(err(VerifyError.into()))
|
||||||
|
} else if self.3 {
|
||||||
|
Box::new(self.0.borrow_mut().call(req))
|
||||||
|
} else {
|
||||||
|
Box::new(err(VerifyError.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseError for VerifyError {
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
HttpResponse::BadRequest().finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_response(&self) -> HttpResponse {
|
||||||
|
self.error_response()
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ impl Sign for ClientRequest {
|
||||||
f: F,
|
f: F,
|
||||||
) -> Result<Self, E>
|
) -> Result<Self, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
K: Display,
|
K: Display,
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ impl Sign for ClientRequest {
|
||||||
|
|
||||||
fn signature<F, E, K>(mut self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
fn signature<F, E, K>(mut self, config: &Config, key_id: K, f: F) -> Result<Self, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<ToStrError> + From<InvalidHeaderValue>,
|
E: From<ToStrError> + From<InvalidHeaderValue>,
|
||||||
K: Display,
|
K: Display,
|
||||||
{
|
{
|
||||||
|
@ -37,7 +37,7 @@ impl Sign for ClientRequest {
|
||||||
|
|
||||||
fn prepare<F, E, K>(request: &ClientRequest, config: &Config, key_id: K, f: F) -> Result<Signed, E>
|
fn prepare<F, E, K>(request: &ClientRequest, config: &Config, key_id: K, f: F) -> Result<Signed, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
E: From<ToStrError>,
|
E: From<ToStrError>,
|
||||||
K: Display,
|
K: Display,
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,10 +52,10 @@ impl Signed {
|
||||||
impl Unsigned {
|
impl Unsigned {
|
||||||
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
|
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
|
||||||
where
|
where
|
||||||
F: FnOnce(&str) -> Result<Vec<u8>, E>,
|
F: FnOnce(&str) -> Result<String, E>,
|
||||||
{
|
{
|
||||||
(f)(&self.signing_string).map(|v| Signed {
|
(f)(&self.signing_string).map(|signature| Signed {
|
||||||
signature: base64::encode(&v),
|
signature,
|
||||||
sig_headers: self.sig_headers,
|
sig_headers: self.sig_headers,
|
||||||
created: self.created,
|
created: self.created,
|
||||||
expires: self.expires,
|
expires: self.expires,
|
||||||
|
|
|
@ -196,7 +196,7 @@ mod tests {
|
||||||
let authorization_header = config
|
let authorization_header = config
|
||||||
.begin_sign("GET", "/foo?bar=baz", headers)
|
.begin_sign("GET", "/foo?bar=baz", headers)
|
||||||
.sign("hi".to_owned(), |s| {
|
.sign("hi".to_owned(), |s| {
|
||||||
Ok(s.as_bytes().to_vec()) as Result<_, std::io::Error>
|
Ok(s.to_owned()) as Result<_, std::io::Error>
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.authorization_header();
|
.authorization_header();
|
||||||
|
@ -207,7 +207,7 @@ mod tests {
|
||||||
let verified = config
|
let verified = config
|
||||||
.begin_verify("GET", "/foo?bar=baz", headers)
|
.begin_verify("GET", "/foo?bar=baz", headers)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.verify(|bytes, string| string.as_bytes() == bytes);
|
.verify(|sig, signing_string| sig == signing_string);
|
||||||
|
|
||||||
assert!(verified);
|
assert!(verified);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Unverified {
|
pub struct Unverified {
|
||||||
key_id: String,
|
key_id: String,
|
||||||
signature: Vec<u8>,
|
signature: String,
|
||||||
algorithm: Option<Algorithm>,
|
algorithm: Option<Algorithm>,
|
||||||
signing_string: String,
|
signing_string: String,
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,6 @@ pub enum Algorithm {
|
||||||
pub enum ValidateError {
|
pub enum ValidateError {
|
||||||
Missing,
|
Missing,
|
||||||
Expired,
|
Expired,
|
||||||
Decode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -85,7 +84,7 @@ impl Unverified {
|
||||||
|
|
||||||
pub fn verify<F, T>(&self, f: F) -> T
|
pub fn verify<F, T>(&self, f: F) -> T
|
||||||
where
|
where
|
||||||
F: FnOnce(&[u8], &str) -> T,
|
F: FnOnce(&str, &str) -> T,
|
||||||
{
|
{
|
||||||
(f)(&self.signature, &self.signing_string)
|
(f)(&self.signature, &self.signing_string)
|
||||||
}
|
}
|
||||||
|
@ -104,13 +103,11 @@ impl Unvalidated {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let signature = base64::decode(&self.signature).map_err(|_| ValidateError::Decode)?;
|
|
||||||
|
|
||||||
Ok(Unverified {
|
Ok(Unverified {
|
||||||
key_id: self.key_id,
|
key_id: self.key_id,
|
||||||
algorithm: self.algorithm,
|
algorithm: self.algorithm,
|
||||||
signing_string: self.signing_string,
|
signing_string: self.signing_string,
|
||||||
signature,
|
signature: self.signature,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,7 +244,6 @@ impl fmt::Display for ValidateError {
|
||||||
match *self {
|
match *self {
|
||||||
ValidateError::Missing => write!(f, "Http Signature is missing"),
|
ValidateError::Missing => write!(f, "Http Signature is missing"),
|
||||||
ValidateError::Expired => write!(f, "Http Signature is expired"),
|
ValidateError::Expired => write!(f, "Http Signature is expired"),
|
||||||
ValidateError::Decode => write!(f, "Http Signature could not be decoded"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,7 +253,6 @@ impl Error for ValidateError {
|
||||||
match *self {
|
match *self {
|
||||||
ValidateError::Missing => "Http Signature is missing",
|
ValidateError::Missing => "Http Signature is missing",
|
||||||
ValidateError::Expired => "Http Signature is expired",
|
ValidateError::Expired => "Http Signature is expired",
|
||||||
ValidateError::Decode => "Http Signature could not be decoded",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue