use chrono::Duration; use http_signature_normalization::create::Signed; use reqwest::{ header::{InvalidHeaderValue, ToStrError}, Request, }; use std::fmt::Display; pub struct Config(http_signature_normalization::Config); pub trait Sign { fn authorization_signature(self, config: &Config, key_id: K, f: F) -> Result where Self: Sized, F: FnOnce(&str) -> Result, E: From + From, K: Display; fn signature(self, config: &Config, key_id: K, f: F) -> Result where Self: Sized, F: FnOnce(&str) -> Result, E: From + From, K: Display; } impl Config { pub fn new(expires_after: Duration) -> Self { Config(http_signature_normalization::Config { expires_after }) } } impl Sign for Request { fn authorization_signature( mut self, config: &Config, key_id: K, f: F, ) -> Result where F: FnOnce(&str) -> Result, E: From + From, K: Display, { let signed = prepare(&self, config, key_id, f)?; let auth_header = signed.authorization_header(); self.headers_mut() .insert("Authorization", auth_header.parse()?); Ok(self) } fn signature(mut self, config: &Config, key_id: K, f: F) -> Result where F: FnOnce(&str) -> Result, E: From + From, K: Display, { let signed = prepare(&self, config, key_id, f)?; let sig_header = signed.signature_header(); self.headers_mut().insert("Signature", sig_header.parse()?); Ok(self) } } fn prepare(req: &Request, config: &Config, key_id: K, f: F) -> Result where F: FnOnce(&str) -> Result, E: From + From, K: Display, { let mut bt = std::collections::BTreeMap::new(); for (k, v) in req.headers().iter() { bt.insert(k.as_str().to_owned(), v.to_str()?.to_owned()); } let path_and_query = if let Some(query) = req.url().query() { format!("{}?{}", req.url().path(), query) } else { req.url().path().to_string() }; let unsigned = config .0 .begin_sign(req.method().as_str(), &path_and_query, bt); let signed = unsigned.sign(key_id.to_string(), f)?; Ok(signed) }