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 }