github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/openstack/obs/auth.go (about)

     1  package obs
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"sort"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  func (obsClient ObsClient) doAuthTemporary(method, bucketName, objectKey string, params map[string]string,
    12  	headers map[string][]string, expires int64) (requestUrl string, err error) {
    13  	isAkSkEmpty := obsClient.conf.securityProvider == nil || obsClient.conf.securityProvider.ak == "" || obsClient.conf.securityProvider.sk == ""
    14  	if !isAkSkEmpty && obsClient.conf.securityProvider.securityToken != "" {
    15  		if obsClient.conf.signature == SignatureObs {
    16  			params[HEADER_STS_TOKEN_OBS] = obsClient.conf.securityProvider.securityToken
    17  		} else {
    18  			params[HEADER_STS_TOKEN_AMZ] = obsClient.conf.securityProvider.securityToken
    19  		}
    20  	}
    21  	requestUrl, canonicalizedUrl := obsClient.conf.formatUrls(bucketName, objectKey, params, true)
    22  	parsedRequestUrl, err := url.Parse(requestUrl)
    23  	if err != nil {
    24  		return "", err
    25  	}
    26  	encodeHeaders(headers)
    27  	hostName := parsedRequestUrl.Host
    28  
    29  	isV4 := obsClient.conf.signature == SignatureV4
    30  	prepareHostAndDate(headers, hostName, isV4)
    31  
    32  	if isAkSkEmpty {
    33  		doLog(LEVEL_WARN, "No ak/sk provided, skip to construct authorization")
    34  	} else {
    35  		if isV4 {
    36  			date, parseDateErr := time.Parse(RFC1123_FORMAT, headers[HEADER_DATE_CAMEL][0])
    37  			if parseDateErr != nil {
    38  				doLog(LEVEL_WARN, "Failed to parse date with reason: %v", parseDateErr)
    39  				return "", parseDateErr
    40  			}
    41  			delete(headers, HEADER_DATE_CAMEL)
    42  			shortDate := date.Format(SHORT_DATE_FORMAT)
    43  			longDate := date.Format(LONG_DATE_FORMAT)
    44  			if len(headers[HEADER_HOST_CAMEL]) != 0 {
    45  				index := strings.LastIndex(headers[HEADER_HOST_CAMEL][0], ":")
    46  				if index != -1 {
    47  					port := headers[HEADER_HOST_CAMEL][0][index+1:]
    48  					if port == "80" || port == "443" {
    49  						headers[HEADER_HOST_CAMEL] = []string{headers[HEADER_HOST_CAMEL][0][:index]}
    50  					}
    51  				}
    52  
    53  			}
    54  
    55  			signedHeaders, _headers := getSignedHeaders(headers)
    56  
    57  			credential, scope := getCredential(obsClient.conf.securityProvider.ak, obsClient.conf.region, shortDate)
    58  			params[PARAM_ALGORITHM_AMZ_CAMEL] = V4_HASH_PREFIX
    59  			params[PARAM_CREDENTIAL_AMZ_CAMEL] = credential
    60  			params[PARAM_DATE_AMZ_CAMEL] = longDate
    61  			params[PARAM_EXPIRES_AMZ_CAMEL] = Int64ToString(expires)
    62  			params[PARAM_SIGNEDHEADERS_AMZ_CAMEL] = strings.Join(signedHeaders, ";")
    63  
    64  			requestUrl, canonicalizedUrl = obsClient.conf.formatUrls(bucketName, objectKey, params, true)
    65  			parsedRequestUrl, _err := url.Parse(requestUrl)
    66  			if _err != nil {
    67  				doLog(LEVEL_WARN, "Failed to parse requestUrl with reason: %v", _err)
    68  				return "", _err
    69  			}
    70  
    71  			stringToSign := getV4StringToSign(method, canonicalizedUrl, parsedRequestUrl.RawQuery, scope, longDate, UNSIGNED_PAYLOAD, signedHeaders, _headers)
    72  			signature := getSignature(stringToSign, obsClient.conf.securityProvider.sk, obsClient.conf.region, shortDate)
    73  
    74  			requestUrl += fmt.Sprintf("&%s=%s", PARAM_SIGNATURE_AMZ_CAMEL, UrlEncode(signature, false))
    75  
    76  		} else {
    77  			originDate := headers[HEADER_DATE_CAMEL][0]
    78  			date, parseDateErr := time.Parse(RFC1123_FORMAT, originDate)
    79  			if parseDateErr != nil {
    80  				doLog(LEVEL_WARN, "Failed to parse date with reason: %v", parseDateErr)
    81  				return "", parseDateErr
    82  			}
    83  			expires += date.Unix()
    84  			headers[HEADER_DATE_CAMEL] = []string{Int64ToString(expires)}
    85  
    86  			stringToSign := getV2StringToSign(method, canonicalizedUrl, headers, obsClient.conf.signature == SignatureObs)
    87  			signature := UrlEncode(Base64Encode(HmacSha1([]byte(obsClient.conf.securityProvider.sk), []byte(stringToSign))), false)
    88  			if !strings.Contains(requestUrl, "?") {
    89  				requestUrl += "?"
    90  			} else {
    91  				requestUrl += "&"
    92  			}
    93  			delete(headers, HEADER_DATE_CAMEL)
    94  
    95  			if obsClient.conf.signature != SignatureObs {
    96  				requestUrl += "AWS"
    97  			}
    98  			requestUrl += fmt.Sprintf("AccessKeyId=%s&Expires=%d&Signature=%s", UrlEncode(obsClient.conf.securityProvider.ak, false), expires, signature)
    99  		}
   100  	}
   101  
   102  	return
   103  }
   104  
   105  func (obsClient ObsClient) doAuth(method, bucketName, objectKey string, params map[string]string,
   106  	headers map[string][]string, hostName string) (requestUrl string, err error) {
   107  	isAkSkEmpty := obsClient.conf.securityProvider == nil || obsClient.conf.securityProvider.ak == "" || obsClient.conf.securityProvider.sk == ""
   108  	if !isAkSkEmpty && obsClient.conf.securityProvider.securityToken != "" {
   109  		if obsClient.conf.signature == SignatureObs {
   110  			headers[HEADER_STS_TOKEN_OBS] = []string{obsClient.conf.securityProvider.securityToken}
   111  		} else {
   112  			headers[HEADER_STS_TOKEN_AMZ] = []string{obsClient.conf.securityProvider.securityToken}
   113  		}
   114  	}
   115  	isObs := obsClient.conf.signature == SignatureObs
   116  	requestUrl, canonicalizedUrl := obsClient.conf.formatUrls(bucketName, objectKey, params, true)
   117  	parsedRequestUrl, err := url.Parse(requestUrl)
   118  	if err != nil {
   119  		return "", err
   120  	}
   121  	encodeHeaders(headers)
   122  
   123  	if hostName == "" {
   124  		hostName = parsedRequestUrl.Host
   125  	}
   126  
   127  	isV4 := obsClient.conf.signature == SignatureV4
   128  	prepareHostAndDate(headers, hostName, isV4)
   129  
   130  	if isAkSkEmpty {
   131  		doLog(LEVEL_WARN, "No ak/sk provided, skip to construct authorization")
   132  	} else {
   133  		ak := obsClient.conf.securityProvider.ak
   134  		sk := obsClient.conf.securityProvider.sk
   135  		var authorization string
   136  		if isV4 {
   137  			headers[HEADER_CONTENT_SHA256_AMZ] = []string{UNSIGNED_PAYLOAD}
   138  			ret := v4Auth(ak, sk, obsClient.conf.region, method, canonicalizedUrl, parsedRequestUrl.RawQuery, headers)
   139  			authorization = fmt.Sprintf("%s Credential=%s,SignedHeaders=%s,Signature=%s", V4_HASH_PREFIX, ret["Credential"], ret["SignedHeaders"], ret["Signature"])
   140  		} else {
   141  			ret := v2Auth(ak, sk, method, canonicalizedUrl, headers, isObs)
   142  			hashPrefix := V2_HASH_PREFIX
   143  			if isObs {
   144  				hashPrefix = OBS_HASH_PREFIX
   145  			}
   146  			authorization = fmt.Sprintf("%s %s:%s", hashPrefix, ak, ret["Signature"])
   147  		}
   148  		headers[HEADER_AUTH_CAMEL] = []string{authorization}
   149  	}
   150  	return
   151  }
   152  
   153  func prepareHostAndDate(headers map[string][]string, hostName string, isV4 bool) {
   154  	headers[HEADER_HOST_CAMEL] = []string{hostName}
   155  	if date, ok := headers[HEADER_DATE_AMZ]; ok {
   156  		flag := false
   157  		if len(date) == 1 {
   158  			if isV4 {
   159  				if t, err := time.Parse(LONG_DATE_FORMAT, date[0]); err == nil {
   160  					headers[HEADER_DATE_CAMEL] = []string{FormatUtcToRfc1123(t)}
   161  					flag = true
   162  				}
   163  			} else {
   164  				if strings.HasSuffix(date[0], "GMT") {
   165  					headers[HEADER_DATE_CAMEL] = []string{date[0]}
   166  					flag = true
   167  				}
   168  			}
   169  		}
   170  		if !flag {
   171  			delete(headers, HEADER_DATE_AMZ)
   172  		}
   173  	}
   174  	if _, ok := headers[HEADER_DATE_CAMEL]; !ok {
   175  		headers[HEADER_DATE_CAMEL] = []string{FormatUtcToRfc1123(time.Now().UTC())}
   176  	}
   177  }
   178  
   179  func encodeHeaders(headers map[string][]string) {
   180  	for key, values := range headers {
   181  		for index, value := range values {
   182  			values[index] = UrlEncode(value, true)
   183  		}
   184  		headers[key] = values
   185  	}
   186  }
   187  
   188  func prepareDateHeader(dataHeader, dateCamelHeader string, headers, _headers map[string][]string) {
   189  	if _, ok := _headers[HEADER_DATE_CAMEL]; ok {
   190  		if _, ok := _headers[dataHeader]; ok {
   191  			_headers[HEADER_DATE_CAMEL] = []string{""}
   192  		} else if _, ok := headers[dateCamelHeader]; ok {
   193  			_headers[HEADER_DATE_CAMEL] = []string{""}
   194  		}
   195  	} else if _, ok := _headers[strings.ToLower(HEADER_DATE_CAMEL)]; ok {
   196  		if _, ok := _headers[dataHeader]; ok {
   197  			_headers[HEADER_DATE_CAMEL] = []string{""}
   198  		} else if _, ok := headers[dateCamelHeader]; ok {
   199  			_headers[HEADER_DATE_CAMEL] = []string{""}
   200  		}
   201  	}
   202  }
   203  
   204  func getStringToSign(keys []string, isObs bool, _headers map[string][]string) []string {
   205  	stringToSign := make([]string, 0, len(keys))
   206  	for _, key := range keys {
   207  		var value string
   208  		prefixHeader := HEADER_PREFIX
   209  		prefixMetaHeader := HEADER_PREFIX_META
   210  		if isObs {
   211  			prefixHeader = HEADER_PREFIX_OBS
   212  			prefixMetaHeader = HEADER_PREFIX_META_OBS
   213  		}
   214  		if strings.HasPrefix(key, prefixHeader) {
   215  			if strings.HasPrefix(key, prefixMetaHeader) {
   216  				for index, v := range _headers[key] {
   217  					value += strings.TrimSpace(v)
   218  					if index != len(_headers[key])-1 {
   219  						value += ","
   220  					}
   221  				}
   222  			} else {
   223  				value = strings.Join(_headers[key], ",")
   224  			}
   225  			value = fmt.Sprintf("%s:%s", key, value)
   226  		} else {
   227  			value = strings.Join(_headers[key], ",")
   228  		}
   229  		stringToSign = append(stringToSign, value)
   230  	}
   231  	return stringToSign
   232  }
   233  
   234  func attachHeaders(headers map[string][]string, isObs bool) string {
   235  	length := len(headers)
   236  	_headers := make(map[string][]string, length)
   237  	keys := make([]string, 0, length)
   238  
   239  	for key, value := range headers {
   240  		_key := strings.ToLower(strings.TrimSpace(key))
   241  		if _key != "" {
   242  			prefixheader := HEADER_PREFIX
   243  			if isObs {
   244  				prefixheader = HEADER_PREFIX_OBS
   245  			}
   246  			if _key == "content-md5" || _key == "content-type" || _key == "date" || strings.HasPrefix(_key, prefixheader) {
   247  				keys = append(keys, _key)
   248  				_headers[_key] = value
   249  			}
   250  		} else {
   251  			delete(headers, key)
   252  		}
   253  	}
   254  
   255  	for _, interestedHeader := range interestedHeaders {
   256  		if _, ok := _headers[interestedHeader]; !ok {
   257  			_headers[interestedHeader] = []string{""}
   258  			keys = append(keys, interestedHeader)
   259  		}
   260  	}
   261  	dateCamelHeader := PARAM_DATE_AMZ_CAMEL
   262  	dataHeader := HEADER_DATE_AMZ
   263  	if isObs {
   264  		dateCamelHeader = PARAM_DATE_OBS_CAMEL
   265  		dataHeader = HEADER_DATE_OBS
   266  	}
   267  	prepareDateHeader(dataHeader, dateCamelHeader, headers, _headers)
   268  
   269  	sort.Strings(keys)
   270  	stringToSign := getStringToSign(keys, isObs, _headers)
   271  	return strings.Join(stringToSign, "\n")
   272  }
   273  
   274  func getCredential(ak, region, shortDate string) (string, string) {
   275  	scope := getScope(region, shortDate)
   276  	return fmt.Sprintf("%s/%s", ak, scope), scope
   277  }
   278  
   279  func getScope(region, shortDate string) string {
   280  	return fmt.Sprintf("%s/%s/%s/%s", shortDate, region, V4_SERVICE_NAME, V4_SERVICE_SUFFIX)
   281  }
   282  
   283  func getSignedHeaders(headers map[string][]string) ([]string, map[string][]string) {
   284  	length := len(headers)
   285  	_headers := make(map[string][]string, length)
   286  	signedHeaders := make([]string, 0, length)
   287  	for key, value := range headers {
   288  		_key := strings.ToLower(strings.TrimSpace(key))
   289  		if _key != "" {
   290  			signedHeaders = append(signedHeaders, _key)
   291  			_headers[_key] = value
   292  		} else {
   293  			delete(headers, key)
   294  		}
   295  	}
   296  	sort.Strings(signedHeaders)
   297  	return signedHeaders, _headers
   298  }
   299  
   300  func getSignature(stringToSign, sk, region, shortDate string) string {
   301  	key := HmacSha256([]byte(V4_HASH_PRE+sk), []byte(shortDate))
   302  	key = HmacSha256(key, []byte(region))
   303  	key = HmacSha256(key, []byte(V4_SERVICE_NAME))
   304  	key = HmacSha256(key, []byte(V4_SERVICE_SUFFIX))
   305  	return Hex(HmacSha256(key, []byte(stringToSign)))
   306  }