1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use crate::MAuthInfo;
use chrono::prelude::*;
use reqwest::{Request, header::HeaderValue};
use thiserror::Error;
impl MAuthInfo {
/// This method determines how to sign the request automatically while respecting the
/// `v2_only_sign_requests` flag in the config file. It always signs with the V2 algorithm and
/// signature, and will also sign with the V1 algorithm, if the configuration permits.
///
/// Note that, as the request signature includes a timestamp, the request must be sent out
/// shortly after the signature takes place.
///
/// Note that it will need to read the entire body in order to sign it, so it will not
/// work properly if any of the streaming body types are used.
pub fn sign_request(&self, req: &mut Request) -> Result<(), SigningError> {
self.sign_request_v2(req)?;
if self.sign_with_v1_also {
self.sign_request_v1(req)?;
}
Ok(())
}
/// Sign a provided request using the MAuth V2 protocol. The signature consists of 2 headers
/// containing both a timestamp and a signature string, and will be added to the headers of the
/// request. It is required to pass a `body_digest` computed by the
/// [`build_body_with_digest`](#method.build_body_with_digest) method, even if the request is
/// an empty-body GET.
///
/// Note that, as the request signature includes a timestamp, the request must be sent out
/// shortly after the signature takes place.
///
/// Also note that it will need to read the entire body in order to sign it, so it will not
/// work properly if any of the streaming body types are used.
pub fn sign_request_v2(&self, req: &mut Request) -> Result<(), SigningError> {
let timestamp_str = Utc::now().timestamp().to_string();
let body_data = match req.body() {
None => &[],
Some(reqwest_body) => reqwest_body.as_bytes().unwrap_or(&[]),
};
let some_string = self.signer.sign_string(
2,
req.method().as_str(),
req.url().path(),
req.url().query().unwrap_or(""),
body_data,
timestamp_str.clone(),
)?;
self.set_headers_v2(req, some_string, ×tamp_str);
Ok(())
}
pub(crate) fn set_headers_v2(&self, req: &mut Request, signature: String, timestamp_str: &str) {
let sig_head_str = format!("MWSV2 {}:{};", self.app_id, &signature);
let headers = req.headers_mut();
headers.insert("MCC-Time", HeaderValue::from_str(timestamp_str).unwrap());
headers.insert(
"MCC-Authentication",
HeaderValue::from_str(&sig_head_str).unwrap(),
);
}
/// Sign a provided request using the MAuth V1 protocol. The signature consists of 2 headers
/// containing both a timestamp and a signature string, and will be added to the headers of the
/// request. It is required to pass a `body`, even if the request is an empty-body GET.
///
/// Note that, as the request signature includes a timestamp, the request must be sent out
/// shortly after the signature takes place.
///
/// Also note that it will need to read the entire body in order to sign it, so it will not
/// work properly if any of the streaming body types are used.
pub fn sign_request_v1(&self, req: &mut Request) -> Result<(), SigningError> {
let timestamp_str = Utc::now().timestamp().to_string();
let body_data = match req.body() {
None => &[],
Some(reqwest_body) => reqwest_body.as_bytes().unwrap_or(&[]),
};
let sig = self.signer.sign_string(
1,
req.method().as_str(),
req.url().path(),
req.url().query().unwrap_or(""),
body_data,
timestamp_str.clone(),
)?;
let headers = req.headers_mut();
headers.insert("X-MWS-Time", HeaderValue::from_str(×tamp_str).unwrap());
headers.insert("X-MWS-Authentication", HeaderValue::from_str(&sig).unwrap());
Ok(())
}
}
/// All of the errors that can take place while attempting to sign a request
#[derive(Debug, Error)]
pub enum SigningError {
#[error("Unable to handle the URL as the format was invalid: {0}")]
UrlEncodingError(std::string::FromUtf8Error),
}
impl From<mauth_core::error::Error> for SigningError {
fn from(err: mauth_core::error::Error) -> SigningError {
match err {
mauth_core::error::Error::UrlEncodingError(url_err) => {
SigningError::UrlEncodingError(url_err)
}
_ => panic!("should not be possible to get this error type from signing a request"),
}
}
}