oss_sdk_rs/
auth.rs

1//! Copyright The NoXF/oss-rust-sdk Authors
2//! Copyright The iFREEGROUP/oss-sdk-rs Contributors
3use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
4use reqwest::header::{CONTENT_TYPE, DATE};
5
6use base64::{ engine::general_purpose, Engine};
7use hmac::{Hmac, Mac};
8
9type HmacSha1 = Hmac<sha1::Sha1>;
10
11use crate::errors::OSSError;
12
13use super::oss::OSS;
14
15pub trait Auth {
16    fn oss_sign(
17        &self,
18        verb: &str,
19        bucket: &str,
20        object: &str,
21        oss_resources: &str,
22        headers: &HeaderMap,
23    ) -> Result<String, OSSError>;
24
25    fn sign_content(&self, content: &str) -> Result<String, OSSError>;
26}
27
28impl<'a> Auth for OSS<'a> {
29    fn oss_sign(
30        &self,
31        verb: &str,
32        bucket: &str,
33        object: &str,
34        oss_resources: &str,
35        headers: &HeaderMap,
36    ) -> Result<String, OSSError> {
37        let date = headers
38            .get(DATE)
39            .map(|d| d.to_str().unwrap_or_default())
40            .unwrap_or_default();
41        let content_type = headers
42            .get(CONTENT_TYPE)
43            .map(|c| c.to_str().unwrap_or_default())
44            .unwrap_or_default();
45        let content_md5 = headers
46            .get("Content-MD5")
47            .map(|md5| general_purpose::STANDARD.encode(md5.to_str().unwrap_or_default()))
48            .unwrap_or_default();
49
50        let mut oss_headers: Vec<(&HeaderName, &HeaderValue)> = headers
51            .iter()
52            .filter(|(k, _)| k.as_str().contains("x-oss-"))
53            .collect();
54        oss_headers.sort_by(|a, b| a.0.to_string().cmp(&b.0.to_string()));
55        let mut oss_headers_str = String::new();
56        for (k, v) in oss_headers {
57            oss_headers_str += &format!(
58                "{}:{}\n",
59                k.to_owned().as_str(),
60                v.to_owned().to_str().unwrap_or("")
61            );
62        }
63
64        let oss_resource_str = get_oss_resource_str(bucket, object, oss_resources);
65        let sign_str = format!(
66            "{}\n{}\n{}\n{}\n{}{}",
67            verb, content_md5, content_type, date, oss_headers_str, oss_resource_str
68        );
69
70        self.sign_content(sign_str.as_str())
71    }
72
73    fn sign_content(&self, content: &str) -> Result<String, OSSError> {
74        let mut hasher =
75            HmacSha1::new_from_slice(self.key_secret().as_bytes()).map_err(OSSError::Sign)?;
76        hasher.update(content.as_bytes());
77
78        let sign_str_base64 = general_purpose::STANDARD.encode(hasher.finalize().into_bytes());
79
80        let authorization = format!("OSS {}:{}", self.key_id(), sign_str_base64);
81        debug!("authorization: {}", authorization);
82        Ok(authorization)
83    }
84}
85
86#[inline]
87fn get_oss_resource_str(bucket: &str, object: &str, oss_resources: &str) -> String {
88    let oss_resources = if !oss_resources.is_empty() {
89        String::from("?") + oss_resources
90    } else {
91        String::new()
92    };
93    if bucket.is_empty() {
94        format!("/{}{}", bucket, oss_resources)
95    } else {
96        format!("/{}/{}{}", bucket, object, oss_resources)
97    }
98}