mirror of
https://git.asonix.dog/asonix/http-signature-normalization.git
synced 2024-11-21 17:00:59 +00:00
Bump http-signature-normalization version, update actix to 3.0
This commit is contained in:
parent
ebeee051bf
commit
7f98235a37
15 changed files with 337 additions and 389 deletions
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "http-signature-normalization"
|
||||
description = "An HTTP Signatures library that leaves the signing to you"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
license-file = "LICENSE"
|
||||
readme = "README.md"
|
||||
|
@ -20,4 +20,5 @@ members = [
|
|||
]
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
chrono = "0.4"
|
||||
thiserror = "1.0"
|
||||
|
|
|
@ -3,7 +3,7 @@ _An HTTP Signatures library that leaves the signing to you_
|
|||
|
||||
- [crates.io](https://crates.io/crates/http-signature-normalization)
|
||||
- [docs.rs](https://docs.rs/http-signature-normalization)
|
||||
- [Join the discussion on Matrix](https://matrix.to/#/!IRQaBCMWKbpBWKjQgx:asonix.dog?via=asonix.dog)
|
||||
- [Hit me up on Mastodon](https://asonix.dog/@asonix)
|
||||
|
||||
Http Signature Normalization is a minimal-dependency crate for producing HTTP Signatures with user-provided signing and verification. The API is simple; there's a series of steps for creation and verification with types that ensure reasonable usage.
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "http-signature-normalization-actix"
|
||||
description = "An HTTP Signatures library that leaves the signing to you"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0-alpha.1"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
license-file = "LICENSE"
|
||||
readme = "README.md"
|
||||
|
@ -25,13 +25,16 @@ name = "client"
|
|||
required-features = ["sha-2"]
|
||||
|
||||
[dependencies]
|
||||
actix-web = "1.0"
|
||||
base64 = { version = "0.10", optional = true }
|
||||
failure = "0.1"
|
||||
futures = "0.1"
|
||||
http-signature-normalization = { version = "0.2.0", path = ".." }
|
||||
actix-web = "3.0.0-alpha.1"
|
||||
actix-http = "2.0.0-alpha.2"
|
||||
base64 = { version = "0.11", optional = true }
|
||||
bytes = "0.5.4"
|
||||
futures = "0.3"
|
||||
http-signature-normalization = { version = "0.3.0", path = ".." }
|
||||
sha2 = { version = "0.8", optional = true }
|
||||
sha3 = { version = "0.8", optional = true }
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
actix = "0.8"
|
||||
actix = "0.10.0-alpha.1"
|
||||
actix-rt = "1.0.0"
|
||||
|
|
|
@ -3,7 +3,7 @@ _An HTTP Signatures library that leaves the signing to you_
|
|||
|
||||
- [crates.io](https://crates.io/crates/http-signature-normalization-actix)
|
||||
- [docs.rs](https://docs.rs/http-signature-normalization-actix)
|
||||
- [Join the discussion on Matrix](https://matrix.to/#/!IRQaBCMWKbpBWKjQgx:asonix.dog?via=asonix.dog)
|
||||
- [Hit me up on Mastodon](https://asonix.dog/@asonix)
|
||||
|
||||
Http Signature Normalization is a minimal-dependency crate for producing HTTP Signatures with user-provided signing and verification. The API is simple; there's a series of steps for creation and verification with types that ensure reasonable usage.
|
||||
|
||||
|
@ -13,79 +13,69 @@ This crate provides extensions the ClientRequest type from Actix Web, and provid
|
|||
|
||||
#### First, add this crate to your dependencies
|
||||
```toml
|
||||
actix = "0.8"
|
||||
actix-web = "1.0"
|
||||
failure = "0.1"
|
||||
http-signature-normalization-actix = { version = "0.1", default-features = false, features = ["sha2"] }
|
||||
actix = "0.10.0-alpha.1"
|
||||
actix-web = "3.0.0-alpha.1"
|
||||
thiserror = "0.1"
|
||||
http-signature-normalization-actix = { version = "0.3.0-alpha.0", default-features = false, features = ["sha-2"] }
|
||||
sha2 = "0.8"
|
||||
```
|
||||
|
||||
#### Then, use it in your client
|
||||
|
||||
```rust
|
||||
use actix::System;
|
||||
use actix_web::client::Client;
|
||||
use failure::Fail;
|
||||
use futures::future::{lazy, Future};
|
||||
use http_signature_normalization_actix::prelude::*;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
fn main() {
|
||||
System::new("client-example")
|
||||
.block_on(lazy(|| {
|
||||
let config = Config::default();
|
||||
let mut digest = Sha256::new();
|
||||
#[actix_rt::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = Config::default();
|
||||
let mut digest = Sha256::new();
|
||||
|
||||
Client::default()
|
||||
.post("http://127.0.0.1:8010/")
|
||||
.header("User-Agent", "Actix Web")
|
||||
.authorization_signature_with_digest(
|
||||
&config,
|
||||
"my-key-id",
|
||||
&mut digest,
|
||||
"Hewwo-owo",
|
||||
|s| Ok(base64::encode(s)) as Result<_, MyError>,
|
||||
)
|
||||
.unwrap()
|
||||
.send()
|
||||
.map_err(|_| ())
|
||||
.and_then(|mut res| res.body().map_err(|_| ()))
|
||||
.map(|body| {
|
||||
println!("{:?}", body);
|
||||
})
|
||||
}))
|
||||
.unwrap();
|
||||
let mut response = Client::default()
|
||||
.post("http://127.0.0.1:8010/")
|
||||
.header("User-Agent", "Actix Web")
|
||||
.authorization_signature_with_digest(&config, "my-key-id", &mut digest, "Hewwo-owo", |s| {
|
||||
Ok(base64::encode(s)) as Result<_, MyError>
|
||||
})?
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Error, {}", e);
|
||||
MyError::SendRequest
|
||||
})?;
|
||||
|
||||
let body = response.body().await.map_err(|e| {
|
||||
eprintln!("Error, {}", e);
|
||||
MyError::Body
|
||||
})?;
|
||||
|
||||
println!("{:?}", body);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum MyError {
|
||||
#[fail(display = "Failed to read header, {}", _0)]
|
||||
Convert(#[cause] ToStrError),
|
||||
#[error("Failed to read header, {0}")]
|
||||
Convert(#[from] ToStrError),
|
||||
|
||||
#[fail(display = "Failed to create header, {}", _0)]
|
||||
Header(#[cause] InvalidHeaderValue),
|
||||
}
|
||||
#[error("Failed to create header, {0}")]
|
||||
Header(#[from] InvalidHeaderValue),
|
||||
|
||||
impl From<ToStrError> for MyError {
|
||||
fn from(e: ToStrError) -> Self {
|
||||
MyError::Convert(e)
|
||||
}
|
||||
}
|
||||
#[error("Failed to send request")]
|
||||
SendRequest,
|
||||
|
||||
impl From<InvalidHeaderValue> for MyError {
|
||||
fn from(e: InvalidHeaderValue) -> Self {
|
||||
MyError::Header(e)
|
||||
}
|
||||
#[error("Failed to retrieve request body")]
|
||||
Body,
|
||||
}
|
||||
```
|
||||
|
||||
#### Or, use it in your server
|
||||
|
||||
```rust
|
||||
use actix::System;
|
||||
use actix_web::{web, App, HttpResponse, HttpServer, ResponseError};
|
||||
use failure::Fail;
|
||||
use http_signature_normalization_actix::{prelude::*, verify::Algorithm};
|
||||
use actix_web::{http::StatusCode, web, App, HttpResponse, HttpServer, ResponseError};
|
||||
use futures::future::{err, ok, Ready};
|
||||
use http_signature_normalization_actix::prelude::*;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -93,7 +83,7 @@ struct MyVerify;
|
|||
|
||||
impl SignatureVerify for MyVerify {
|
||||
type Error = MyError;
|
||||
type Future = Result<bool, Self::Error>;
|
||||
type Future = Ready<Result<bool, Self::Error>>;
|
||||
|
||||
fn signature_verify(
|
||||
&mut self,
|
||||
|
@ -104,26 +94,28 @@ impl SignatureVerify for MyVerify {
|
|||
) -> Self::Future {
|
||||
match algorithm {
|
||||
Some(Algorithm::Hs2019) => (),
|
||||
_ => return Err(MyError::Algorithm),
|
||||
_ => return err(MyError::Algorithm),
|
||||
};
|
||||
|
||||
if key_id != "my-key-id" {
|
||||
return Err(MyError::Key);
|
||||
return err(MyError::Key);
|
||||
}
|
||||
|
||||
let decoded = base64::decode(signature).map_err(|_| MyError::Decode)?;
|
||||
let decoded = match base64::decode(signature) {
|
||||
Ok(decoded) => decoded,
|
||||
Err(_) => return err(MyError::Decode),
|
||||
};
|
||||
|
||||
Ok(decoded == signing_string.as_bytes())
|
||||
ok(decoded == signing_string.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
fn index(_: (DigestVerified, SignatureVerified)) -> &'static str {
|
||||
async fn index(_: (DigestVerified, SignatureVerified)) -> &'static str {
|
||||
"Eyyyyup"
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let sys = System::new("server-example");
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = Config::default();
|
||||
|
||||
HttpServer::new(move || {
|
||||
|
@ -137,41 +129,35 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.route("/", web::post().to(index))
|
||||
})
|
||||
.bind("127.0.0.1:8010")?
|
||||
.start();
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
sys.run()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum MyError {
|
||||
#[fail(display = "Failed to verify, {}", _0)]
|
||||
Verify(#[cause] PrepareVerifyError),
|
||||
#[error("Failed to verify, {}", _0)]
|
||||
Verify(#[from] PrepareVerifyError),
|
||||
|
||||
#[fail(display = "Unsupported algorithm")]
|
||||
#[error("Unsupported algorithm")]
|
||||
Algorithm,
|
||||
|
||||
#[fail(display = "Couldn't decode signature")]
|
||||
#[error("Couldn't decode signature")]
|
||||
Decode,
|
||||
|
||||
#[fail(display = "Invalid key")]
|
||||
#[error("Invalid key")]
|
||||
Key,
|
||||
}
|
||||
|
||||
impl ResponseError for MyError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::BadRequest().finish()
|
||||
}
|
||||
|
||||
fn render_response(&self) -> HttpResponse {
|
||||
self.error_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrepareVerifyError> for MyError {
|
||||
fn from(e: PrepareVerifyError) -> Self {
|
||||
MyError::Verify(e)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -1,54 +1,45 @@
|
|||
use actix::System;
|
||||
use actix_web::client::Client;
|
||||
use failure::Fail;
|
||||
use futures::future::{lazy, Future};
|
||||
use http_signature_normalization_actix::prelude::*;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
fn main() {
|
||||
System::new("client-example")
|
||||
.block_on(lazy(|| {
|
||||
let config = Config::default();
|
||||
let mut digest = Sha256::new();
|
||||
#[actix_rt::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = Config::default();
|
||||
let mut digest = Sha256::new();
|
||||
|
||||
Client::default()
|
||||
.post("http://127.0.0.1:8010/")
|
||||
.header("User-Agent", "Actix Web")
|
||||
.authorization_signature_with_digest(
|
||||
&config,
|
||||
"my-key-id",
|
||||
&mut digest,
|
||||
"Hewwo-owo",
|
||||
|s| Ok(base64::encode(s)) as Result<_, MyError>,
|
||||
)
|
||||
.unwrap()
|
||||
.send()
|
||||
.map_err(|_| ())
|
||||
.and_then(|mut res| res.body().map_err(|_| ()))
|
||||
.map(|body| {
|
||||
println!("{:?}", body);
|
||||
})
|
||||
}))
|
||||
.unwrap();
|
||||
let mut response = Client::default()
|
||||
.post("http://127.0.0.1:8010/")
|
||||
.header("User-Agent", "Actix Web")
|
||||
.authorization_signature_with_digest(&config, "my-key-id", &mut digest, "Hewwo-owo", |s| {
|
||||
Ok(base64::encode(s)) as Result<_, MyError>
|
||||
})?
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Error, {}", e);
|
||||
MyError::SendRequest
|
||||
})?;
|
||||
|
||||
let body = response.body().await.map_err(|e| {
|
||||
eprintln!("Error, {}", e);
|
||||
MyError::Body
|
||||
})?;
|
||||
|
||||
println!("{:?}", body);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum MyError {
|
||||
#[fail(display = "Failed to read header, {}", _0)]
|
||||
Convert(#[cause] ToStrError),
|
||||
#[error("Failed to read header, {0}")]
|
||||
Convert(#[from] ToStrError),
|
||||
|
||||
#[fail(display = "Failed to create header, {}", _0)]
|
||||
Header(#[cause] InvalidHeaderValue),
|
||||
}
|
||||
#[error("Failed to create header, {0}")]
|
||||
Header(#[from] InvalidHeaderValue),
|
||||
|
||||
impl From<ToStrError> for MyError {
|
||||
fn from(e: ToStrError) -> Self {
|
||||
MyError::Convert(e)
|
||||
}
|
||||
}
|
||||
#[error("Failed to send request")]
|
||||
SendRequest,
|
||||
|
||||
impl From<InvalidHeaderValue> for MyError {
|
||||
fn from(e: InvalidHeaderValue) -> Self {
|
||||
MyError::Header(e)
|
||||
}
|
||||
#[error("Failed to retrieve request body")]
|
||||
Body,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use actix::System;
|
||||
use actix_web::{web, App, HttpResponse, HttpServer, ResponseError};
|
||||
use failure::Fail;
|
||||
use http_signature_normalization_actix::{prelude::*, verify::Algorithm};
|
||||
use actix_web::{http::StatusCode, web, App, HttpResponse, HttpServer, ResponseError};
|
||||
use futures::future::{err, ok, Ready};
|
||||
use http_signature_normalization_actix::prelude::*;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -9,7 +8,7 @@ struct MyVerify;
|
|||
|
||||
impl SignatureVerify for MyVerify {
|
||||
type Error = MyError;
|
||||
type Future = Result<bool, Self::Error>;
|
||||
type Future = Ready<Result<bool, Self::Error>>;
|
||||
|
||||
fn signature_verify(
|
||||
&mut self,
|
||||
|
@ -20,26 +19,28 @@ impl SignatureVerify for MyVerify {
|
|||
) -> Self::Future {
|
||||
match algorithm {
|
||||
Some(Algorithm::Hs2019) => (),
|
||||
_ => return Err(MyError::Algorithm),
|
||||
_ => return err(MyError::Algorithm),
|
||||
};
|
||||
|
||||
if key_id != "my-key-id" {
|
||||
return Err(MyError::Key);
|
||||
return err(MyError::Key);
|
||||
}
|
||||
|
||||
let decoded = base64::decode(signature).map_err(|_| MyError::Decode)?;
|
||||
let decoded = match base64::decode(signature) {
|
||||
Ok(decoded) => decoded,
|
||||
Err(_) => return err(MyError::Decode),
|
||||
};
|
||||
|
||||
Ok(decoded == signing_string.as_bytes())
|
||||
ok(decoded == signing_string.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
fn index(_: (DigestVerified, SignatureVerified)) -> &'static str {
|
||||
async fn index(_: (DigestVerified, SignatureVerified)) -> &'static str {
|
||||
"Eyyyyup"
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let sys = System::new("server-example");
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = Config::default();
|
||||
|
||||
HttpServer::new(move || {
|
||||
|
@ -53,39 +54,33 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.route("/", web::post().to(index))
|
||||
})
|
||||
.bind("127.0.0.1:8010")?
|
||||
.start();
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
sys.run()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum MyError {
|
||||
#[fail(display = "Failed to verify, {}", _0)]
|
||||
Verify(#[cause] PrepareVerifyError),
|
||||
#[error("Failed to verify, {}", _0)]
|
||||
Verify(#[from] PrepareVerifyError),
|
||||
|
||||
#[fail(display = "Unsupported algorithm")]
|
||||
#[error("Unsupported algorithm")]
|
||||
Algorithm,
|
||||
|
||||
#[fail(display = "Couldn't decode signature")]
|
||||
#[error("Couldn't decode signature")]
|
||||
Decode,
|
||||
|
||||
#[fail(display = "Invalid key")]
|
||||
#[error("Invalid key")]
|
||||
Key,
|
||||
}
|
||||
|
||||
impl ResponseError for MyError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::BadRequest().finish()
|
||||
}
|
||||
|
||||
fn render_response(&self) -> HttpResponse {
|
||||
self.error_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrepareVerifyError> for MyError {
|
||||
fn from(e: PrepareVerifyError) -> Self {
|
||||
MyError::Verify(e)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,17 +3,22 @@
|
|||
use actix_web::{
|
||||
dev::{Body, Payload, Service, ServiceRequest, ServiceResponse, Transform},
|
||||
error::PayloadError,
|
||||
http::header::HeaderValue,
|
||||
web::Bytes,
|
||||
http::{header::HeaderValue, StatusCode},
|
||||
FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError,
|
||||
};
|
||||
use failure::Fail;
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::{
|
||||
future::{err, ok, Either, FutureResult},
|
||||
future::{err, ok, ready, Ready},
|
||||
stream::once,
|
||||
Future, Poll, Stream,
|
||||
Stream, StreamExt,
|
||||
};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use super::{DigestPart, DigestVerify};
|
||||
|
||||
|
@ -42,8 +47,8 @@ pub struct VerifyDigest<T>(bool, T);
|
|||
#[doc(hidden)]
|
||||
pub struct VerifyMiddleware<T, S>(Rc<RefCell<S>>, bool, T);
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
#[fail(display = "Error verifying digest")]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Error verifying digest")]
|
||||
#[doc(hidden)]
|
||||
pub struct VerifyError;
|
||||
|
||||
|
@ -67,14 +72,16 @@ where
|
|||
|
||||
impl FromRequest for DigestVerified {
|
||||
type Error = VerifyError;
|
||||
type Future = Result<Self, Self::Error>;
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
type Config = ();
|
||||
|
||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||
req.extensions()
|
||||
.get::<Self>()
|
||||
.map(|s| *s)
|
||||
.ok_or(VerifyError)
|
||||
ready(
|
||||
req.extensions()
|
||||
.get::<Self>()
|
||||
.map(|s| *s)
|
||||
.ok_or(VerifyError),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +100,7 @@ where
|
|||
type Error = actix_web::Error;
|
||||
type Transform = VerifyMiddleware<T, S>;
|
||||
type InitError = ();
|
||||
type Future = FutureResult<Self::Transform, Self::InitError>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ok(VerifyMiddleware(
|
||||
|
@ -117,41 +124,48 @@ where
|
|||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<Body>;
|
||||
type Error = actix_web::Error;
|
||||
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.0.borrow_mut().poll_ready()
|
||||
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
self.0.borrow_mut().poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
||||
if let Some(digest) = req.headers().get("Digest") {
|
||||
let vec = match parse_digest(digest) {
|
||||
Some(vec) => vec,
|
||||
None => return Box::new(err(VerifyError.into())),
|
||||
None => return Box::pin(err(VerifyError.into())),
|
||||
};
|
||||
let payload = req.take_payload();
|
||||
let mut payload = req.take_payload();
|
||||
let service = self.0.clone();
|
||||
let mut verify_digest = self.2.clone();
|
||||
|
||||
Box::new(payload.concat2().from_err().and_then(move |bytes| {
|
||||
Box::pin(async move {
|
||||
let mut output_bytes = BytesMut::new();
|
||||
while let Some(res) = payload.next().await {
|
||||
let bytes = res?;
|
||||
output_bytes.extend(bytes);
|
||||
}
|
||||
let bytes = output_bytes.freeze();
|
||||
|
||||
if verify_digest.verify(&vec, &bytes.as_ref()) {
|
||||
req.set_payload(
|
||||
(Box::new(once(Ok(bytes)))
|
||||
as Box<dyn Stream<Item = Bytes, Error = PayloadError>>)
|
||||
(Box::pin(once(ok(bytes)))
|
||||
as Pin<Box<dyn Stream<Item = Result<Bytes, PayloadError>> + 'static>>)
|
||||
.into(),
|
||||
);
|
||||
|
||||
req.extensions_mut().insert(DigestVerified);
|
||||
|
||||
Either::A(service.borrow_mut().call(req))
|
||||
service.borrow_mut().call(req).await
|
||||
} else {
|
||||
Either::B(err(VerifyError.into()))
|
||||
Err(VerifyError.into())
|
||||
}
|
||||
}))
|
||||
})
|
||||
} else if self.1 {
|
||||
Box::new(err(VerifyError.into()))
|
||||
Box::pin(err(VerifyError.into()))
|
||||
} else {
|
||||
Box::new(self.0.borrow_mut().call(req))
|
||||
Box::pin(self.0.borrow_mut().call(req))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,11 +193,11 @@ fn parse_digest(h: &HeaderValue) -> Option<Vec<DigestPart>> {
|
|||
}
|
||||
|
||||
impl ResponseError for VerifyError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::BadRequest().finish()
|
||||
}
|
||||
|
||||
fn render_response(&self) -> HttpResponse {
|
||||
Self::error_response(self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
//! Digest headers are commonly used in conjunction with HTTP Signatures to verify the whole
|
||||
//! request when request bodies are present
|
||||
|
||||
use actix_http::encoding::Decoder;
|
||||
use actix_web::{
|
||||
client::{ClientRequest, ClientResponse},
|
||||
error::PayloadError,
|
||||
client::{ClientRequest, ClientResponse, SendRequestError},
|
||||
dev::Payload,
|
||||
http::header::{InvalidHeaderValue, ToStrError},
|
||||
web::Bytes,
|
||||
};
|
||||
use futures::{Future, Stream};
|
||||
use std::fmt::Display;
|
||||
use std::{fmt::Display, future::Future};
|
||||
|
||||
use crate::{Config, Sign};
|
||||
|
||||
|
@ -112,7 +111,7 @@ where
|
|||
/// the digest
|
||||
pub fn send(
|
||||
self,
|
||||
) -> impl Future<Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>> {
|
||||
) -> impl Future<Output = Result<ClientResponse<Decoder<Payload>>, SendRequestError>> {
|
||||
self.req.send_body(self.body.as_ref().to_vec())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
//!
|
||||
//! ### Use it in a server
|
||||
//! ```rust,ignore
|
||||
//! use actix::System;
|
||||
//! use actix_web::{web, App, HttpResponse, HttpServer, ResponseError};
|
||||
//! use failure::Fail;
|
||||
//! use http_signature_normalization_actix::{prelude::*, verify::Algorithm};
|
||||
//! use actix_web::{http::StatusCode, web, App, HttpResponse, HttpServer, ResponseError};
|
||||
//! use futures::future::{err, ok, Ready};
|
||||
//! use http_signature_normalization_actix::prelude::*;
|
||||
//! use sha2::{Digest, Sha256};
|
||||
//!
|
||||
//! #[derive(Clone, Debug)]
|
||||
|
@ -19,7 +18,7 @@
|
|||
//!
|
||||
//! impl SignatureVerify for MyVerify {
|
||||
//! type Error = MyError;
|
||||
//! type Future = Result<bool, Self::Error>;
|
||||
//! type Future = Ready<Result<bool, Self::Error>>;
|
||||
//!
|
||||
//! fn signature_verify(
|
||||
//! &mut self,
|
||||
|
@ -30,28 +29,28 @@
|
|||
//! ) -> Self::Future {
|
||||
//! match algorithm {
|
||||
//! Some(Algorithm::Hs2019) => (),
|
||||
//! _ => return Err(MyError::Algorithm),
|
||||
//! _ => return err(MyError::Algorithm),
|
||||
//! };
|
||||
//!
|
||||
//! if key_id != "my-key-id" {
|
||||
//! return Err(MyError::Key);
|
||||
//! return err(MyError::Key);
|
||||
//! }
|
||||
//!
|
||||
//! let decoded = base64::decode(signature).map_err(|_| MyError::Decode)?;
|
||||
//! let decoded = match base64::decode(signature) {
|
||||
//! Ok(decoded) => decoded,
|
||||
//! Err(_) => return err(MyError::Decode),
|
||||
//! };
|
||||
//!
|
||||
//! // In a real system, you'd want to actually verify a signature, not just check for
|
||||
//! // byte equality
|
||||
//! Ok(decoded == signing_string.as_bytes())
|
||||
//! ok(decoded == signing_string.as_bytes())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn index(_: (DigestVerified, SignatureVerified)) -> &'static str {
|
||||
//! async fn index(_: (DigestVerified, SignatureVerified)) -> &'static str {
|
||||
//! "Eyyyyup"
|
||||
//! }
|
||||
//!
|
||||
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let sys = System::new("server-example");
|
||||
//!
|
||||
//! #[actix_rt::main]
|
||||
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let config = Config::default();
|
||||
//!
|
||||
//! HttpServer::new(move || {
|
||||
|
@ -65,103 +64,84 @@
|
|||
//! .route("/", web::post().to(index))
|
||||
//! })
|
||||
//! .bind("127.0.0.1:8010")?
|
||||
//! .start();
|
||||
//! .run()
|
||||
//! .await?;
|
||||
//!
|
||||
//! sys.run()?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! #[derive(Debug, Fail)]
|
||||
//! #[derive(Debug, thiserror::Error)]
|
||||
//! enum MyError {
|
||||
//! #[fail(display = "Failed to verify, {}", _0)]
|
||||
//! Verify(#[cause] PrepareVerifyError),
|
||||
//! #[error("Failed to verify, {}", _0)]
|
||||
//! Verify(#[from] PrepareVerifyError),
|
||||
//!
|
||||
//! #[fail(display = "Unsupported algorithm")]
|
||||
//! #[error("Unsupported algorithm")]
|
||||
//! Algorithm,
|
||||
//!
|
||||
//! #[fail(display = "Couldn't decode signature")]
|
||||
//! #[error("Couldn't decode signature")]
|
||||
//! Decode,
|
||||
//!
|
||||
//! #[fail(display = "Invalid key")]
|
||||
//! #[error("Invalid key")]
|
||||
//! Key,
|
||||
//! }
|
||||
//!
|
||||
//! impl ResponseError for MyError {
|
||||
//! fn status_code(&self) -> StatusCode {
|
||||
//! StatusCode::BAD_REQUEST
|
||||
//! }
|
||||
//!
|
||||
//! fn error_response(&self) -> HttpResponse {
|
||||
//! HttpResponse::BadRequest().finish()
|
||||
//! }
|
||||
//!
|
||||
//! fn render_response(&self) -> HttpResponse {
|
||||
//! self.error_response()
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl From<PrepareVerifyError> for MyError {
|
||||
//! fn from(e: PrepareVerifyError) -> Self {
|
||||
//! MyError::Verify(e)
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Use it in a client
|
||||
//! ```rust,ignore
|
||||
//! use actix::System;
|
||||
//! use actix_web::client::Client;
|
||||
//! use failure::Fail;
|
||||
//! use futures::future::{lazy, Future};
|
||||
//! use http_signature_normalization_actix::prelude::*;
|
||||
//! use sha2::{Digest, Sha256};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! System::new("client-example")
|
||||
//! .block_on(lazy(|| {
|
||||
//! let config = Config::default();
|
||||
//! let mut digest = Sha256::new();
|
||||
//! #[actix_rt::main]
|
||||
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let config = Config::default();
|
||||
//! let mut digest = Sha256::new();
|
||||
//!
|
||||
//! Client::default()
|
||||
//! .post("http://127.0.0.1:8010/")
|
||||
//! .header("User-Agent", "Actix Web")
|
||||
//! .authorization_signature_with_digest(
|
||||
//! &config,
|
||||
//! "my-key-id",
|
||||
//! &mut digest,
|
||||
//! "Hewwo-owo",
|
||||
//! |s| {
|
||||
//! // In a real-world system, you'd actually want to sign the string,
|
||||
//! // not just base64 encode it
|
||||
//! Ok(base64::encode(s)) as Result<_, MyError>
|
||||
//! },
|
||||
//! )
|
||||
//! .unwrap()
|
||||
//! .send()
|
||||
//! .map_err(|_| ())
|
||||
//! .and_then(|mut res| res.body().map_err(|_| ()))
|
||||
//! .map(|body| {
|
||||
//! println!("{:?}", body);
|
||||
//! })
|
||||
//! }))
|
||||
//! .unwrap();
|
||||
//! let mut response = Client::default()
|
||||
//! .post("http://127.0.0.1:8010/")
|
||||
//! .header("User-Agent", "Actix Web")
|
||||
//! .authorization_signature_with_digest(&config, "my-key-id", &mut digest, "Hewwo-owo", |s| {
|
||||
//! Ok(base64::encode(s)) as Result<_, MyError>
|
||||
//! })?
|
||||
//! .send()
|
||||
//! .await
|
||||
//! .map_err(|e| {
|
||||
//! eprintln!("Error, {}", e);
|
||||
//! MyError::SendRequest
|
||||
//! })?;
|
||||
//!
|
||||
//! let body = response.body().await.map_err(|e| {
|
||||
//! eprintln!("Error, {}", e);
|
||||
//! MyError::Body
|
||||
//! })?;
|
||||
//!
|
||||
//! println!("{:?}", body);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! #[derive(Debug, Fail)]
|
||||
//! #[derive(Debug, thiserror::Error)]
|
||||
//! pub enum MyError {
|
||||
//! #[fail(display = "Failed to read header, {}", _0)]
|
||||
//! Convert(#[cause] ToStrError),
|
||||
//! #[error("Failed to read header, {0}")]
|
||||
//! Convert(#[from] ToStrError),
|
||||
//!
|
||||
//! #[fail(display = "Failed to create header, {}", _0)]
|
||||
//! Header(#[cause] InvalidHeaderValue),
|
||||
//! }
|
||||
//! #[error("Failed to create header, {0}")]
|
||||
//! Header(#[from] InvalidHeaderValue),
|
||||
//!
|
||||
//! impl From<ToStrError> for MyError {
|
||||
//! fn from(e: ToStrError) -> Self {
|
||||
//! MyError::Convert(e)
|
||||
//! }
|
||||
//! }
|
||||
//! #[error("Failed to send request")]
|
||||
//! SendRequest,
|
||||
//!
|
||||
//! impl From<InvalidHeaderValue> for MyError {
|
||||
//! fn from(e: InvalidHeaderValue) -> Self {
|
||||
//! MyError::Header(e)
|
||||
//! }
|
||||
//! #[error("Failed to retrieve request body")]
|
||||
//! Body,
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
|
@ -171,9 +151,7 @@ use actix_web::http::{
|
|||
Method,
|
||||
};
|
||||
|
||||
use failure::Fail;
|
||||
use futures::future::IntoFuture;
|
||||
use std::{collections::BTreeMap, fmt::Display};
|
||||
use std::{collections::BTreeMap, fmt::Display, future::Future};
|
||||
|
||||
mod sign;
|
||||
|
||||
|
@ -187,7 +165,7 @@ pub mod middleware;
|
|||
pub mod prelude {
|
||||
pub use crate::{
|
||||
middleware::{SignatureVerified, VerifySignature},
|
||||
verify::Unverified,
|
||||
verify::{Algorithm, Unverified},
|
||||
Config, PrepareVerifyError, Sign, SignatureVerify,
|
||||
};
|
||||
|
||||
|
@ -220,7 +198,7 @@ pub trait SignatureVerify {
|
|||
type Error: actix_web::ResponseError;
|
||||
|
||||
/// The future that resolves to the verification state of the signature
|
||||
type Future: IntoFuture<Item = bool, Error = Self::Error>;
|
||||
type Future: Future<Output = Result<bool, Self::Error>>;
|
||||
|
||||
/// Given the algorithm, key_id, signature, and signing_string, produce a future that resulves
|
||||
/// to a the verification status
|
||||
|
@ -259,16 +237,16 @@ pub struct Config {
|
|||
pub config: http_signature_normalization::Config,
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
/// An error when preparing to verify a request
|
||||
pub enum PrepareVerifyError {
|
||||
#[fail(display = "Signature error, {}", _0)]
|
||||
#[error("Signature error, {0}")]
|
||||
/// An error validating the request
|
||||
Sig(#[cause] http_signature_normalization::PrepareVerifyError),
|
||||
Sig(#[from] http_signature_normalization::PrepareVerifyError),
|
||||
|
||||
#[fail(display = "Failed to read header, {}", _0)]
|
||||
#[error("Failed to read header, {0}")]
|
||||
/// An error converting the header to a string for validation
|
||||
Header(#[cause] ToStrError),
|
||||
Header(#[from] ToStrError),
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -318,15 +296,3 @@ impl Config {
|
|||
Ok(unverified)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<http_signature_normalization::PrepareVerifyError> for PrepareVerifyError {
|
||||
fn from(e: http_signature_normalization::PrepareVerifyError) -> Self {
|
||||
PrepareVerifyError::Sig(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ToStrError> for PrepareVerifyError {
|
||||
fn from(e: ToStrError) -> Self {
|
||||
PrepareVerifyError::Header(e)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
|
||||
use actix_web::{
|
||||
dev::{Body, Payload, Service, ServiceRequest, ServiceResponse, Transform},
|
||||
FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError,
|
||||
http::StatusCode,
|
||||
Error, FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError,
|
||||
};
|
||||
use failure::Fail;
|
||||
use futures::{
|
||||
future::{err, ok, Either, FutureResult, IntoFuture},
|
||||
Future, Poll,
|
||||
use futures::future::{err, ok, ready, Ready};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{Config, SignatureVerify};
|
||||
|
||||
|
@ -45,8 +48,8 @@ enum HeaderKind {
|
|||
Signature,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Fail)]
|
||||
#[fail(display = "Failed to verify http signature")]
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[error("Failed to verify http signature")]
|
||||
#[doc(hidden)]
|
||||
pub struct VerifyError;
|
||||
|
||||
|
@ -82,17 +85,12 @@ 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,
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error> + 'static,
|
||||
{
|
||||
fn handle(
|
||||
&mut self,
|
||||
req: ServiceRequest,
|
||||
) -> Box<dyn Future<Item = ServiceResponse<Body>, Error = actix_web::Error>> {
|
||||
) -> Pin<Box<dyn Future<Output = Result<ServiceResponse<Body>, Error>>>> {
|
||||
let res = self.1.begin_verify(
|
||||
req.method(),
|
||||
req.uri().path_and_query(),
|
||||
|
@ -101,32 +99,29 @@ where
|
|||
|
||||
let unverified = match res {
|
||||
Ok(unverified) => unverified,
|
||||
Err(_) => return Box::new(err(VerifyError.into())),
|
||||
Err(_) => return Box::pin(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| {
|
||||
let service = self.0.clone();
|
||||
|
||||
let fut = unverified.verify(|signature, signing_string| {
|
||||
self.4
|
||||
.signature_verify(algorithm, &key_id, signature, signing_string)
|
||||
});
|
||||
|
||||
let service = self.0.clone();
|
||||
Box::pin(async move {
|
||||
let verified = fut.await?;
|
||||
|
||||
Box::new(
|
||||
verified
|
||||
.into_future()
|
||||
.from_err::<actix_web::Error>()
|
||||
.and_then(move |verified| {
|
||||
if verified {
|
||||
req.extensions_mut().insert(SignatureVerified);
|
||||
Either::A(service.borrow_mut().call(req))
|
||||
} else {
|
||||
Either::B(err(VerifyError.into()))
|
||||
}
|
||||
}),
|
||||
)
|
||||
if verified {
|
||||
req.extensions_mut().insert(SignatureVerified);
|
||||
service.borrow_mut().call(req).await
|
||||
} else {
|
||||
Err(VerifyError.into())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,14 +137,16 @@ impl HeaderKind {
|
|||
|
||||
impl FromRequest for SignatureVerified {
|
||||
type Error = VerifyError;
|
||||
type Future = Result<Self, Self::Error>;
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
type Config = ();
|
||||
|
||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||
req.extensions()
|
||||
.get::<Self>()
|
||||
.map(|s| *s)
|
||||
.ok_or(VerifyError)
|
||||
ready(
|
||||
req.extensions()
|
||||
.get::<Self>()
|
||||
.map(|s| *s)
|
||||
.ok_or(VerifyError),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +165,7 @@ where
|
|||
type Error = actix_web::Error;
|
||||
type Transform = VerifyMiddleware<T, S>;
|
||||
type InitError = ();
|
||||
type Future = FutureResult<Self::Transform, Self::InitError>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ok(VerifyMiddleware(
|
||||
|
@ -194,10 +191,10 @@ where
|
|||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<Body>;
|
||||
type Error = actix_web::Error;
|
||||
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.0.borrow_mut().poll_ready()
|
||||
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
self.0.borrow_mut().poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||
|
@ -213,21 +210,21 @@ where
|
|||
return self.handle(req);
|
||||
}
|
||||
|
||||
Box::new(err(VerifyError.into()))
|
||||
Box::pin(err(VerifyError.into()))
|
||||
} else if self.3 {
|
||||
Box::new(self.0.borrow_mut().call(req))
|
||||
Box::pin(self.0.borrow_mut().call(req))
|
||||
} else {
|
||||
Box::new(err(VerifyError.into()))
|
||||
Box::pin(err(VerifyError.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for VerifyError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::BadRequest().finish()
|
||||
}
|
||||
|
||||
fn render_response(&self) -> HttpResponse {
|
||||
self.error_response()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
http = "0.2"
|
||||
http-signature-normalization = { version = "0.2.0", path = ".." }
|
||||
http-signature-normalization = { version = "0.3.0", path = ".." }
|
||||
|
|
|
@ -5,9 +5,21 @@ authors = ["asonix <asonix@asonix.dog>"]
|
|||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
default = ["sha-2"]
|
||||
digest = ["base64", "serde", "serde_json", "serde_urlencoded", "thiserror"]
|
||||
sha-2 = ["digest", "sha2"]
|
||||
|
||||
[dependencies]
|
||||
base64 = { version = "0.11.0", optional = true }
|
||||
bytes = "0.5.3"
|
||||
futures = "0.3.1"
|
||||
chrono = "0.4.10"
|
||||
http = "0.2.0"
|
||||
http-signature-normalization = { version = "0.2.0", path = ".." }
|
||||
http-signature-normalization = { version = "0.3.0", path = ".." }
|
||||
reqwest = "0.10.1"
|
||||
serde = { version = "1.0.104", features = ["derive"], optional = true }
|
||||
serde_json = { version = "1.0.44", optional = true }
|
||||
serde_urlencoded = { version = "0.6.1", optional = true }
|
||||
sha2 = { version = "0.8.1", optional = true }
|
||||
thiserror = { version = "1.0.9", optional = true }
|
||||
|
|
16
http-signature-normalization-reqwest/src/digest/mod.rs
Normal file
16
http-signature-normalization-reqwest/src/digest/mod.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use reqwest::Request;
|
||||
use std::{future::Future, pin::Pin};
|
||||
|
||||
pub trait CreateDigest {
|
||||
fn create_digest(&mut self, payload: &[u8]) -> String;
|
||||
}
|
||||
|
||||
pub trait WithDigest: Sized {
|
||||
type Future: Future<Output = Self>;
|
||||
|
||||
fn with_digest<T>(&mut self, creator: T) -> Self::Future;
|
||||
}
|
||||
|
||||
impl WithDigest for Request {
|
||||
type Future = Pin<Box<dyn Future<Output = Self> + Send>>;
|
||||
}
|
|
@ -6,6 +6,8 @@ use reqwest::{
|
|||
};
|
||||
use std::fmt::Display;
|
||||
|
||||
pub mod digest;
|
||||
|
||||
pub struct Config(http_signature_normalization::Config);
|
||||
|
||||
pub trait Sign {
|
||||
|
|
48
src/lib.rs
48
src/lib.rs
|
@ -45,7 +45,7 @@
|
|||
//! ```
|
||||
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use std::{collections::BTreeMap, error::Error, fmt};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub mod create;
|
||||
pub mod verify;
|
||||
|
@ -77,15 +77,18 @@ pub struct Config {
|
|||
pub expires_after: Duration,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
/// Error preparing a header for validation
|
||||
///
|
||||
/// This could be due to a missing header, and unparsable header, or an expired header
|
||||
pub enum PrepareVerifyError {
|
||||
#[error("{0}")]
|
||||
/// Error validating the header
|
||||
Validate(ValidateError),
|
||||
Validate(#[from] ValidateError),
|
||||
|
||||
#[error("{0}")]
|
||||
/// Error parsing the header
|
||||
Parse(ParseSignatureError),
|
||||
Parse(#[from] ParseSignatureError),
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -189,43 +192,6 @@ fn build_signing_string(
|
|||
signing_string
|
||||
}
|
||||
|
||||
impl fmt::Display for PrepareVerifyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
PrepareVerifyError::Validate(ref e) => fmt::Display::fmt(e, f),
|
||||
PrepareVerifyError::Parse(ref e) => fmt::Display::fmt(e, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for PrepareVerifyError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
PrepareVerifyError::Validate(ref e) => e.description(),
|
||||
PrepareVerifyError::Parse(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match *self {
|
||||
PrepareVerifyError::Validate(ref e) => Some(e),
|
||||
PrepareVerifyError::Parse(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValidateError> for PrepareVerifyError {
|
||||
fn from(v: ValidateError) -> Self {
|
||||
PrepareVerifyError::Validate(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseSignatureError> for PrepareVerifyError {
|
||||
fn from(p: ParseSignatureError) -> Self {
|
||||
PrepareVerifyError::Parse(p)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
|
|
Loading…
Reference in a new issue