Skip to main content

ve_tos_rust_sdk/
auth.rs

1/*
2 * Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::config::ConfigHolder;
18use crate::constant::{HEADER_AUTHORIZATION, HEADER_CONTENT_SHA256, HEADER_CONTENT_TYPE_LOWER, HEADER_HOST, HEADER_HOST_LOWER, HEADER_PREFIX, HEADER_REQUEST_DATE, HEADER_SECURITY_TOKEN, ISO8601_DATE_FORMAT, LONG_DATE_FORMAT, NOT_ALLOWED_REQUEST_HEADER, QUERY_ALGORITHM, QUERY_CREDENTIAL, QUERY_DATE, QUERY_EXPIRES, QUERY_POLICY, QUERY_SECURITY_TOKEN, QUERY_SIGNATURE, QUERY_SIGNED_HEADERS};
19use crate::enumeration::HttpMethodType;
20use crate::error::{GenericError, TosError};
21use crate::http::HttpRequest;
22use crate::internal::{base64, check_bucket, hex, hex_sha256, hmac_sha256, url_encode, url_encode_with_safe, AdditionalContext};
23use arc_swap::ArcSwap;
24use chrono::{DateTime, TimeDelta, Utc};
25use std::collections::HashMap;
26use std::ops::Add;
27use std::sync::Arc;
28use tracing::log::debug;
29
30pub trait SignerAPI {
31    fn pre_signed_url(&self, input: &PreSignedURLInput) -> Result<PreSignedURLOutput, TosError>;
32    fn pre_signed_post_signature(&self, input: &PreSignedPostSignatureInput) -> Result<PreSignedPostSignatureOutput, TosError>;
33    fn pre_signed_policy_url(&self, input: &PreSignedPolicyURLInput) -> Result<PreSignedPolicyURLOutput, TosError>;
34}
35
36#[derive(Debug, Clone, PartialEq, Default)]
37pub struct PreSignedURLInput {
38    pub(crate) http_method: HttpMethodType,
39    pub(crate) bucket: String,
40    pub(crate) key: String,
41    pub(crate) expires: i64,
42    pub(crate) header: HashMap<String, String>,
43    pub(crate) query: HashMap<String, String>,
44    pub(crate) alternative_endpoint: String,
45    pub(crate) is_custom_domain: Option<bool>,
46    pub(crate) is_signed_all_headers: bool,
47}
48
49impl PreSignedURLInput {
50    pub fn new(bucket: impl Into<String>) -> Self {
51        Self {
52            http_method: Default::default(),
53            bucket: bucket.into(),
54            key: "".to_string(),
55            expires: 3600,
56            header: Default::default(),
57            query: Default::default(),
58            alternative_endpoint: "".to_string(),
59            is_custom_domain: None,
60            is_signed_all_headers: false,
61        }
62    }
63
64    pub fn new_with_key(bucket: impl Into<String>, key: impl Into<String>) -> Self {
65        Self {
66            http_method: Default::default(),
67            bucket: bucket.into(),
68            key: key.into(),
69            expires: 3600,
70            header: Default::default(),
71            query: Default::default(),
72            alternative_endpoint: "".to_string(),
73            is_custom_domain: None,
74            is_signed_all_headers: false,
75        }
76    }
77
78    pub fn http_method(&self) -> &HttpMethodType {
79        &self.http_method
80    }
81
82    pub fn bucket(&self) -> &str {
83        &self.bucket
84    }
85
86    pub fn key(&self) -> &str {
87        &self.key
88    }
89
90    pub fn expires(&self) -> i64 {
91        self.expires
92    }
93
94    pub fn header(&self) -> &HashMap<String, String> {
95        &self.header
96    }
97
98    pub fn query(&self) -> &HashMap<String, String> {
99        &self.query
100    }
101
102    pub fn alternative_endpoint(&self) -> &str {
103        &self.alternative_endpoint
104    }
105
106    pub fn is_custom_domain(&self) -> Option<bool> {
107        self.is_custom_domain
108    }
109
110    pub fn is_signed_all_headers(&self) -> bool {
111        self.is_signed_all_headers
112    }
113
114    pub fn set_http_method(&mut self, http_method: impl Into<HttpMethodType>) {
115        self.http_method = http_method.into();
116    }
117
118    pub fn set_bucket(&mut self, bucket: impl Into<String>) {
119        self.bucket = bucket.into();
120    }
121
122    pub fn set_key(&mut self, key: impl Into<String>) {
123        self.key = key.into();
124    }
125
126    pub fn set_expires(&mut self, expires: i64) {
127        self.expires = expires;
128    }
129
130    pub fn set_header(&mut self, header: impl Into<HashMap<String, String>>) {
131        self.header = header.into();
132    }
133
134    pub fn set_query(&mut self, query: impl Into<HashMap<String, String>>) {
135        self.query = query.into();
136    }
137
138    pub fn set_alternative_endpoint(&mut self, alternative_endpoint: impl Into<String>) {
139        self.alternative_endpoint = alternative_endpoint.into();
140    }
141
142    pub fn set_is_custom_domain(&mut self, is_custom_domain: impl Into<bool>) {
143        self.is_custom_domain = Some(is_custom_domain.into());
144    }
145
146    pub fn set_is_signed_all_headers(&mut self, is_signed_all_headers: bool) {
147        self.is_signed_all_headers = is_signed_all_headers;
148    }
149}
150
151#[derive(Debug, Clone, PartialEq, Default)]
152pub struct PreSignedURLOutput {
153    pub(crate) signed_url: String,
154    pub(crate) signed_header: HashMap<String, String>,
155}
156
157impl PreSignedURLOutput {
158    pub fn signed_url(&self) -> &str {
159        &self.signed_url
160    }
161
162    pub fn signed_header(&self) -> &HashMap<String, String> {
163        &self.signed_header
164    }
165}
166
167#[derive(Debug, Clone, PartialEq, Default)]
168pub struct PreSignedPostSignatureInput {
169    pub(crate) bucket: String,
170    pub(crate) key: String,
171    pub(crate) expires: i64,
172    pub(crate) conditions: Vec<PostSignatureCondition>,
173    pub(crate) content_length_range: Option<ContentLengthRange>,
174    pub(crate) multi_values_conditions: Vec<PostSignatureMultiValuesCondition>,
175}
176impl PreSignedPostSignatureInput {
177    pub fn new(bucket: impl Into<String>) -> Self {
178        Self {
179            bucket: bucket.into(),
180            key: "".to_string(),
181            expires: 3600,
182            conditions: vec![],
183            content_length_range: None,
184            multi_values_conditions: vec![],
185        }
186    }
187    pub fn new_with_key(bucket: impl Into<String>, key: impl Into<String>) -> Self {
188        Self {
189            bucket: bucket.into(),
190            key: key.into(),
191            expires: 3600,
192            conditions: vec![],
193            content_length_range: None,
194            multi_values_conditions: vec![],
195        }
196    }
197
198    pub fn bucket(&self) -> &str {
199        &self.bucket
200    }
201
202    pub fn key(&self) -> &str {
203        &self.key
204    }
205
206    pub fn expires(&self) -> i64 {
207        self.expires
208    }
209
210    pub fn conditions(&self) -> &Vec<PostSignatureCondition> {
211        &self.conditions
212    }
213
214    pub fn content_length_range(&self) -> &Option<ContentLengthRange> {
215        &self.content_length_range
216    }
217
218    pub fn multi_values_conditions(&self) -> &Vec<PostSignatureMultiValuesCondition> {
219        &self.multi_values_conditions
220    }
221
222    pub fn set_bucket(&mut self, bucket: impl Into<String>) {
223        self.bucket = bucket.into();
224    }
225
226    pub fn set_key(&mut self, key: impl Into<String>) {
227        self.key = key.into();
228    }
229
230    pub fn set_expires(&mut self, expires: i64) {
231        self.expires = expires;
232    }
233
234    pub fn set_conditions(&mut self, conditions: impl Into<Vec<PostSignatureCondition>>) {
235        self.conditions = conditions.into();
236    }
237
238    pub fn add_condition(&mut self, condition: impl Into<PostSignatureCondition>) {
239        self.conditions.push(condition.into());
240    }
241
242    pub fn set_content_length_range(&mut self, content_length_range: impl Into<ContentLengthRange>) {
243        self.content_length_range = Some(content_length_range.into());
244    }
245
246    pub fn set_multi_values_conditions(&mut self, multi_values_conditions: impl Into<Vec<PostSignatureMultiValuesCondition>>) {
247        self.multi_values_conditions = multi_values_conditions.into();
248    }
249
250    pub fn add_multi_values_condition(&mut self, multi_values_condition: impl Into<PostSignatureMultiValuesCondition>) {
251        self.multi_values_conditions.push(multi_values_condition.into());
252    }
253}
254
255#[derive(Debug, Clone, PartialEq, Default)]
256pub struct PostSignatureCondition {
257    pub(crate) key: String,
258    pub(crate) value: String,
259    pub(crate) operator: Option<String>,
260}
261
262impl PostSignatureCondition {
263    pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
264        Self {
265            key: key.into(),
266            value: value.into(),
267            operator: None,
268        }
269    }
270
271    pub fn new_with_operator(key: impl Into<String>, value: impl Into<String>, operator: impl Into<String>) -> Self {
272        Self {
273            key: key.into(),
274            value: value.into(),
275            operator: Some(operator.into()),
276        }
277    }
278
279    pub(crate) fn to_serde_json_value(&self) -> serde_json::Value {
280        match &self.operator {
281            None => {
282                let mut m = serde_json::Map::with_capacity(2);
283                m.insert(self.key.clone(), serde_json::Value::String(self.value.clone()));
284                serde_json::Value::Object(m)
285            }
286            Some(operator) => {
287                let mut v = Vec::with_capacity(3);
288                v.push(serde_json::Value::String(operator.clone()));
289                if !&self.key.starts_with("$") {
290                    v.push(serde_json::Value::String(String::from("$") + &self.key));
291                } else {
292                    v.push(serde_json::Value::String(self.key.clone()));
293                }
294                v.push(serde_json::Value::String(self.value.clone()));
295                serde_json::Value::Array(v)
296            }
297        }
298    }
299
300    pub fn key(&self) -> &str {
301        &self.key
302    }
303
304    pub fn value(&self) -> &str {
305        &self.value
306    }
307
308    pub fn operator(&self) -> &Option<String> {
309        &self.operator
310    }
311
312    pub fn set_key(&mut self, key: impl Into<String>) {
313        self.key = key.into();
314    }
315
316    pub fn set_value(&mut self, value: impl Into<String>) {
317        self.value = value.into();
318    }
319
320    pub fn set_operator(&mut self, operator: impl Into<String>) {
321        self.operator = Some(operator.into());
322    }
323}
324#[derive(Debug, Clone, PartialEq, Default)]
325pub struct ContentLengthRange {
326    pub(crate) range_start: i64,
327    pub(crate) range_end: i64,
328}
329impl ContentLengthRange {
330    pub fn new(range_start: i64, range_end: i64) -> Self {
331        Self {
332            range_start,
333            range_end,
334        }
335    }
336
337    pub fn range_start(&self) -> i64 {
338        self.range_start
339    }
340
341    pub fn range_end(&self) -> i64 {
342        self.range_end
343    }
344
345    pub fn set_range_start(&mut self, range_start: i64) {
346        self.range_start = range_start;
347    }
348
349    pub fn set_range_end(&mut self, range_end: i64) {
350        self.range_end = range_end;
351    }
352}
353#[derive(Debug, Clone, PartialEq, Default)]
354pub struct PostSignatureMultiValuesCondition {
355    pub(crate) key: String,
356    pub(crate) values: Vec<String>,
357    pub(crate) operator: String,
358}
359
360impl PostSignatureMultiValuesCondition {
361    pub fn new(key: impl Into<String>, values: impl Into<Vec<String>>, operator: impl Into<String>) -> Self {
362        Self {
363            key: key.into(),
364            values: values.into(),
365            operator: operator.into(),
366        }
367    }
368
369    pub(crate) fn to_serde_json_value(&self) -> serde_json::Value {
370        let mut v = Vec::with_capacity(3);
371        v.push(serde_json::Value::String(self.operator.clone()));
372        if !&self.key.starts_with("$") {
373            v.push(serde_json::Value::String(String::from("$") + &self.key));
374        } else {
375            v.push(serde_json::Value::String(self.key.clone()));
376        }
377        let mut values = Vec::with_capacity(self.values.len());
378        for value in self.values.iter() {
379            values.push(serde_json::Value::String(value.clone()));
380        }
381        v.push(serde_json::Value::Array(values));
382        serde_json::Value::Array(v)
383    }
384
385    pub fn key(&self) -> &str {
386        &self.key
387    }
388
389    pub fn values(&self) -> &Vec<String> {
390        &self.values
391    }
392
393    pub fn operator(&self) -> &str {
394        &self.operator
395    }
396
397    pub fn set_key(&mut self, key: impl Into<String>) {
398        self.key = key.into();
399    }
400
401    pub fn set_values(&mut self, values: impl Into<Vec<String>>) {
402        self.values = values.into();
403    }
404
405    pub fn set_operator(&mut self, operator: impl Into<String>) {
406        self.operator = operator.into();
407    }
408}
409
410#[derive(Debug, Clone, PartialEq, Default)]
411pub struct PreSignedPostSignatureOutput {
412    pub(crate) origin_policy: String,
413    pub(crate) policy: String,
414    pub(crate) algorithm: String,
415    pub(crate) credential: String,
416    pub(crate) date: String,
417    pub(crate) signature: String,
418}
419
420impl PreSignedPostSignatureOutput {
421    pub fn origin_policy(&self) -> &str {
422        &self.origin_policy
423    }
424
425    pub fn policy(&self) -> &str {
426        &self.policy
427    }
428
429    pub fn algorithm(&self) -> &str {
430        &self.algorithm
431    }
432
433    pub fn credential(&self) -> &str {
434        &self.credential
435    }
436
437    pub fn date(&self) -> &str {
438        &self.date
439    }
440
441    pub fn signature(&self) -> &str {
442        &self.signature
443    }
444}
445
446#[derive(Debug, Clone, PartialEq, Default)]
447pub struct PreSignedPolicyURLInput {
448    pub(crate) bucket: String,
449    pub(crate) expires: i64,
450    pub(crate) conditions: Vec<PolicySignatureCondition>,
451    pub(crate) alternative_endpoint: String,
452    pub(crate) is_custom_domain: Option<bool>,
453}
454impl PreSignedPolicyURLInput {
455    pub fn new(bucket: impl Into<String>) -> Self {
456        Self {
457            bucket: bucket.into(),
458            expires: 3600,
459            conditions: vec![],
460            alternative_endpoint: "".to_string(),
461            is_custom_domain: None,
462        }
463    }
464
465    pub fn bucket(&self) -> &str {
466        &self.bucket
467    }
468
469    pub fn expires(&self) -> i64 {
470        self.expires
471    }
472
473    pub fn conditions(&self) -> &Vec<PolicySignatureCondition> {
474        &self.conditions
475    }
476
477    pub fn alternative_endpoint(&self) -> &str {
478        &self.alternative_endpoint
479    }
480
481    pub fn is_custom_domain(&self) -> Option<bool> {
482        self.is_custom_domain
483    }
484
485    pub fn set_bucket(&mut self, bucket: impl Into<String>) {
486        self.bucket = bucket.into();
487    }
488
489    pub fn set_expires(&mut self, expires: i64) {
490        self.expires = expires;
491    }
492
493    pub fn add_condition(&mut self, condition: impl Into<PolicySignatureCondition>) {
494        self.conditions.push(condition.into());
495    }
496
497    pub fn set_conditions(&mut self, conditions: impl Into<Vec<PolicySignatureCondition>>) {
498        self.conditions = conditions.into();
499    }
500
501    pub fn set_alternative_endpoint(&mut self, alternative_endpoint: impl Into<String>) {
502        self.alternative_endpoint = alternative_endpoint.into();
503    }
504
505    pub fn set_is_custom_domain(&mut self, is_custom_domain: bool) {
506        self.is_custom_domain = Some(is_custom_domain);
507    }
508}
509
510#[derive(Debug, Clone, PartialEq, Default)]
511pub struct PolicySignatureCondition {
512    pub(crate) key: String,
513    pub(crate) value: String,
514    pub(crate) operator: String,
515}
516impl PolicySignatureCondition {
517    pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
518        Self {
519            key: key.into(),
520            value: value.into(),
521            operator: "".to_string(),
522        }
523    }
524
525    pub fn new_with_operator(key: impl Into<String>, value: impl Into<String>, operator: impl Into<String>) -> Self {
526        Self {
527            key: key.into(),
528            value: value.into(),
529            operator: operator.into(),
530        }
531    }
532
533    pub fn key(&self) -> &str {
534        &self.key
535    }
536
537    pub fn value(&self) -> &str {
538        &self.value
539    }
540
541    pub fn operator(&self) -> &str {
542        &self.operator
543    }
544
545    pub fn set_key(&mut self, key: impl Into<String>) {
546        self.key = key.into();
547    }
548
549    pub fn set_value(&mut self, value: impl Into<String>) {
550        self.value = value.into();
551    }
552
553    pub fn set_operator(&mut self, operator: impl Into<String>) {
554        self.operator = operator.into();
555    }
556}
557#[derive(Debug, Clone, Default)]
558pub struct PreSignedPolicyURLOutput {
559    pub(crate) signed_query: String,
560    pub(crate) config_holder: Arc<ConfigHolder>,
561    pub(crate) is_custom_domain: bool,
562    pub(crate) bucket: String,
563    pub(crate) domain: String,
564    pub(crate) schema: String,
565}
566
567impl PreSignedPolicyURLOutput {
568    pub fn signed_query(&self) -> &str {
569        &self.signed_query
570    }
571
572    pub fn get_signed_url_for_list(&self, additional_query: Option<HashMap<impl AsRef<str>, impl AsRef<str>>>) -> String {
573        let mut result = self.config_holder.get_endpoint_with_domain(&self.bucket, "", &self.schema, &self.domain, false, self.is_custom_domain);
574        result.push('?');
575        result.push_str(&self.signed_query);
576        if let Some(additional_query) = additional_query {
577            for (key, value) in additional_query.iter() {
578                result.push('&');
579                result.push_str(key.as_ref());
580                result.push('=');
581                result.push_str(value.as_ref());
582            }
583        }
584        result
585    }
586    pub fn get_signed_url_for_get_or_head(&self, key: impl AsRef<str>, additional_query: Option<HashMap<impl AsRef<str>, impl AsRef<str>>>) -> String {
587        let mut result = self.config_holder.get_endpoint_with_domain(&self.bucket, key.as_ref(), &self.schema, &self.domain, true, self.is_custom_domain);
588        result.push('?');
589        result.push_str(&self.signed_query);
590        if let Some(additional_query) = additional_query {
591            for (key, value) in additional_query.iter() {
592                result.push('&');
593                result.push_str(key.as_ref());
594                result.push('=');
595                result.push_str(value.as_ref());
596            }
597        }
598        result
599    }
600}
601
602pub(crate) fn pre_signed_policy_url(config_holder: &ArcSwap<ConfigHolder>, ak: &str, sk: &str, security_token: &str, input: &PreSignedPolicyURLInput) -> Result<PreSignedPolicyURLOutput, TosError> {
603    let config_holder = config_holder.load();
604    if input.conditions().len() == 0 {
605        return Err(TosError::client_error("empty conditions"));
606    }
607
608    let mut is_custom_domain = config_holder.is_custom_domain;
609    if let Some(x) = input.is_custom_domain() {
610        is_custom_domain = x;
611    }
612    let bucket = input.bucket().trim();
613    if !is_custom_domain {
614        check_bucket(bucket)?;
615    }
616    let schema;
617    let domain;
618    if input.alternative_endpoint() != "" {
619        (schema, domain, _) = config_holder.split_endpoint(input.alternative_endpoint())?;
620    } else {
621        schema = "".to_string();
622        domain = "".to_string();
623    }
624
625    let is_anonymous = ak == "" || sk == "";
626    let mut conditions = Vec::<serde_json::Value>::with_capacity(input.conditions().len() + 1);
627    conditions.push(PostSignatureCondition::new("bucket", input.bucket()).to_serde_json_value());
628    let (long_date, short_date, credential_scope) = calc_date_and_credential_scope(&config_holder.region, None);
629    for condition in input.conditions() {
630        if condition.key() != "key" && condition.key() != "$key" {
631            return Err(TosError::client_error("condition key must be 'key'"));
632        }
633        conditions.push(PostSignatureCondition::new_with_operator(condition.key(), condition.value(), condition.operator()).to_serde_json_value());
634    }
635
636    let mut query = HashMap::with_capacity(7);
637    query.insert(QUERY_ALGORITHM, ALGORITHM.to_string());
638    query.insert(QUERY_DATE, long_date.clone());
639    query.insert(QUERY_EXPIRES, input.expires().to_string());
640    if !is_anonymous {
641        query.insert(QUERY_CREDENTIAL, format!("{}/{}", ak, credential_scope));
642        if security_token != "" {
643            query.insert(QUERY_SECURITY_TOKEN, security_token.to_string());
644        }
645    }
646    match serde_json::to_string(&HashMap::from([("conditions", serde_json::Value::Array(conditions))])) {
647        Err(ex) => Err(TosError::client_error_with_cause("trans json error",
648                                                         GenericError::JsonError(ex.to_string()))),
649        Ok(origin_policy) => {
650            let policy = base64(origin_policy.as_str());
651            query.insert(QUERY_POLICY, policy);
652            let mut query = Some(query);
653            if !is_anonymous {
654                let canonical_request = calc_canonical_request("", &mut query, "", "", "", UNSIGNED_PAYLOAD);
655                let string_to_sign = calc_string_to_sign(&long_date, &credential_scope, &canonical_request);
656                let signature = calc_signature(&string_to_sign, &short_date, &config_holder.region, sk)?;
657                query.as_mut().unwrap().insert(QUERY_SIGNATURE, signature);
658            }
659            let mut signed_query = String::new();
660            if let Some(query) = query.as_ref() {
661                for (idx, kv) in query.iter().enumerate() {
662                    signed_query.push_str(url_encode(*kv.0).as_str());
663                    signed_query.push('=');
664                    signed_query.push_str(kv.1);
665                    if idx != query.len() - 1 {
666                        signed_query.push('&');
667                    }
668                }
669            }
670            Ok(PreSignedPolicyURLOutput {
671                signed_query,
672                config_holder: config_holder.clone(),
673                is_custom_domain,
674                bucket: bucket.to_string(),
675                domain,
676                schema,
677            })
678        }
679    }
680}
681
682pub(crate) fn pre_signed_post_signature(config_holder: &ArcSwap<ConfigHolder>, ak: &str, sk: &str, security_token: &str, input: &PreSignedPostSignatureInput) -> Result<PreSignedPostSignatureOutput, TosError> {
683    let config_holder = config_holder.load();
684    let (long_date, short_date, mut credential) = calc_date_and_credential(ak, &config_holder.region);
685    let mut conditions = Vec::<serde_json::Value>::with_capacity(7 + input.conditions().len() + input.multi_values_conditions().len());
686    conditions.push(PostSignatureCondition::new(QUERY_ALGORITHM, ALGORITHM).to_serde_json_value());
687    conditions.push(PostSignatureCondition::new(QUERY_DATE, long_date.clone()).to_serde_json_value());
688    let is_anonymous = ak == "" || sk == "";
689    if !is_anonymous {
690        conditions.push(PostSignatureCondition::new(QUERY_CREDENTIAL, credential.clone()).to_serde_json_value());
691        if security_token != "" {
692            conditions.push(PostSignatureCondition::new(QUERY_SECURITY_TOKEN, security_token).to_serde_json_value());
693        }
694    }
695
696    let bucket = input.bucket().trim();
697    if bucket != "" {
698        check_bucket(bucket)?;
699        conditions.push(PostSignatureCondition::new("bucket", bucket).to_serde_json_value());
700    }
701    let key = input.key();
702    if key != "" {
703        conditions.push(PostSignatureCondition::new("key", key).to_serde_json_value());
704    }
705    if input.conditions().len() > 0 {
706        for condition in input.conditions() {
707            conditions.push(condition.to_serde_json_value());
708        }
709    }
710    if let Some(content_length_range) = &input.content_length_range {
711        let mut condition = PostSignatureCondition::new(content_length_range.range_start.to_string(), content_length_range.range_end.to_string());
712        condition.set_operator("content-length-range");
713        conditions.push(condition.to_serde_json_value());
714    }
715    if input.multi_values_conditions().len() > 0 {
716        for condition in input.multi_values_conditions() {
717            conditions.push(condition.to_serde_json_value());
718        }
719    }
720    let expiration = Utc::now().add(TimeDelta::new(input.expires(), 0).unwrap()).format(ISO8601_DATE_FORMAT).to_string();
721    let mut origin_policy = HashMap::with_capacity(2);
722    origin_policy.insert("expiration", serde_json::Value::String(expiration));
723    origin_policy.insert("conditions", serde_json::Value::Array(conditions));
724
725    match serde_json::to_string(&origin_policy) {
726        Err(ex) => Err(TosError::client_error_with_cause("trans json error",
727                                                         GenericError::JsonError(ex.to_string()))),
728        Ok(result) => {
729            let origin_policy = result;
730            let policy = base64(origin_policy.as_str());
731            let mut signature = String::new();
732            if is_anonymous {
733                credential = String::new();
734            } else {
735                signature = calc_signature(&policy, &short_date, &config_holder.region, sk)?;
736            }
737            Ok(PreSignedPostSignatureOutput {
738                origin_policy,
739                policy,
740                algorithm: ALGORITHM.to_string(),
741                credential,
742                date: long_date,
743                signature,
744            })
745        }
746    }
747}
748pub(crate) fn pre_signed_url(config_holder: &ArcSwap<ConfigHolder>, ak: &str, sk: &str, security_token: &str, input: &PreSignedURLInput) -> Result<PreSignedURLOutput, TosError> {
749    let config_holder = config_holder.load();
750    let mut is_custom_domain = config_holder.is_custom_domain;
751    if let Some(x) = input.is_custom_domain() {
752        is_custom_domain = x;
753    }
754    let bucket = input.bucket().trim();
755    if !is_custom_domain {
756        check_bucket(bucket)?;
757    }
758    let schema;
759    let domain;
760    if input.alternative_endpoint() != "" {
761        (schema, domain, _) = config_holder.split_endpoint(input.alternative_endpoint())?;
762    } else {
763        schema = "".to_string();
764        domain = "".to_string();
765    }
766
767    let is_anonymous = ak == "" || sk == "";
768    if is_anonymous {
769        let signed_url = config_holder.get_endpoint_with_domain(bucket, input.key(), &schema, &domain, true, is_custom_domain);
770        return Ok(PreSignedURLOutput {
771            signed_url,
772            signed_header: HashMap::default(),
773        });
774    }
775
776    let (long_date, short_date, credential_scope) = calc_date_and_credential_scope(&config_holder.region, None);
777
778    let mut signed_header = HashMap::with_capacity(input.header.len() + 1);
779    for (key, value) in &input.header {
780        signed_header.insert(key.clone(), value.to_string());
781    }
782
783    signed_header.insert(HEADER_HOST.to_string(), config_holder.get_host_with_domain(bucket, &domain, is_custom_domain));
784    let (canonical_headers, signed_headers, mut content_sha256) = calc_canonical_headers(&signed_header, &None, input.is_signed_all_headers);
785
786    let mut query = HashMap::with_capacity(input.query.len() + 7);
787    for (key, value) in &input.query {
788        query.insert(key.as_str(), value.to_string());
789    }
790    let mut expires = input.expires;
791    if expires <= 0 {
792        expires = 3600;
793    }
794    query.insert(QUERY_ALGORITHM, ALGORITHM.to_string());
795    query.insert(QUERY_CREDENTIAL, format!("{}/{}", ak, credential_scope));
796    query.insert(QUERY_DATE, long_date.clone());
797    query.insert(QUERY_EXPIRES, expires.to_string());
798    query.insert(QUERY_SIGNED_HEADERS, signed_headers.clone());
799    if security_token != "" {
800        query.insert(QUERY_SECURITY_TOKEN, security_token.to_string());
801    }
802    if content_sha256 == "" {
803        content_sha256 = UNSIGNED_PAYLOAD.to_string();
804    }
805
806    let mut query = Some(query);
807    let canonical_request = calc_canonical_request(input.http_method.as_str(), &mut query, input.key(), &canonical_headers, &signed_headers, content_sha256.as_str());
808    let string_to_sign = calc_string_to_sign(&long_date, &credential_scope, &canonical_request);
809    let signature = calc_signature(&string_to_sign, &short_date, &config_holder.region, sk)?;
810    query.as_mut().unwrap().insert(QUERY_SIGNATURE, signature);
811    let mut signed_url = config_holder.get_endpoint_with_domain(input.bucket(), input.key(), &schema, &domain, true, is_custom_domain);
812
813    if let Some(query) = query.as_ref() {
814        if query.len() > 0 {
815            signed_url.push('?');
816            for (idx, kv) in query.iter().enumerate() {
817                signed_url.push_str(url_encode(*kv.0).as_str());
818                signed_url.push('=');
819                signed_url.push_str(kv.1);
820                if idx != query.len() - 1 {
821                    signed_url.push('&');
822                }
823            }
824        }
825    }
826    Ok(PreSignedURLOutput {
827        signed_url,
828        signed_header,
829    })
830}
831
832pub(crate) const SERVICE_TAG: &str = "tos";
833pub(crate) const REQUEST_TAG: &str = "request";
834pub(crate) const EMPTY_HASH_PAYLOAD: &str = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
835pub(crate) const UNSIGNED_PAYLOAD: &str = "UNSIGNED-PAYLOAD";
836pub(crate) const ALGORITHM: &str = "TOS4-HMAC-SHA256";
837
838pub(crate) fn sign_header<'a, 'c, B>(request: &mut HttpRequest<'c, B>, ak: &str, sk: &str, security_token: &str,
839                                     config_holder: &ConfigHolder, ac: &AdditionalContext<'a>) -> Result<(), TosError>
840where
841    'a: 'c,
842{
843    let is_anonymous = ak == "" || sk == "";
844    if !is_anonymous && security_token != "" {
845        request.header.insert(HEADER_SECURITY_TOKEN, security_token.to_string());
846    }
847    let (long_date, short_date, credential_scope) = calc_date_and_credential_scope(&config_holder.region, ac.request_date);
848    let bucket = request.bucket;
849    if ac.request_host != "" {
850        request.header.insert(HEADER_HOST, ac.request_host.to_string());
851    } else if ac.is_control_operation {
852        request.header.insert(HEADER_HOST, config_holder.get_host_with_domain(bucket, &config_holder.domain_control, config_holder.is_custom_domain));
853    } else {
854        request.header.insert(HEADER_HOST, config_holder.get_host(bucket));
855    }
856    request.header.insert(HEADER_REQUEST_DATE, long_date.clone());
857    if let Some(request_header) = ac.request_header {
858        if request_header.len() > 0 {
859            for (k, v) in request_header.iter() {
860                if request.header.contains_key(k.as_str()) {
861                    continue;
862                }
863
864                if NOT_ALLOWED_REQUEST_HEADER.contains_key(k.to_lowercase().as_str()) {
865                    continue;
866                }
867
868                request.header.insert(k, v.to_string());
869            }
870        }
871    }
872
873    if let Some(request_query) = ac.request_query {
874        if request_query.len() > 0 {
875            if request.query.is_none() {
876                request.query = Some(HashMap::with_capacity(request_query.len()));
877            }
878            if let Some(query) = &mut request.query {
879                for (k, v) in request_query.iter() {
880                    if query.contains_key(k.as_str()) {
881                        continue;
882                    }
883                    query.insert(k, v.to_string());
884                }
885            }
886        }
887    }
888    let (canonical_headers, signed_headers, _) = calc_canonical_headers(&request.header, &request.meta, false);
889    let canonical_request = calc_canonical_request(request.method.as_str(), &mut request.query, request.key, &canonical_headers, &signed_headers, EMPTY_HASH_PAYLOAD);
890    debug!("canonical_request: {}", canonical_request);
891    if is_anonymous {
892        return Ok(());
893    }
894
895    let string_to_sign = calc_string_to_sign(&long_date, &credential_scope, &canonical_request);
896    // println!("{}", string_to_sign);
897    debug!("string_to_sign: {}", string_to_sign);
898    let signature = calc_signature(&string_to_sign, &short_date, &config_holder.region, sk)?;
899    // println!("{}", signature);
900
901    let mut authorization = String::with_capacity(ALGORITHM.len() + ak.len() +
902        credential_scope.len() + signed_headers.len() + signature.len() + 64);
903    authorization = authorization + ALGORITHM +
904        " Credential=" + ak + "/" + credential_scope.as_str() +
905        ", SignedHeaders=" + signed_headers.as_str() +
906        ", Signature=" + signature.as_str();
907    // println!("{}", authorization);
908    request.header.insert(HEADER_AUTHORIZATION, authorization);
909    Ok(())
910}
911
912pub(crate) fn calc_string_to_sign(long_date: &str, credential_scope: &str, canonical_request: &str) -> String {
913    let mut string_to_sign = String::with_capacity(ALGORITHM.len() + long_date.len() + credential_scope.len() + 3);
914    string_to_sign = string_to_sign + ALGORITHM + "\n" + long_date + "\n" + credential_scope + "\n" + hex_sha256(canonical_request).as_str();
915    // println!("{}", string_to_sign);
916    string_to_sign
917}
918
919pub(crate) fn calc_signature(string_to_sign: &str, short_date: &str, region: &str, sk: &str) -> Result<String, TosError> {
920    let result = hmac_sha256(string_to_sign, hmac_sha256(REQUEST_TAG,
921                                                         hmac_sha256(SERVICE_TAG,
922                                                                     hmac_sha256(region,
923                                                                                 hmac_sha256(short_date, sk)?)?)?)?)?;
924    // println!("{}", hex(result.as_ref()));
925    Ok(hex(result))
926}
927
928pub(crate) fn calc_canonical_request(method: &str, query: &mut Option<HashMap<&str, String>>, key: &str, canonical_headers: &str, signed_headers: &str, content_sha256: &str) -> String {
929    let mut canonical_request = String::with_capacity(key.len() * 2 + 64);
930    if method != "" {
931        canonical_request.push_str(method);
932        canonical_request.push('\n');
933        canonical_request.push('/');
934    }
935    if key != "" {
936        canonical_request.push_str(url_encode_with_safe(key, "/").as_str());
937    }
938
939    if method != "" {
940        canonical_request.push('\n');
941    }
942    if let Some(query) = query.as_mut() {
943        let mut keys = Vec::<&str>::with_capacity(query.len());
944        for key in query.keys() {
945            keys.push(key);
946        }
947        keys.sort();
948        let mut value;
949        let mut encoded_value;
950        for (idx, key) in keys.iter().enumerate() {
951            value = query.get(*key).unwrap();
952            canonical_request.push_str(url_encode(*key).as_str());
953            canonical_request.push('=');
954            if *value != "" {
955                encoded_value = url_encode(value);
956            } else {
957                encoded_value = String::new();
958            }
959            canonical_request.push_str(encoded_value.as_str());
960            if idx != keys.len() - 1 {
961                canonical_request.push('&');
962            }
963            query.insert(*key, encoded_value);
964        }
965    }
966    canonical_request.push('\n');
967
968    if canonical_headers != "" {
969        canonical_request.push_str(canonical_headers);
970    }
971    if method != "" {
972        canonical_request.push('\n');
973    }
974
975    if signed_headers != "" {
976        canonical_request.push_str(signed_headers);
977    }
978    if method != "" {
979        canonical_request.push('\n');
980    }
981
982    canonical_request.push_str(content_sha256);
983    // println!("{}", canonical_request);
984    canonical_request
985}
986
987pub(crate) fn calc_date_and_credential(ak: &str, region: &str) -> (String, String, String) {
988    let long_date = Utc::now().format(LONG_DATE_FORMAT).to_string();
989    let short_date = &long_date[0..8];
990    let mut credential_scope = String::with_capacity(ak.len() + short_date.len() + region.len() + SERVICE_TAG.len() + REQUEST_TAG.len() + 4);
991    credential_scope.push_str(ak);
992    credential_scope.push('/');
993    credential_scope.push_str(short_date);
994    credential_scope.push('/');
995    credential_scope.push_str(region);
996    credential_scope.push('/');
997    credential_scope.push_str(SERVICE_TAG);
998    credential_scope.push('/');
999    credential_scope.push_str(REQUEST_TAG);
1000    let short_date = short_date.to_string();
1001    (long_date, short_date, credential_scope)
1002}
1003
1004pub(crate) fn calc_date_and_credential_scope(region: &str, request_date: Option<DateTime<Utc>>) -> (String, String, String) {
1005    let now;
1006    if let Some(request_date) = request_date {
1007        now = request_date;
1008    } else {
1009        now = Utc::now();
1010    }
1011    let long_date = now.format(LONG_DATE_FORMAT).to_string();
1012    let short_date = &long_date[0..8];
1013    let mut credential_scope = String::with_capacity(short_date.len() + region.len() + SERVICE_TAG.len() + REQUEST_TAG.len() + 3);
1014    credential_scope.push_str(short_date);
1015    credential_scope.push('/');
1016    credential_scope.push_str(region);
1017    credential_scope.push('/');
1018    credential_scope.push_str(SERVICE_TAG);
1019    credential_scope.push('/');
1020    credential_scope.push_str(REQUEST_TAG);
1021    let short_date = short_date.to_string();
1022    (long_date, short_date, credential_scope)
1023}
1024
1025pub(crate) fn calc_canonical_headers(header: &HashMap<impl AsRef<str>, String>, meta: &Option<HashMap<String, String>>, is_signed_all_headers: bool) -> (String, String, String) {
1026    let mut all_header: HashMap<String, &str>;
1027    let mut keys;
1028    let mut total_len = 0;
1029    if let Some(m) = meta {
1030        keys = Vec::<String>::with_capacity(header.len() + m.len());
1031        all_header = HashMap::with_capacity(header.len() + m.len());
1032        for (key, value) in m {
1033            let key = key.to_lowercase();
1034            all_header.insert(key.clone(), value);
1035            total_len += key.len();
1036            keys.push(key);
1037        }
1038    } else {
1039        keys = Vec::<String>::with_capacity(header.len());
1040        all_header = HashMap::with_capacity(header.len());
1041    }
1042
1043    for (key, value) in header {
1044        let key = key.as_ref().to_lowercase();
1045        all_header.insert(key.clone(), value);
1046        total_len += key.len();
1047        keys.push(key);
1048    }
1049
1050    keys.sort();
1051    let mut content_sha256 = "".to_string();
1052    let mut signed_headers = String::with_capacity(total_len + keys.len());
1053    let mut canonical_headers = String::with_capacity(total_len * 2 + keys.len() * 2);
1054    for key in keys.iter() {
1055        if !is_signed_all_headers && key != HEADER_HOST_LOWER && key != HEADER_CONTENT_TYPE_LOWER && !key.starts_with(HEADER_PREFIX) {
1056            continue;
1057        }
1058
1059        let val = all_header.get(key.as_str()).unwrap().trim();
1060        if key == HEADER_CONTENT_SHA256 {
1061            content_sha256 = val.to_string();
1062        }
1063
1064        signed_headers.push_str(key);
1065        signed_headers.push(';');
1066        canonical_headers.push_str(key);
1067        canonical_headers.push(':');
1068        canonical_headers.push_str(val);
1069        canonical_headers.push('\n');
1070    }
1071
1072    signed_headers = (&signed_headers[0..signed_headers.len() - 1]).to_string();
1073    (canonical_headers, signed_headers, content_sha256)
1074}