1use std::{collections::HashMap, str::FromStr};
2
3use chrono::Utc;
4use jsonwebtoken::{crypto::sign, Algorithm, EncodingKey};
5use reqwest::{
6 header::{HeaderName, HeaderValue},
7 Method, Request, Url,
8};
9use serde::{Deserialize, Serialize};
10
11use crate::{Error, Result};
12
13#[derive(Debug, Deserialize, Serialize)]
14pub struct Signature {
15 pub access_key: String,
16 pub algorithm: String,
17 pub request_time: String,
18 pub sign: String,
19 pub signed_headers: String,
20}
21
22#[derive(Debug, Serialize, Ord, Eq, PartialOrd, PartialEq)]
23struct SignHeader {
24 pub key: String,
25 pub value: String,
26}
27
28pub fn signature(
29 value: &mut Signature,
30 sign_headers: HashMap<String, String>,
31 secret_access_key: String,
32 seconds_offset: u64,
33) -> Result<String> {
34 let request_time = value.request_time.parse::<i64>().map_err(Error::any)?;
36 let now = Utc::now().timestamp();
37 if now.abs_diff(request_time).gt(&seconds_offset) {
38 return Err(Error::Invalid("time span exceeds threshold".to_string()));
39 }
40 let algorithm = Algorithm::from_str(&value.algorithm).map_err(Error::any)?;
42
43 value.sign = Default::default();
45 let message = serde_json::to_string(&value).map_err(Error::any)?;
46 let mut sort_headers = Vec::new();
48 for h in value
49 .signed_headers
50 .split(';')
51 .collect::<Vec<&str>>()
52 .iter()
53 {
54 match sign_headers.get_key_value(*h) {
55 Some((k, v)) => sort_headers.push(SignHeader {
56 key: k.to_owned(),
57 value: v.to_owned(),
58 }),
59 None => return Err(Error::Invalid(format!("lack {}", *h))),
60 }
61 }
62 sort_headers.sort();
63 let signature = serde_json::to_string(&sort_headers).map_err(Error::any)?;
64 let sign_result = sign(
65 [message, signature].join(".").as_bytes(),
66 &EncodingKey::from_secret(secret_access_key.as_bytes()),
67 algorithm,
68 )
69 .map_err(Error::any)?;
70 Ok(sign_result)
71}
72
73pub fn query(
74 value: &mut Signature,
75 sign_headers: HashMap<String, String>,
76 secret_access_key: String,
77 seconds_offset: u64,
78) -> Result<String> {
79 value.sign = signature(
80 value,
81 sign_headers.clone(),
82 secret_access_key,
83 seconds_offset,
84 )?;
85 let prefix = serde_urlencoded::to_string(value).map_err(Error::any)?;
86 let suffix = serde_urlencoded::to_string(sign_headers).map_err(Error::any)?;
87 Ok([prefix, suffix].join("&"))
88}
89
90pub fn request(
91 method: Method,
92 url: Url,
93 value: &mut Signature,
94 sign_headers: HashMap<String, String>,
95 secret_access_key: String,
96) -> Result<Request> {
97 value.sign = signature(value, sign_headers.clone(), secret_access_key, 15)?;
99 let mut req = Request::new(method, url);
100
101 req.headers_mut().append(
102 HeaderName::from_static("access_key"),
103 HeaderValue::from_str(&value.access_key).map_err(Error::any)?,
104 );
105 req.headers_mut().append(
106 HeaderName::from_static("algorithm"),
107 HeaderValue::from_str(&value.algorithm).map_err(Error::any)?,
108 );
109 req.headers_mut().append(
110 HeaderName::from_static("request_time"),
111 HeaderValue::from_str(&value.request_time).map_err(Error::any)?,
112 );
113 req.headers_mut().append(
114 HeaderName::from_static("sign"),
115 HeaderValue::from_str(&value.sign).map_err(Error::any)?,
116 );
117 req.headers_mut().append(
118 HeaderName::from_static("signed_headers"),
119 HeaderValue::from_str(&value.signed_headers).map_err(Error::any)?,
120 );
121 for (k, v) in sign_headers.iter() {
122 req.headers_mut().append(
123 HeaderName::from_str(k).map_err(Error::any)?,
124 HeaderValue::from_str(v.as_str()).map_err(Error::any)?,
125 );
126 }
127 Ok(req)
128}