github.com/huaweicloud/golangsdk@v0.0.0-20210831081626-d823fe11ceba/openstack/obs/auth.go (about) 1 // Copyright 2019 Huawei Technologies Co.,Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); you may not use 3 // this file except in compliance with the License. You may obtain a copy of the 4 // License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software distributed 9 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 // CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 // specific language governing permissions and limitations under the License. 12 13 package obs 14 15 import ( 16 "fmt" 17 "net/url" 18 "sort" 19 "strings" 20 "time" 21 ) 22 23 func (obsClient ObsClient) doAuthTemporary(method, bucketName, objectKey string, params map[string]string, 24 headers map[string][]string, expires int64) (requestURL string, err error) { 25 sh := obsClient.getSecurity() 26 isAkSkEmpty := sh.ak == "" || sh.sk == "" 27 if isAkSkEmpty == false && sh.securityToken != "" { 28 if obsClient.conf.signature == SignatureObs { 29 params[HEADER_STS_TOKEN_OBS] = sh.securityToken 30 } else { 31 params[HEADER_STS_TOKEN_AMZ] = sh.securityToken 32 } 33 } 34 requestURL, canonicalizedURL := obsClient.conf.formatUrls(bucketName, objectKey, params, true) 35 parsedRequestURL, err := url.Parse(requestURL) 36 if err != nil { 37 return "", err 38 } 39 encodeHeaders(headers) 40 hostName := parsedRequestURL.Host 41 42 isV4 := obsClient.conf.signature == SignatureV4 43 prepareHostAndDate(headers, hostName, isV4) 44 45 if isAkSkEmpty { 46 doLog(LEVEL_WARN, "No ak/sk provided, skip to construct authorization") 47 } else { 48 if isV4 { 49 date, parseDateErr := time.Parse(RFC1123_FORMAT, headers[HEADER_DATE_CAMEL][0]) 50 if parseDateErr != nil { 51 doLog(LEVEL_WARN, "Failed to parse date with reason: %v", parseDateErr) 52 return "", parseDateErr 53 } 54 delete(headers, HEADER_DATE_CAMEL) 55 shortDate := date.Format(SHORT_DATE_FORMAT) 56 longDate := date.Format(LONG_DATE_FORMAT) 57 if len(headers[HEADER_HOST_CAMEL]) != 0 { 58 index := strings.LastIndex(headers[HEADER_HOST_CAMEL][0], ":") 59 if index != -1 { 60 port := headers[HEADER_HOST_CAMEL][0][index+1:] 61 if port == "80" || port == "443" { 62 headers[HEADER_HOST_CAMEL] = []string{headers[HEADER_HOST_CAMEL][0][:index]} 63 } 64 } 65 66 } 67 68 signedHeaders, _headers := getSignedHeaders(headers) 69 70 credential, scope := getCredential(sh.ak, obsClient.conf.region, shortDate) 71 params[PARAM_ALGORITHM_AMZ_CAMEL] = V4_HASH_PREFIX 72 params[PARAM_CREDENTIAL_AMZ_CAMEL] = credential 73 params[PARAM_DATE_AMZ_CAMEL] = longDate 74 params[PARAM_EXPIRES_AMZ_CAMEL] = Int64ToString(expires) 75 params[PARAM_SIGNEDHEADERS_AMZ_CAMEL] = strings.Join(signedHeaders, ";") 76 77 requestURL, canonicalizedURL = obsClient.conf.formatUrls(bucketName, objectKey, params, true) 78 parsedRequestURL, _err := url.Parse(requestURL) 79 if _err != nil { 80 return "", _err 81 } 82 83 stringToSign := getV4StringToSign(method, canonicalizedURL, parsedRequestURL.RawQuery, scope, longDate, UNSIGNED_PAYLOAD, signedHeaders, _headers) 84 signature := getSignature(stringToSign, sh.sk, obsClient.conf.region, shortDate) 85 86 requestURL += fmt.Sprintf("&%s=%s", PARAM_SIGNATURE_AMZ_CAMEL, UrlEncode(signature, false)) 87 88 } else { 89 originDate := headers[HEADER_DATE_CAMEL][0] 90 date, parseDateErr := time.Parse(RFC1123_FORMAT, originDate) 91 if parseDateErr != nil { 92 doLog(LEVEL_WARN, "Failed to parse date with reason: %v", parseDateErr) 93 return "", parseDateErr 94 } 95 expires += date.Unix() 96 headers[HEADER_DATE_CAMEL] = []string{Int64ToString(expires)} 97 98 stringToSign := getV2StringToSign(method, canonicalizedURL, headers, obsClient.conf.signature == SignatureObs) 99 signature := UrlEncode(Base64Encode(HmacSha1([]byte(sh.sk), []byte(stringToSign))), false) 100 if strings.Index(requestURL, "?") < 0 { 101 requestURL += "?" 102 } else { 103 requestURL += "&" 104 } 105 delete(headers, HEADER_DATE_CAMEL) 106 107 if obsClient.conf.signature != SignatureObs { 108 requestURL += "AWS" 109 } 110 requestURL += fmt.Sprintf("AccessKeyId=%s&Expires=%d&Signature=%s", UrlEncode(sh.ak, false), expires, signature) 111 } 112 } 113 114 return 115 } 116 117 func (obsClient ObsClient) doAuth(method, bucketName, objectKey string, params map[string]string, 118 headers map[string][]string, hostName string) (requestURL string, err error) { 119 sh := obsClient.getSecurity() 120 isAkSkEmpty := sh.ak == "" || sh.sk == "" 121 if isAkSkEmpty == false && sh.securityToken != "" { 122 if obsClient.conf.signature == SignatureObs { 123 headers[HEADER_STS_TOKEN_OBS] = []string{sh.securityToken} 124 } else { 125 headers[HEADER_STS_TOKEN_AMZ] = []string{sh.securityToken} 126 } 127 } 128 isObs := obsClient.conf.signature == SignatureObs 129 requestURL, canonicalizedURL := obsClient.conf.formatUrls(bucketName, objectKey, params, true) 130 parsedRequestURL, err := url.Parse(requestURL) 131 if err != nil { 132 return "", err 133 } 134 encodeHeaders(headers) 135 136 if hostName == "" { 137 hostName = parsedRequestURL.Host 138 } 139 140 isV4 := obsClient.conf.signature == SignatureV4 141 prepareHostAndDate(headers, hostName, isV4) 142 143 if isAkSkEmpty { 144 doLog(LEVEL_WARN, "No ak/sk provided, skip to construct authorization") 145 } else { 146 ak := sh.ak 147 sk := sh.sk 148 var authorization string 149 if isV4 { 150 headers[HEADER_CONTENT_SHA256_AMZ] = []string{UNSIGNED_PAYLOAD} 151 ret := v4Auth(ak, sk, obsClient.conf.region, method, canonicalizedURL, parsedRequestURL.RawQuery, headers) 152 authorization = fmt.Sprintf("%s Credential=%s,SignedHeaders=%s,Signature=%s", V4_HASH_PREFIX, ret["Credential"], ret["SignedHeaders"], ret["Signature"]) 153 } else { 154 ret := v2Auth(ak, sk, method, canonicalizedURL, headers, isObs) 155 hashPrefix := V2_HASH_PREFIX 156 if isObs { 157 hashPrefix = OBS_HASH_PREFIX 158 } 159 authorization = fmt.Sprintf("%s %s:%s", hashPrefix, ak, ret["Signature"]) 160 } 161 headers[HEADER_AUTH_CAMEL] = []string{authorization} 162 } 163 return 164 } 165 166 func prepareHostAndDate(headers map[string][]string, hostName string, isV4 bool) { 167 headers[HEADER_HOST_CAMEL] = []string{hostName} 168 if date, ok := headers[HEADER_DATE_AMZ]; ok { 169 flag := false 170 if len(date) == 1 { 171 if isV4 { 172 if t, err := time.Parse(LONG_DATE_FORMAT, date[0]); err == nil { 173 headers[HEADER_DATE_CAMEL] = []string{FormatUtcToRfc1123(t)} 174 flag = true 175 } 176 } else { 177 if strings.HasSuffix(date[0], "GMT") { 178 headers[HEADER_DATE_CAMEL] = []string{date[0]} 179 flag = true 180 } 181 } 182 } 183 if !flag { 184 delete(headers, HEADER_DATE_AMZ) 185 } 186 } 187 if _, ok := headers[HEADER_DATE_CAMEL]; !ok { 188 headers[HEADER_DATE_CAMEL] = []string{FormatUtcToRfc1123(time.Now().UTC())} 189 } 190 191 } 192 193 func encodeHeaders(headers map[string][]string) { 194 for key, values := range headers { 195 for index, value := range values { 196 values[index] = UrlEncode(value, true) 197 } 198 headers[key] = values 199 } 200 } 201 202 func prepareDateHeader(dataHeader, dateCamelHeader string, headers, _headers map[string][]string) { 203 if _, ok := _headers[HEADER_DATE_CAMEL]; ok { 204 if _, ok := _headers[dataHeader]; ok { 205 _headers[HEADER_DATE_CAMEL] = []string{""} 206 } else if _, ok := headers[dateCamelHeader]; ok { 207 _headers[HEADER_DATE_CAMEL] = []string{""} 208 } 209 } else if _, ok := _headers[strings.ToLower(HEADER_DATE_CAMEL)]; ok { 210 if _, ok := _headers[dataHeader]; ok { 211 _headers[HEADER_DATE_CAMEL] = []string{""} 212 } else if _, ok := headers[dateCamelHeader]; ok { 213 _headers[HEADER_DATE_CAMEL] = []string{""} 214 } 215 } 216 } 217 218 func getStringToSign(keys []string, isObs bool, _headers map[string][]string) []string { 219 stringToSign := make([]string, 0, len(keys)) 220 for _, key := range keys { 221 var value string 222 prefixHeader := HEADER_PREFIX 223 prefixMetaHeader := HEADER_PREFIX_META 224 if isObs { 225 prefixHeader = HEADER_PREFIX_OBS 226 prefixMetaHeader = HEADER_PREFIX_META_OBS 227 } 228 if strings.HasPrefix(key, prefixHeader) { 229 if strings.HasPrefix(key, prefixMetaHeader) { 230 for index, v := range _headers[key] { 231 value += strings.TrimSpace(v) 232 if index != len(_headers[key])-1 { 233 value += "," 234 } 235 } 236 } else { 237 value = strings.Join(_headers[key], ",") 238 } 239 value = fmt.Sprintf("%s:%s", key, value) 240 } else { 241 value = strings.Join(_headers[key], ",") 242 } 243 stringToSign = append(stringToSign, value) 244 } 245 return stringToSign 246 } 247 248 func attachHeaders(headers map[string][]string, isObs bool) string { 249 length := len(headers) 250 _headers := make(map[string][]string, length) 251 keys := make([]string, 0, length) 252 253 for key, value := range headers { 254 _key := strings.ToLower(strings.TrimSpace(key)) 255 if _key != "" { 256 prefixheader := HEADER_PREFIX 257 if isObs { 258 prefixheader = HEADER_PREFIX_OBS 259 } 260 if _key == "content-md5" || _key == "content-type" || _key == "date" || strings.HasPrefix(_key, prefixheader) { 261 keys = append(keys, _key) 262 _headers[_key] = value 263 } 264 } else { 265 delete(headers, key) 266 } 267 } 268 269 for _, interestedHeader := range interestedHeaders { 270 if _, ok := _headers[interestedHeader]; !ok { 271 _headers[interestedHeader] = []string{""} 272 keys = append(keys, interestedHeader) 273 } 274 } 275 dateCamelHeader := PARAM_DATE_AMZ_CAMEL 276 dataHeader := HEADER_DATE_AMZ 277 if isObs { 278 dateCamelHeader = PARAM_DATE_OBS_CAMEL 279 dataHeader = HEADER_DATE_OBS 280 } 281 prepareDateHeader(dataHeader, dateCamelHeader, headers, _headers) 282 283 sort.Strings(keys) 284 stringToSign := getStringToSign(keys, isObs, _headers) 285 return strings.Join(stringToSign, "\n") 286 } 287 288 func getV2StringToSign(method, canonicalizedURL string, headers map[string][]string, isObs bool) string { 289 stringToSign := strings.Join([]string{method, "\n", attachHeaders(headers, isObs), "\n", canonicalizedURL}, "") 290 291 var isSecurityToken bool 292 var securityToken []string 293 if isObs { 294 securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_OBS] 295 } else { 296 securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_AMZ] 297 } 298 var query []string 299 if !isSecurityToken { 300 parmas := strings.Split(canonicalizedURL, "?") 301 if len(parmas) > 1 { 302 query = strings.Split(parmas[1], "&") 303 for _, value := range query { 304 if strings.HasPrefix(value, HEADER_STS_TOKEN_AMZ+"=") || strings.HasPrefix(value, HEADER_STS_TOKEN_OBS+"=") { 305 if value[len(HEADER_STS_TOKEN_AMZ)+1:] != "" { 306 securityToken = []string{value[len(HEADER_STS_TOKEN_AMZ)+1:]} 307 isSecurityToken = true 308 } 309 } 310 } 311 } 312 } 313 logStringToSign := stringToSign 314 if isSecurityToken && len(securityToken) > 0 { 315 logStringToSign = strings.Replace(logStringToSign, securityToken[0], "******", -1) 316 } 317 doLog(LEVEL_DEBUG, "The v2 auth stringToSign:\n%s", logStringToSign) 318 return stringToSign 319 } 320 321 func v2Auth(ak, sk, method, canonicalizedURL string, headers map[string][]string, isObs bool) map[string]string { 322 stringToSign := getV2StringToSign(method, canonicalizedURL, headers, isObs) 323 return map[string]string{"Signature": Base64Encode(HmacSha1([]byte(sk), []byte(stringToSign)))} 324 } 325 326 func getScope(region, shortDate string) string { 327 return fmt.Sprintf("%s/%s/%s/%s", shortDate, region, V4_SERVICE_NAME, V4_SERVICE_SUFFIX) 328 } 329 330 func getCredential(ak, region, shortDate string) (string, string) { 331 scope := getScope(region, shortDate) 332 return fmt.Sprintf("%s/%s", ak, scope), scope 333 } 334 335 func getV4StringToSign(method, canonicalizedURL, queryURL, scope, longDate, payload string, signedHeaders []string, headers map[string][]string) string { 336 canonicalRequest := make([]string, 0, 10+len(signedHeaders)*4) 337 canonicalRequest = append(canonicalRequest, method) 338 canonicalRequest = append(canonicalRequest, "\n") 339 canonicalRequest = append(canonicalRequest, canonicalizedURL) 340 canonicalRequest = append(canonicalRequest, "\n") 341 canonicalRequest = append(canonicalRequest, queryURL) 342 canonicalRequest = append(canonicalRequest, "\n") 343 344 for _, signedHeader := range signedHeaders { 345 values, _ := headers[signedHeader] 346 for _, value := range values { 347 canonicalRequest = append(canonicalRequest, signedHeader) 348 canonicalRequest = append(canonicalRequest, ":") 349 canonicalRequest = append(canonicalRequest, value) 350 canonicalRequest = append(canonicalRequest, "\n") 351 } 352 } 353 canonicalRequest = append(canonicalRequest, "\n") 354 canonicalRequest = append(canonicalRequest, strings.Join(signedHeaders, ";")) 355 canonicalRequest = append(canonicalRequest, "\n") 356 canonicalRequest = append(canonicalRequest, payload) 357 358 _canonicalRequest := strings.Join(canonicalRequest, "") 359 360 var isSecurityToken bool 361 var securityToken []string 362 if securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_OBS]; !isSecurityToken { 363 securityToken, isSecurityToken = headers[HEADER_STS_TOKEN_AMZ] 364 } 365 var query []string 366 if !isSecurityToken { 367 query = strings.Split(queryURL, "&") 368 for _, value := range query { 369 if strings.HasPrefix(value, HEADER_STS_TOKEN_AMZ+"=") || strings.HasPrefix(value, HEADER_STS_TOKEN_OBS+"=") { 370 if value[len(HEADER_STS_TOKEN_AMZ)+1:] != "" { 371 securityToken = []string{value[len(HEADER_STS_TOKEN_AMZ)+1:]} 372 isSecurityToken = true 373 } 374 } 375 } 376 } 377 logCanonicalRequest := _canonicalRequest 378 if isSecurityToken && len(securityToken) > 0 { 379 logCanonicalRequest = strings.Replace(logCanonicalRequest, securityToken[0], "******", -1) 380 } 381 doLog(LEVEL_DEBUG, "The v4 auth canonicalRequest:\n%s", logCanonicalRequest) 382 383 stringToSign := make([]string, 0, 7) 384 stringToSign = append(stringToSign, V4_HASH_PREFIX) 385 stringToSign = append(stringToSign, "\n") 386 stringToSign = append(stringToSign, longDate) 387 stringToSign = append(stringToSign, "\n") 388 stringToSign = append(stringToSign, scope) 389 stringToSign = append(stringToSign, "\n") 390 stringToSign = append(stringToSign, HexSha256([]byte(_canonicalRequest))) 391 392 _stringToSign := strings.Join(stringToSign, "") 393 394 doLog(LEVEL_DEBUG, "The v4 auth stringToSign:\n%s", _stringToSign) 395 return _stringToSign 396 } 397 398 func getSignedHeaders(headers map[string][]string) ([]string, map[string][]string) { 399 length := len(headers) 400 _headers := make(map[string][]string, length) 401 signedHeaders := make([]string, 0, length) 402 for key, value := range headers { 403 _key := strings.ToLower(strings.TrimSpace(key)) 404 if _key != "" { 405 signedHeaders = append(signedHeaders, _key) 406 _headers[_key] = value 407 } else { 408 delete(headers, key) 409 } 410 } 411 sort.Strings(signedHeaders) 412 return signedHeaders, _headers 413 } 414 415 func getSignature(stringToSign, sk, region, shortDate string) string { 416 key := HmacSha256([]byte(V4_HASH_PRE+sk), []byte(shortDate)) 417 key = HmacSha256(key, []byte(region)) 418 key = HmacSha256(key, []byte(V4_SERVICE_NAME)) 419 key = HmacSha256(key, []byte(V4_SERVICE_SUFFIX)) 420 return Hex(HmacSha256(key, []byte(stringToSign))) 421 } 422 423 // V4Auth is a wrapper for v4Auth 424 func V4Auth(ak, sk, region, method, canonicalizedURL, queryURL string, headers map[string][]string) map[string]string { 425 return v4Auth(ak, sk, region, method, canonicalizedURL, queryURL, headers) 426 } 427 428 func v4Auth(ak, sk, region, method, canonicalizedURL, queryURL string, headers map[string][]string) map[string]string { 429 var t time.Time 430 if val, ok := headers[HEADER_DATE_AMZ]; ok { 431 var err error 432 t, err = time.Parse(LONG_DATE_FORMAT, val[0]) 433 if err != nil { 434 t = time.Now().UTC() 435 } 436 } else if val, ok := headers[PARAM_DATE_AMZ_CAMEL]; ok { 437 var err error 438 t, err = time.Parse(LONG_DATE_FORMAT, val[0]) 439 if err != nil { 440 t = time.Now().UTC() 441 } 442 } else if val, ok := headers[HEADER_DATE_CAMEL]; ok { 443 var err error 444 t, err = time.Parse(RFC1123_FORMAT, val[0]) 445 if err != nil { 446 t = time.Now().UTC() 447 } 448 } else if val, ok := headers[strings.ToLower(HEADER_DATE_CAMEL)]; ok { 449 var err error 450 t, err = time.Parse(RFC1123_FORMAT, val[0]) 451 if err != nil { 452 t = time.Now().UTC() 453 } 454 } else { 455 t = time.Now().UTC() 456 } 457 shortDate := t.Format(SHORT_DATE_FORMAT) 458 longDate := t.Format(LONG_DATE_FORMAT) 459 460 signedHeaders, _headers := getSignedHeaders(headers) 461 462 credential, scope := getCredential(ak, region, shortDate) 463 464 payload := UNSIGNED_PAYLOAD 465 if val, ok := headers[HEADER_CONTENT_SHA256_AMZ]; ok { 466 payload = val[0] 467 } 468 stringToSign := getV4StringToSign(method, canonicalizedURL, queryURL, scope, longDate, payload, signedHeaders, _headers) 469 470 signature := getSignature(stringToSign, sk, region, shortDate) 471 472 ret := make(map[string]string, 3) 473 ret["Credential"] = credential 474 ret["SignedHeaders"] = strings.Join(signedHeaders, ";") 475 ret["Signature"] = signature 476 return ret 477 }