|
- // Copyright 2019 Huawei Technologies Co.,Ltd.
- // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
- // this file except in compliance with the License. You may obtain a copy of the
- // License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software distributed
- // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- // CONDITIONS OF ANY KIND, either express or implied. See the License for the
- // specific language governing permissions and limitations under the License.
-
- package obs
-
- import (
- "fmt"
- "net/url"
- "sort"
- "strings"
- "time"
- )
-
- func (obsClient ObsClient) doAuthTemporary(method, bucketName, objectKey string, params map[string]string,
- headers map[string][]string, expires int64) (requestURL string, err error) {
- isAkSkEmpty := obsClient.conf.securityProvider == nil || obsClient.conf.securityProvider.ak == "" || obsClient.conf.securityProvider.sk == ""
- if isAkSkEmpty == false && obsClient.conf.securityProvider.securityToken != "" {
- if obsClient.conf.signature == SignatureObs {
- params[HEADER_STS_TOKEN_OBS] = obsClient.conf.securityProvider.securityToken
- } else {
- params[HEADER_STS_TOKEN_AMZ] = obsClient.conf.securityProvider.securityToken
- }
- }
- requestURL, canonicalizedURL := obsClient.conf.formatUrls(bucketName, objectKey, params, true)
- parsedRequestURL, err := url.Parse(requestURL)
- if err != nil {
- return "", err
- }
- encodeHeaders(headers)
- hostName := parsedRequestURL.Host
-
- isV4 := obsClient.conf.signature == SignatureV4
- prepareHostAndDate(headers, hostName, isV4)
-
- if isAkSkEmpty {
- doLog(LEVEL_WARN, "No ak/sk provided, skip to construct authorization")
- } else {
- if isV4 {
- date, parseDateErr := time.Parse(RFC1123_FORMAT, headers[HEADER_DATE_CAMEL][0])
- if parseDateErr != nil {
- doLog(LEVEL_WARN, "Failed to parse date with reason: %v", parseDateErr)
- return "", parseDateErr
- }
- delete(headers, HEADER_DATE_CAMEL)
- shortDate := date.Format(SHORT_DATE_FORMAT)
- longDate := date.Format(LONG_DATE_FORMAT)
- if len(headers[HEADER_HOST_CAMEL]) != 0 {
- index := strings.LastIndex(headers[HEADER_HOST_CAMEL][0], ":")
- if index != -1 {
- port := headers[HEADER_HOST_CAMEL][0][index+1:]
- if port == "80" || port == "443" {
- headers[HEADER_HOST_CAMEL] = []string{headers[HEADER_HOST_CAMEL][0][:index]}
- }
- }
-
- }
-
- signedHeaders, _headers := getSignedHeaders(headers)
-
- credential, scope := getCredential(obsClient.conf.securityProvider.ak, obsClient.conf.region, shortDate)
- params[PARAM_ALGORITHM_AMZ_CAMEL] = V4_HASH_PREFIX
- params[PARAM_CREDENTIAL_AMZ_CAMEL] = credential
- params[PARAM_DATE_AMZ_CAMEL] = longDate
- params[PARAM_EXPIRES_AMZ_CAMEL] = Int64ToString(expires)
- params[PARAM_SIGNEDHEADERS_AMZ_CAMEL] = strings.Join(signedHeaders, ";")
-
- requestURL, canonicalizedURL = obsClient.conf.formatUrls(bucketName, objectKey, params, true)
- parsedRequestURL, _err := url.Parse(requestURL)
- if _err != nil {
- return "", _err
- }
-
- stringToSign := getV4StringToSign(method, canonicalizedURL, parsedRequestURL.RawQuery, scope, longDate, UNSIGNED_PAYLOAD, signedHeaders, _headers)
- signature := getSignature(stringToSign, obsClient.conf.securityProvider.sk, obsClient.conf.region, shortDate)
-
- requestURL += fmt.Sprintf("&%s=%s", PARAM_SIGNATURE_AMZ_CAMEL, UrlEncode(signature, false))
-
- } else {
- originDate := headers[HEADER_DATE_CAMEL][0]
- date, parseDateErr := time.Parse(RFC1123_FORMAT, originDate)
- if parseDateErr != nil {
- doLog(LEVEL_WARN, "Failed to parse date with reason: %v", parseDateErr)
- return "", parseDateErr
- }
- expires += date.Unix()
- headers[HEADER_DATE_CAMEL] = []string{Int64ToString(expires)}
-
- stringToSign := getV2StringToSign(method, canonicalizedURL, headers, obsClient.conf.signature == SignatureObs)
- signature := UrlEncode(Base64Encode(HmacSha1([]byte(obsClient.conf.securityProvider.sk), []byte(stringToSign))), false)
- if strings.Index(requestURL, "?") < 0 {
- requestURL += "?"
- } else {
- requestURL += "&"
- }
- delete(headers, HEADER_DATE_CAMEL)
-
- if obsClient.conf.signature != SignatureObs {
- requestURL += "AWS"
- }
- requestURL += fmt.Sprintf("AccessKeyId=%s&Expires=%d&Signature=%s", UrlEncode(obsClient.conf.securityProvider.ak, false), expires, signature)
- }
- }
-
- return
- }
-
- func (obsClient ObsClient) doAuth(method, bucketName, objectKey string, params map[string]string,
- headers map[string][]string, hostName string) (requestURL string, err error) {
- isAkSkEmpty := obsClient.conf.securityProvider == nil || obsClient.conf.securityProvider.ak == "" || obsClient.conf.securityProvider.sk == ""
- if isAkSkEmpty == false && obsClient.conf.securityProvider.securityToken != "" {
- if obsClient.conf.signature == SignatureObs {
- headers[HEADER_STS_TOKEN_OBS] = []string{obsClient.conf.securityProvider.securityToken}
- } else {
- headers[HEADER_STS_TOKEN_AMZ] = []string{obsClient.conf.securityProvider.securityToken}
- }
- }
- isObs := obsClient.conf.signature == SignatureObs
- requestURL, canonicalizedURL := obsClient.conf.formatUrls(bucketName, objectKey, params, true)
- parsedRequestURL, err := url.Parse(requestURL)
- if err != nil {
- return "", err
- }
- encodeHeaders(headers)
-
- if hostName == "" {
- hostName = parsedRequestURL.Host
- }
-
- isV4 := obsClient.conf.signature == SignatureV4
- prepareHostAndDate(headers, hostName, isV4)
-
- if isAkSkEmpty {
- doLog(LEVEL_WARN, "No ak/sk provided, skip to construct authorization")
- } else {
- ak := obsClient.conf.securityProvider.ak
- sk := obsClient.conf.securityProvider.sk
- var authorization string
- if isV4 {
- headers[HEADER_CONTENT_SHA256_AMZ] = []string{UNSIGNED_PAYLOAD}
- ret := v4Auth(ak, sk, obsClient.conf.region, method, canonicalizedURL, parsedRequestURL.RawQuery, headers)
- authorization = fmt.Sprintf("%s Credential=%s,SignedHeaders=%s,Signature=%s", V4_HASH_PREFIX, ret["Credential"], ret["SignedHeaders"], ret["Signature"])
- } else {
- ret := v2Auth(ak, sk, method, canonicalizedURL, headers, isObs)
- hashPrefix := V2_HASH_PREFIX
- if isObs {
- hashPrefix = OBS_HASH_PREFIX
- }
- authorization = fmt.Sprintf("%s %s:%s", hashPrefix, ak, ret["Signature"])
- }
- headers[HEADER_AUTH_CAMEL] = []string{authorization}
- }
- return
- }
-
- func prepareHostAndDate(headers map[string][]string, hostName string, isV4 bool) {
- headers[HEADER_HOST_CAMEL] = []string{hostName}
- if date, ok := headers[HEADER_DATE_AMZ]; ok {
- flag := false
- if len(date) == 1 {
- if isV4 {
- if t, err := time.Parse(LONG_DATE_FORMAT, date[0]); err == nil {
- headers[HEADER_DATE_CAMEL] = []string{FormatUtcToRfc1123(t)}
- flag = true
- }
- } else {
- if strings.HasSuffix(date[0], "GMT") {
- headers[HEADER_DATE_CAMEL] = []string{date[0]}
- flag = true
- }
- }
- }
- if !flag {
- delete(headers, HEADER_DATE_AMZ)
- }
- }
- if _, ok := headers[HEADER_DATE_CAMEL]; !ok {
- headers[HEADER_DATE_CAMEL] = []string{FormatUtcToRfc1123(time.Now().UTC())}
- }
- }
-
- func encodeHeaders(headers map[string][]string) {
- for key, values := range headers {
- for index, value := range values {
- values[index] = UrlEncode(value, true)
- }
- headers[key] = values
- }
- }
-
- func attachHeaders(headers map[string][]string, isObs bool) string {
- length := len(headers)
- _headers := make(map[string][]string, length)
- keys := make([]string, 0, length)
-
- for key, value := range headers {
- _key := strings.ToLower(strings.TrimSpace(key))
- if _key != "" {
- prefixheader := HEADER_PREFIX
- if isObs {
- prefixheader = HEADER_PREFIX_OBS
- }
- if _key == "content-md5" || _key == "content-type" || _key == "date" || strings.HasPrefix(_key, prefixheader) {
- keys = append(keys, _key)
- _headers[_key] = value
- }
- } else {
- delete(headers, key)
- }
- }
-
- for _, interestedHeader := range interestedHeaders {
- if _, ok := _headers[interestedHeader]; !ok {
- _headers[interestedHeader] = []string{""}
- keys = append(keys, interestedHeader)
- }
- }
- dateCamelHeader := PARAM_DATE_AMZ_CAMEL
- dataHeader := HEADER_DATE_AMZ
- if isObs {
- dateCamelHeader = PARAM_DATE_OBS_CAMEL
- dataHeader = HEADER_DATE_OBS
- }
- if _, ok := _headers[HEADER_DATE_CAMEL]; ok {
- if _, ok := _headers[dataHeader]; ok {
- _headers[HEADER_DATE_CAMEL] = []string{""}
- } else if _, ok := headers[dateCamelHeader]; ok {
- _headers[HEADER_DATE_CAMEL] = []string{""}
- }
- } else if _, ok := _headers[strings.ToLower(HEADER_DATE_CAMEL)]; ok {
- if _, ok := _headers[dataHeader]; ok {
- _headers[HEADER_DATE_CAMEL] = []string{""}
- } else if _, ok := headers[dateCamelHeader]; ok {
- _headers[HEADER_DATE_CAMEL] = []string{""}
- }
- }
-
- sort.Strings(keys)
-
- stringToSign := make([]string, 0, len(keys))
- for _, key := range keys {
- var value string
- prefixHeader := HEADER_PREFIX
- prefixMetaHeader := HEADER_PREFIX_META
- if isObs {
- prefixHeader = HEADER_PREFIX_OBS
- prefixMetaHeader = HEADER_PREFIX_META_OBS
- }
- if strings.HasPrefix(key, prefixHeader) {
- if strings.HasPrefix(key, prefixMetaHeader) {
- for index, v := range _headers[key] {
- value += strings.TrimSpace(v)
- if index != len(_headers[key])-1 {
- value += ","
- }
- }
- } else {
- value = strings.Join(_headers[key], ",")
- }
- value = fmt.Sprintf("%s:%s", key, value)
- } else {
- value = strings.Join(_headers[key], ",")
- }
- stringToSign = append(stringToSign, value)
- }
- return strings.Join(stringToSign, "\n")
- }
-
- func getV2StringToSign(method, canonicalizedURL string, headers map[string][]string, isObs bool) string {
- stringToSign := strings.Join([]string{method, "\n", attachHeaders(headers, isObs), "\n", canonicalizedURL}, "")
-
- var isSecurityToken bool
- var securityToken []string
- if isObs {
- securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_OBS]
- } else {
- securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_AMZ]
- }
- var query []string
- if !isSecurityToken {
- parmas := strings.Split(canonicalizedURL, "?")
- if len(parmas) > 1 {
- query = strings.Split(parmas[1], "&")
- for _, value := range query {
- if strings.HasPrefix(value, HEADER_STS_TOKEN_AMZ+"=") || strings.HasPrefix(value, HEADER_STS_TOKEN_OBS+"=") {
- if value[len(HEADER_STS_TOKEN_AMZ)+1:] != "" {
- securityToken = []string{value[len(HEADER_STS_TOKEN_AMZ)+1:]}
- isSecurityToken = true
- }
- }
- }
- }
- }
- logStringToSign := stringToSign
- if isSecurityToken && len(securityToken) > 0 {
- logStringToSign = strings.Replace(logStringToSign, securityToken[0], "******", -1)
- }
- doLog(LEVEL_DEBUG, "The v2 auth stringToSign:\n%s", logStringToSign)
- return stringToSign
- }
-
- func v2Auth(ak, sk, method, canonicalizedURL string, headers map[string][]string, isObs bool) map[string]string {
- stringToSign := getV2StringToSign(method, canonicalizedURL, headers, isObs)
- return map[string]string{"Signature": Base64Encode(HmacSha1([]byte(sk), []byte(stringToSign)))}
- }
-
- func getScope(region, shortDate string) string {
- return fmt.Sprintf("%s/%s/%s/%s", shortDate, region, V4_SERVICE_NAME, V4_SERVICE_SUFFIX)
- }
-
- func getCredential(ak, region, shortDate string) (string, string) {
- scope := getScope(region, shortDate)
- return fmt.Sprintf("%s/%s", ak, scope), scope
- }
-
- func getV4StringToSign(method, canonicalizedURL, queryURL, scope, longDate, payload string, signedHeaders []string, headers map[string][]string) string {
- canonicalRequest := make([]string, 0, 10+len(signedHeaders)*4)
- canonicalRequest = append(canonicalRequest, method)
- canonicalRequest = append(canonicalRequest, "\n")
- canonicalRequest = append(canonicalRequest, canonicalizedURL)
- canonicalRequest = append(canonicalRequest, "\n")
- canonicalRequest = append(canonicalRequest, queryURL)
- canonicalRequest = append(canonicalRequest, "\n")
-
- for _, signedHeader := range signedHeaders {
- values, _ := headers[signedHeader]
- for _, value := range values {
- canonicalRequest = append(canonicalRequest, signedHeader)
- canonicalRequest = append(canonicalRequest, ":")
- canonicalRequest = append(canonicalRequest, value)
- canonicalRequest = append(canonicalRequest, "\n")
- }
- }
- canonicalRequest = append(canonicalRequest, "\n")
- canonicalRequest = append(canonicalRequest, strings.Join(signedHeaders, ";"))
- canonicalRequest = append(canonicalRequest, "\n")
- canonicalRequest = append(canonicalRequest, payload)
-
- _canonicalRequest := strings.Join(canonicalRequest, "")
-
- var isSecurityToken bool
- var securityToken []string
- if securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_OBS]; !isSecurityToken {
- securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_AMZ]
- }
- var query []string
- if !isSecurityToken {
- query = strings.Split(queryURL, "&")
- for _, value := range query {
- if strings.HasPrefix(value, HEADER_STS_TOKEN_AMZ+"=") || strings.HasPrefix(value, HEADER_STS_TOKEN_OBS+"=") {
- if value[len(HEADER_STS_TOKEN_AMZ)+1:] != "" {
- securityToken = []string{value[len(HEADER_STS_TOKEN_AMZ)+1:]}
- isSecurityToken = true
- }
- }
- }
- }
- logCanonicalRequest := _canonicalRequest
- if isSecurityToken && len(securityToken) > 0 {
- logCanonicalRequest = strings.Replace(logCanonicalRequest, securityToken[0], "******", -1)
- }
- doLog(LEVEL_DEBUG, "The v4 auth canonicalRequest:\n%s", logCanonicalRequest)
-
- stringToSign := make([]string, 0, 7)
- stringToSign = append(stringToSign, V4_HASH_PREFIX)
- stringToSign = append(stringToSign, "\n")
- stringToSign = append(stringToSign, longDate)
- stringToSign = append(stringToSign, "\n")
- stringToSign = append(stringToSign, scope)
- stringToSign = append(stringToSign, "\n")
- stringToSign = append(stringToSign, HexSha256([]byte(_canonicalRequest)))
-
- _stringToSign := strings.Join(stringToSign, "")
-
- doLog(LEVEL_DEBUG, "The v4 auth stringToSign:\n%s", _stringToSign)
- return _stringToSign
- }
-
- func getSignedHeaders(headers map[string][]string) ([]string, map[string][]string) {
- length := len(headers)
- _headers := make(map[string][]string, length)
- signedHeaders := make([]string, 0, length)
- for key, value := range headers {
- _key := strings.ToLower(strings.TrimSpace(key))
- if _key != "" {
- signedHeaders = append(signedHeaders, _key)
- _headers[_key] = value
- } else {
- delete(headers, key)
- }
- }
- sort.Strings(signedHeaders)
- return signedHeaders, _headers
- }
-
- func getSignature(stringToSign, sk, region, shortDate string) string {
- key := HmacSha256([]byte(V4_HASH_PRE+sk), []byte(shortDate))
- key = HmacSha256(key, []byte(region))
- key = HmacSha256(key, []byte(V4_SERVICE_NAME))
- key = HmacSha256(key, []byte(V4_SERVICE_SUFFIX))
- return Hex(HmacSha256(key, []byte(stringToSign)))
- }
-
- // V4Auth is a wrapper for v4Auth
- func V4Auth(ak, sk, region, method, canonicalizedURL, queryURL string, headers map[string][]string) map[string]string {
- return v4Auth(ak, sk, region, method, canonicalizedURL, queryURL, headers)
- }
-
- func v4Auth(ak, sk, region, method, canonicalizedURL, queryURL string, headers map[string][]string) map[string]string {
- var t time.Time
- if val, ok := headers[HEADER_DATE_AMZ]; ok {
- var err error
- t, err = time.Parse(LONG_DATE_FORMAT, val[0])
- if err != nil {
- t = time.Now().UTC()
- }
- } else if val, ok := headers[PARAM_DATE_AMZ_CAMEL]; ok {
- var err error
- t, err = time.Parse(LONG_DATE_FORMAT, val[0])
- if err != nil {
- t = time.Now().UTC()
- }
- } else if val, ok := headers[HEADER_DATE_CAMEL]; ok {
- var err error
- t, err = time.Parse(RFC1123_FORMAT, val[0])
- if err != nil {
- t = time.Now().UTC()
- }
- } else if val, ok := headers[strings.ToLower(HEADER_DATE_CAMEL)]; ok {
- var err error
- t, err = time.Parse(RFC1123_FORMAT, val[0])
- if err != nil {
- t = time.Now().UTC()
- }
- } else {
- t = time.Now().UTC()
- }
- shortDate := t.Format(SHORT_DATE_FORMAT)
- longDate := t.Format(LONG_DATE_FORMAT)
-
- signedHeaders, _headers := getSignedHeaders(headers)
-
- credential, scope := getCredential(ak, region, shortDate)
-
- payload := UNSIGNED_PAYLOAD
- if val, ok := headers[HEADER_CONTENT_SHA256_AMZ]; ok {
- payload = val[0]
- }
- stringToSign := getV4StringToSign(method, canonicalizedURL, queryURL, scope, longDate, payload, signedHeaders, _headers)
-
- signature := getSignature(stringToSign, sk, region, shortDate)
-
- ret := make(map[string]string, 3)
- ret["Credential"] = credential
- ret["SignedHeaders"] = strings.Join(signedHeaders, ";")
- ret["Signature"] = signature
- return ret
- }
|