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
use base64::Engine;
use base64::engine::general_purpose;
use hmac::{Hmac, Mac};
use reqwest::header::DATE;
use tracing::debug;
use crate::oss::{API, OSS, OSSInfo};
use crate::request::{RequestBuilder};

pub trait AuthAPI {
    fn sign<S: AsRef<str>>(
        &self,
        object: S,
        build: &RequestBuilder,
    ) -> String;

    fn oss_sign<S: AsRef<str>>(
        &self,
        object: S,
        build: &RequestBuilder,
    ) -> String;
}

impl<'a> AuthAPI for OSS {
    fn sign<S: AsRef<str>>(
        &self,
        key: S,
        build: &RequestBuilder,
    ) -> String {
        let date = build
            .headers
            .get(&DATE.to_string())
            .expect("Date header is required");
        let mut oss_headers = build
            .oss_headers
            .iter()
            .map(|(k, v)| (k.to_lowercase(), v))
            .collect::<Vec<_>>();

        oss_headers.sort_by(|a, b| a.0.cmp(&b.0));

        let mut canonicalized_oss_headers = oss_headers
            .iter()
            .map(|(k, v)| format!("{}:{}", k, v))
            .collect::<Vec<_>>()
            .join("\n");

        if oss_headers.len() == 1 {
            canonicalized_oss_headers = format!("{}\n", canonicalized_oss_headers);
        }

        let mut canonicalized_resource = self.format_oss_resource_str(self.bucket().as_str(), key.as_ref());
        if build.parameters.len() > 0 {
            let mut params = build
                .parameters
                .iter()
                .collect::<Vec<_>>();
            params.sort_by(|a, b| a.0.cmp(&b.0));
            canonicalized_resource = format!(
                "{}?{}",
                canonicalized_resource,
                params
                    .into_iter()
                    .map(|(k, v)| format!("{}={}", k, v))
                    .collect::<Vec<_>>()
                    .join("&")
            );
        }
        let verb = build.method.to_string();
        let content_md5 = build.content_md5.clone().unwrap_or_default();
        let content_type = build.content_type.clone().unwrap_or_default();
        let sign_str = format!(
            "{}\n{}\n{}\n{}\n{}{}",
            verb,
            content_md5,
            content_type,
            date,
            canonicalized_oss_headers,
            canonicalized_resource,
        );
        debug!("sign_str: {}", sign_str);
        let mut hasher: Hmac<sha1::Sha1> = Hmac::new_from_slice(self.key_secret().as_bytes()).unwrap();
        hasher.update(sign_str.as_bytes());

        general_purpose::STANDARD.encode(&hasher.finalize().into_bytes())
    }

    fn oss_sign<S: AsRef<str>>(&self, object: S, build: &RequestBuilder) -> String {
        let sign_str_base64 = self.sign(object, build);
        format!("OSS {}:{}", self.key_id(), sign_str_base64)
    }
}