2019-09-21 16:26:11 +00:00
|
|
|
//! Types and logic for creating signature and authorization headers
|
2019-09-11 05:17:30 +00:00
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
ALGORITHM_FIELD, ALGORITHM_VALUE, CREATED_FIELD, EXPIRES_FIELD, HEADERS_FIELD, KEY_ID_FIELD,
|
|
|
|
SIGNATURE_FIELD,
|
|
|
|
};
|
|
|
|
|
2019-09-11 06:24:51 +00:00
|
|
|
#[derive(Debug)]
|
2019-09-21 16:26:11 +00:00
|
|
|
/// The signed stage of creating a signature
|
|
|
|
///
|
|
|
|
/// From here, the Signature or Authorization headers can be generated as string
|
2019-09-11 05:17:30 +00:00
|
|
|
pub struct Signed {
|
|
|
|
signature: String,
|
|
|
|
sig_headers: Vec<String>,
|
2020-03-17 23:54:00 +00:00
|
|
|
created: Option<DateTime<Utc>>,
|
|
|
|
expires: Option<DateTime<Utc>>,
|
2019-09-11 05:17:30 +00:00
|
|
|
key_id: String,
|
|
|
|
}
|
|
|
|
|
2019-09-11 06:24:51 +00:00
|
|
|
#[derive(Debug)]
|
2019-09-21 16:26:11 +00:00
|
|
|
/// The Unsigned stage of creating a signature
|
|
|
|
///
|
|
|
|
/// From here, the `sign` method can be used to sign the signing_string, producing a [`Signed`]
|
|
|
|
/// type.
|
2019-09-11 05:17:30 +00:00
|
|
|
pub struct Unsigned {
|
|
|
|
pub(crate) signing_string: String,
|
|
|
|
pub(crate) sig_headers: Vec<String>,
|
2020-03-17 23:54:00 +00:00
|
|
|
pub(crate) created: Option<DateTime<Utc>>,
|
|
|
|
pub(crate) expires: Option<DateTime<Utc>>,
|
2019-09-11 05:17:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Signed {
|
2019-09-21 16:26:11 +00:00
|
|
|
/// Turn the Signed type into a String that can be used as the Signature Header
|
|
|
|
///
|
|
|
|
/// Done manually, it would look like `format!("Signature: {}", signed.signature_header())`
|
2019-09-11 05:24:04 +00:00
|
|
|
pub fn signature_header(self) -> String {
|
2020-01-18 00:35:30 +00:00
|
|
|
self.into_header()
|
2019-09-11 05:17:30 +00:00
|
|
|
}
|
|
|
|
|
2019-09-21 16:26:11 +00:00
|
|
|
/// 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())`
|
2019-09-11 05:24:04 +00:00
|
|
|
pub fn authorization_header(self) -> String {
|
2020-01-18 00:35:30 +00:00
|
|
|
format!("Signature {}", self.into_header())
|
2019-09-11 05:17:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn into_header(self) -> String {
|
2020-03-17 23:54:00 +00:00
|
|
|
let mapped = self.created.and_then(|c| self.expires.map(|e| (c, e)));
|
|
|
|
let header_parts = if let Some((created, expires)) = mapped {
|
|
|
|
vec![
|
|
|
|
(KEY_ID_FIELD, self.key_id),
|
|
|
|
(ALGORITHM_FIELD, ALGORITHM_VALUE.to_owned()),
|
|
|
|
(CREATED_FIELD, created.timestamp().to_string()),
|
|
|
|
(EXPIRES_FIELD, expires.timestamp().to_string()),
|
|
|
|
(HEADERS_FIELD, self.sig_headers.join(" ")),
|
|
|
|
(SIGNATURE_FIELD, self.signature),
|
|
|
|
]
|
|
|
|
} else {
|
|
|
|
vec![
|
|
|
|
(KEY_ID_FIELD, self.key_id),
|
|
|
|
(ALGORITHM_FIELD, ALGORITHM_VALUE.to_owned()),
|
|
|
|
(HEADERS_FIELD, self.sig_headers.join(" ")),
|
|
|
|
(SIGNATURE_FIELD, self.signature),
|
|
|
|
]
|
|
|
|
};
|
2019-09-11 05:17:30 +00:00
|
|
|
|
|
|
|
header_parts
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v)| format!("{}=\"{}\"", k, v))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(",")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Unsigned {
|
2019-09-21 16:26:11 +00:00
|
|
|
/// Sign the signing string, producing a String that can be used in an HTTP Header
|
|
|
|
///
|
|
|
|
/// When using RSA or HMAC to sign the string, be sure to base64-encode the result to produce a
|
|
|
|
/// String.
|
2019-09-11 05:17:30 +00:00
|
|
|
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
|
|
|
|
where
|
2019-09-13 22:55:51 +00:00
|
|
|
F: FnOnce(&str) -> Result<String, E>,
|
2019-09-11 05:17:30 +00:00
|
|
|
{
|
2019-09-13 22:55:51 +00:00
|
|
|
(f)(&self.signing_string).map(|signature| Signed {
|
|
|
|
signature,
|
2019-09-11 05:17:30 +00:00
|
|
|
sig_headers: self.sig_headers,
|
|
|
|
created: self.created,
|
|
|
|
expires: self.expires,
|
|
|
|
key_id,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|