diff --git a/.gitignore b/.gitignore index 65f1105..9f8a812 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.idea/ + # ---> Rust # Generated by Cargo # will have compiled files and executables diff --git a/Cargo.toml b/Cargo.toml index 9212e49..d96a7ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oss" -version = "0.3.0" +version = "0.3.1" authors = ["Hatter Jiang "] edition = "2018" description = "Simple Alibaba Cloud OSS Client in Rust" diff --git a/src/lib.rs b/src/lib.rs index ee7a37a..f0d153f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,44 +1,41 @@ #[macro_use] extern crate rust_util; -use std::{ - io::Read, - fs::{ self, File }, - env, - path::PathBuf, - io::{ Error, ErrorKind }, -}; use crypto::{ - mac::{ Mac, MacResult }, hmac::Hmac, + mac::{Mac, MacResult}, sha1::Sha1, }; use reqwest::Response; -use rust_util::{ XResult, new_box_ioerror, util_time::get_current_secs }; +use rust_util::{new_box_ioerror, util_time::get_current_secs, XResult}; +use std::{ + env, + fs::{self, File}, + io::Read, + io::{Error, ErrorKind}, + path::PathBuf, +}; pub const OSS_VERB_GET: &str = "GET"; pub const OSS_VERB_PUT: &str = "PUT"; pub const OSS_VERB_DELETE: &str = "DELETE"; /// OSSClient - Alibaba Cloud OSS Client -/// +/// /// Reference URL: https://help.aliyun.com/document_detail/31952.html -/// -/// ```rust -/// let oss_client = OSSClient::new("AK", "SK"); -/// ``` +/// #[derive(Clone, Debug)] pub struct OSSClient { endpoint: String, access_key_id: String, access_key_secret: String, + security_token: Option, } /// OSS Client implemention impl OSSClient { - /// New OSSClient - /// + /// /// Use access_key_id and access_key_secret to create a OSSClient /// Consider support STS! pub fn new(endpoint: &str, access_key_id: &str, access_key_secret: &str) -> OSSClient { @@ -46,6 +43,20 @@ impl OSSClient { endpoint: endpoint.into(), access_key_id: access_key_id.into(), access_key_secret: access_key_secret.into(), + security_token: None, + } + } + + /// New OSSClient + /// + /// Use access_key_id and access_key_secret to create a OSSClient + /// Consider support STS! + pub fn new_sts(endpoint: &str, access_key_id: &str, access_key_secret: &str, security_token: &str) -> OSSClient { + OSSClient { + endpoint: endpoint.into(), + access_key_id: access_key_id.into(), + access_key_secret: access_key_secret.into(), + security_token: Some(security_token.into()), } } @@ -62,7 +73,7 @@ impl OSSClient { } /// New OSSClient from JSON - /// + /// /// JSON sample: /// ```json /// { @@ -82,7 +93,7 @@ impl OSSClient { let access_key_secret = json_value["accessKeySecret"].as_str().unwrap_or_default(); if endpoint.is_empty() || access_key_id.is_empty() || access_key_secret.is_empty() { - return Err(Box::new(Error::new(ErrorKind::Other,"Endpoint, access_key_id or access_key_secret cannot be empty"))); + return Err(Box::new(Error::new(ErrorKind::Other, "Endpoint, access_key_id or access_key_secret cannot be empty"))); } Ok(Self::new(endpoint, access_key_id, access_key_secret)) @@ -150,25 +161,30 @@ impl OSSClient { let mut signed_url = String::with_capacity(1024); signed_url.push_str(iff!(is_https, "https://", "http://")); signed_url.push_str(&format!("{}.{}/{}", bucket_name, self.endpoint, key)); - + let current_secs = get_current_secs(); let expire_secs = current_secs + expire_in_seconds; - + signed_url.push_str("?Expires="); signed_url.push_str(expire_secs.to_string().as_str()); signed_url.push_str("&OSSAccessKeyId="); signed_url.push_str(&urlencoding::encode(&self.access_key_id)); signed_url.push_str("&Signature="); - - let to_be_signed = get_to_be_signed(verb, expire_secs, bucket_name, key); + + let to_be_signed = get_to_be_signed(verb, expire_secs, bucket_name, key, &self.security_token); let signature = to_base64(calc_hmac_sha1(self.access_key_secret.as_bytes(), to_be_signed.as_bytes())); signed_url.push_str(&urlencoding::encode(signature.as_str())); - + + if let Some(security_token) = &self.security_token { + signed_url.push_str("&security-token="); + signed_url.push_str(&urlencoding::encode(security_token)); + } + signed_url } } -fn get_to_be_signed(verb: &str, expire_secs: u64, bucket_name: &str, key: &str) -> String { +fn get_to_be_signed(verb: &str, expire_secs: u64, bucket_name: &str, key: &str, security_token: &Option) -> String { let mut to_be_signed = String::with_capacity(512); to_be_signed.push_str(verb); to_be_signed.push_str("\n"); @@ -180,6 +196,10 @@ fn get_to_be_signed(verb: &str, expire_secs: u64, bucket_name: &str, key: &str) to_be_signed.push_str(bucket_name); to_be_signed.push_str("/"); to_be_signed.push_str(key); + if let Some(security_token) = security_token { + to_be_signed.push_str("?security-token="); + to_be_signed.push_str(security_token); + } to_be_signed }