| 1 | /* |
| 2 | * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"). |
| 5 | * You may not use this file except in compliance with the License. |
| 6 | * A copy of the License is located at |
| 7 | * |
| 8 | * http://aws.amazon.com/apache2.0 |
| 9 | * |
| 10 | * or in the "license" file accompanying this file. This file is distributed |
| 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| 12 | * express or implied. See the License for the specific language governing |
| 13 | * permissions and limitations under the License. |
| 14 | */ |
| 15 | |
| 16 | #include <aws/core/internal/AWSHttpResourceClient.h> |
| 17 | #include <aws/core/client/DefaultRetryStrategy.h> |
| 18 | #include <aws/core/http/HttpClient.h> |
| 19 | #include <aws/core/http/HttpClientFactory.h> |
| 20 | #include <aws/core/http/HttpResponse.h> |
| 21 | #include <aws/core/utils/logging/LogMacros.h> |
| 22 | #include <aws/core/utils/StringUtils.h> |
| 23 | #include <aws/core/utils/HashingUtils.h> |
| 24 | #include <aws/core/platform/Environment.h> |
| 25 | #include <aws/core/client/AWSError.h> |
| 26 | #include <aws/core/client/CoreErrors.h> |
| 27 | #include <aws/core/utils/xml/XmlSerializer.h> |
| 28 | #include <mutex> |
| 29 | #include <sstream> |
| 30 | |
| 31 | using namespace Aws; |
| 32 | using namespace Aws::Utils; |
| 33 | using namespace Aws::Utils::Logging; |
| 34 | using namespace Aws::Utils::Xml; |
| 35 | using namespace Aws::Http; |
| 36 | using namespace Aws::Client; |
| 37 | using namespace Aws::Internal; |
| 38 | |
| 39 | static const char EC2_SECURITY_CREDENTIALS_RESOURCE[] = "/latest/meta-data/iam/security-credentials" ; |
| 40 | static const char EC2_REGION_RESOURCE[] = "/latest/meta-data/placement/availability-zone" ; |
| 41 | static const char EC2_IMDS_TOKEN_RESOURCE[] = "/latest/api/token" ; |
| 42 | static const char EC2_IMDS_TOKEN_TTL_DEFAULT_VALUE[] = "21600" ; |
| 43 | static const char [] = "x-aws-ec2-metadata-token-ttl-seconds" ; |
| 44 | static const char [] = "x-aws-ec2-metadata-token" ; |
| 45 | static const char RESOURCE_CLIENT_CONFIGURATION_ALLOCATION_TAG[] = "AWSHttpResourceClient" ; |
| 46 | static const char EC2_METADATA_CLIENT_LOG_TAG[] = "EC2MetadataClient" ; |
| 47 | static const char ECS_CREDENTIALS_CLIENT_LOG_TAG[] = "ECSCredentialsClient" ; |
| 48 | |
| 49 | namespace Aws |
| 50 | { |
| 51 | namespace Client |
| 52 | { |
| 53 | Aws::String ComputeUserAgentString(); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | static ClientConfiguration MakeDefaultHttpResourceClientConfiguration(const char *logtag) |
| 58 | { |
| 59 | ClientConfiguration res; |
| 60 | |
| 61 | res.maxConnections = 2; |
| 62 | res.scheme = Scheme::HTTP; |
| 63 | |
| 64 | #if defined(WIN32) && defined(BYPASS_DEFAULT_PROXY) |
| 65 | // For security reasons, we must bypass any proxy settings when fetching sensitive information, for example |
| 66 | // user credentials. On Windows, IXMLHttpRequest2 does not support bypassing proxy settings, therefore, |
| 67 | // we force using WinHTTP client. On POSIX systems, CURL is set to bypass proxy settings by default. |
| 68 | res.httpLibOverride = TransferLibType::WIN_HTTP_CLIENT; |
| 69 | AWS_LOGSTREAM_INFO(logtag, "Overriding the current HTTP client to WinHTTP to bypass proxy settings." ); |
| 70 | #else |
| 71 | (void) logtag; // To disable warning about unused variable |
| 72 | #endif |
| 73 | // Explicitly set the proxy settings to empty/zero to avoid relying on defaults that could potentially change |
| 74 | // in the future. |
| 75 | res.proxyHost = "" ; |
| 76 | res.proxyUserName = "" ; |
| 77 | res.proxyPassword = "" ; |
| 78 | res.proxyPort = 0; |
| 79 | |
| 80 | // EC2MetadataService throttles by delaying the response so the service client should set a large read timeout. |
| 81 | // EC2MetadataService delay is in order of seconds so it only make sense to retry after a couple of seconds. |
| 82 | res.connectTimeoutMs = 1000; |
| 83 | res.requestTimeoutMs = 5000; |
| 84 | res.retryStrategy = Aws::MakeShared<DefaultRetryStrategy>(RESOURCE_CLIENT_CONFIGURATION_ALLOCATION_TAG, 4, 1000); |
| 85 | |
| 86 | return res; |
| 87 | } |
| 88 | |
| 89 | AWSHttpResourceClient::AWSHttpResourceClient(const Aws::Client::ClientConfiguration& clientConfiguration, const char* logtag) |
| 90 | : m_logtag(logtag), m_retryStrategy(clientConfiguration.retryStrategy), m_httpClient(nullptr) |
| 91 | { |
| 92 | AWS_LOGSTREAM_INFO(m_logtag.c_str(), |
| 93 | "Creating AWSHttpResourceClient with max connections " |
| 94 | << clientConfiguration.maxConnections |
| 95 | << " and scheme " |
| 96 | << SchemeMapper::ToString(clientConfiguration.scheme)); |
| 97 | |
| 98 | m_httpClient = CreateHttpClient(clientConfiguration); |
| 99 | } |
| 100 | |
| 101 | AWSHttpResourceClient::AWSHttpResourceClient(const char* logtag) |
| 102 | : AWSHttpResourceClient(MakeDefaultHttpResourceClientConfiguration(logtag), logtag) |
| 103 | { |
| 104 | } |
| 105 | |
| 106 | AWSHttpResourceClient::~AWSHttpResourceClient() |
| 107 | { |
| 108 | } |
| 109 | |
| 110 | Aws::String AWSHttpResourceClient::GetResource(const char* endpoint, const char* resource, const char* authToken) const |
| 111 | { |
| 112 | return GetResourceWithAWSWebServiceResult(endpoint, resource, authToken).GetPayload(); |
| 113 | } |
| 114 | |
| 115 | AmazonWebServiceResult<Aws::String> AWSHttpResourceClient::GetResourceWithAWSWebServiceResult(const char *endpoint, const char *resource, const char *authToken) const |
| 116 | { |
| 117 | Aws::StringStream ss; |
| 118 | ss << endpoint << resource; |
| 119 | std::shared_ptr<HttpRequest> request(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, |
| 120 | Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); |
| 121 | |
| 122 | request->SetUserAgent(ComputeUserAgentString()); |
| 123 | |
| 124 | if (authToken) |
| 125 | { |
| 126 | request->SetHeaderValue(Aws::Http::AWS_AUTHORIZATION_HEADER, authToken); |
| 127 | } |
| 128 | |
| 129 | return GetResourceWithAWSWebServiceResult(request); |
| 130 | } |
| 131 | |
| 132 | AmazonWebServiceResult<Aws::String> AWSHttpResourceClient::GetResourceWithAWSWebServiceResult(const std::shared_ptr<HttpRequest> &httpRequest) const |
| 133 | { |
| 134 | AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Retrieving credentials from " << httpRequest->GetURIString()); |
| 135 | |
| 136 | for (long retries = 0;; retries++) |
| 137 | { |
| 138 | std::shared_ptr<HttpResponse> response(m_httpClient->MakeRequest(httpRequest)); |
| 139 | |
| 140 | if (response && response->GetResponseCode() == HttpResponseCode::OK) |
| 141 | { |
| 142 | Aws::IStreamBufIterator eos; |
| 143 | return {Aws::String(Aws::IStreamBufIterator(response->GetResponseBody()), eos), response ? response->GetHeaders() : HeaderValueCollection(), HttpResponseCode::OK}; |
| 144 | } |
| 145 | |
| 146 | const Aws::Client::AWSError<Aws::Client::CoreErrors> error = [this, &response]() { |
| 147 | if (!response || response->GetResponseBody().tellp() < 1) |
| 148 | { |
| 149 | AWS_LOGSTREAM_ERROR(m_logtag.c_str(), "Http request to retrieve credentials failed" ); |
| 150 | return AWSError<CoreErrors>(CoreErrors::NETWORK_CONNECTION, true); // Retryable |
| 151 | } |
| 152 | else if (m_errorMarshaller) |
| 153 | { |
| 154 | return m_errorMarshaller->Marshall(*response); |
| 155 | } |
| 156 | else |
| 157 | { |
| 158 | const auto responseCode = response->GetResponseCode(); |
| 159 | |
| 160 | AWS_LOGSTREAM_ERROR(m_logtag.c_str(), "Http request to retrieve credentials failed with error code " |
| 161 | << static_cast<int>(responseCode)); |
| 162 | return CoreErrorsMapper::GetErrorForHttpResponseCode(responseCode); |
| 163 | } |
| 164 | }(); |
| 165 | |
| 166 | if (!m_retryStrategy->ShouldRetry(error, retries)) |
| 167 | { |
| 168 | AWS_LOGSTREAM_ERROR(m_logtag.c_str(), "Can not retrive resource from " << httpRequest->GetURIString()); |
| 169 | return {{}, response ? response->GetHeaders() : HeaderValueCollection(), error.GetResponseCode()}; |
| 170 | } |
| 171 | auto sleepMillis = m_retryStrategy->CalculateDelayBeforeNextRetry(error, retries); |
| 172 | AWS_LOGSTREAM_WARN(m_logtag.c_str(), "Request failed, now waiting " << sleepMillis << " ms before attempting again." ); |
| 173 | m_httpClient->RetryRequestSleep(std::chrono::milliseconds(sleepMillis)); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | EC2MetadataClient::EC2MetadataClient(const char* endpoint) |
| 178 | : AWSHttpResourceClient(EC2_METADATA_CLIENT_LOG_TAG), m_endpoint(endpoint), m_tokenRequired(true) |
| 179 | { |
| 180 | } |
| 181 | |
| 182 | EC2MetadataClient::EC2MetadataClient(const Aws::Client::ClientConfiguration &clientConfiguration, const char *endpoint) |
| 183 | : AWSHttpResourceClient(clientConfiguration, EC2_METADATA_CLIENT_LOG_TAG), m_endpoint(endpoint), m_tokenRequired(true) |
| 184 | { |
| 185 | } |
| 186 | |
| 187 | EC2MetadataClient::~EC2MetadataClient() |
| 188 | { |
| 189 | |
| 190 | } |
| 191 | |
| 192 | Aws::String EC2MetadataClient::GetResource(const char* resourcePath) const |
| 193 | { |
| 194 | return GetResource(m_endpoint.c_str(), resourcePath, nullptr/*authToken*/); |
| 195 | } |
| 196 | |
| 197 | Aws::String EC2MetadataClient::GetDefaultCredentials() const |
| 198 | { |
| 199 | std::unique_lock<std::recursive_mutex> locker(m_tokenMutex); |
| 200 | if (m_tokenRequired) |
| 201 | { |
| 202 | return GetDefaultCredentialsSecurely(); |
| 203 | } |
| 204 | |
| 205 | AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Getting default credentials for ec2 instance" ); |
| 206 | auto result = GetResourceWithAWSWebServiceResult(m_endpoint.c_str(), EC2_SECURITY_CREDENTIALS_RESOURCE, nullptr); |
| 207 | Aws::String credentialsString = result.GetPayload(); |
| 208 | auto httpResponseCode = result.GetResponseCode(); |
| 209 | |
| 210 | // Note, if service is insane, it might return 404 for our initial secure call, |
| 211 | // then when we fall back to insecure call, it might return 401 ask for secure call, |
| 212 | // Then, SDK might get into a recursive loop call situation between secure and insecure call. |
| 213 | if (httpResponseCode == Http::HttpResponseCode::UNAUTHORIZED) |
| 214 | { |
| 215 | m_tokenRequired = true; |
| 216 | return {}; |
| 217 | } |
| 218 | locker.unlock(); |
| 219 | |
| 220 | Aws::String trimmedCredentialsString = StringUtils::Trim(credentialsString.c_str()); |
| 221 | if (trimmedCredentialsString.empty()) return {}; |
| 222 | |
| 223 | Aws::Vector<Aws::String> securityCredentials = StringUtils::Split(trimmedCredentialsString, '\n'); |
| 224 | |
| 225 | AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource, " << EC2_SECURITY_CREDENTIALS_RESOURCE |
| 226 | << " returned credential string " << trimmedCredentialsString); |
| 227 | |
| 228 | if (securityCredentials.size() == 0) |
| 229 | { |
| 230 | AWS_LOGSTREAM_WARN(m_logtag.c_str(), "Initial call to ec2Metadataservice to get credentials failed" ); |
| 231 | return {}; |
| 232 | } |
| 233 | |
| 234 | Aws::StringStream ss; |
| 235 | ss << EC2_SECURITY_CREDENTIALS_RESOURCE << "/" << securityCredentials[0]; |
| 236 | AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource " << ss.str()); |
| 237 | return GetResource(ss.str().c_str()); |
| 238 | } |
| 239 | |
| 240 | Aws::String EC2MetadataClient::GetDefaultCredentialsSecurely() const |
| 241 | { |
| 242 | std::unique_lock<std::recursive_mutex> locker(m_tokenMutex); |
| 243 | if (!m_tokenRequired) |
| 244 | { |
| 245 | return GetDefaultCredentials(); |
| 246 | } |
| 247 | |
| 248 | Aws::StringStream ss; |
| 249 | ss << m_endpoint << EC2_IMDS_TOKEN_RESOURCE; |
| 250 | std::shared_ptr<HttpRequest> tokenRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_PUT, |
| 251 | Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); |
| 252 | tokenRequest->SetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER, EC2_IMDS_TOKEN_TTL_DEFAULT_VALUE); |
| 253 | auto userAgentString = ComputeUserAgentString(); |
| 254 | tokenRequest->SetUserAgent(userAgentString); |
| 255 | AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Calling EC2MetadataService to get token" ); |
| 256 | auto result = GetResourceWithAWSWebServiceResult(tokenRequest); |
| 257 | Aws::String tokenString = result.GetPayload(); |
| 258 | Aws::String trimmedTokenString = StringUtils::Trim(tokenString.c_str()); |
| 259 | |
| 260 | if (result.GetResponseCode() == HttpResponseCode::BAD_REQUEST) |
| 261 | { |
| 262 | return {}; |
| 263 | } |
| 264 | else if (result.GetResponseCode() != HttpResponseCode::OK || trimmedTokenString.empty()) |
| 265 | { |
| 266 | m_tokenRequired = false; |
| 267 | AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Calling EC2MetadataService to get token failed, falling back to less secure way." ); |
| 268 | return GetDefaultCredentials(); |
| 269 | } |
| 270 | m_token = trimmedTokenString; |
| 271 | locker.unlock(); |
| 272 | ss.str("" ); |
| 273 | ss << m_endpoint << EC2_SECURITY_CREDENTIALS_RESOURCE; |
| 274 | std::shared_ptr<HttpRequest> profileRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, |
| 275 | Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); |
| 276 | profileRequest->SetHeaderValue(EC2_IMDS_TOKEN_HEADER, trimmedTokenString); |
| 277 | profileRequest->SetUserAgent(userAgentString); |
| 278 | Aws::String profileString = GetResourceWithAWSWebServiceResult(profileRequest).GetPayload(); |
| 279 | |
| 280 | Aws::String trimmedProfileString = StringUtils::Trim(profileString.c_str()); |
| 281 | Aws::Vector<Aws::String> securityCredentials = StringUtils::Split(trimmedProfileString, '\n'); |
| 282 | |
| 283 | AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource, " << EC2_SECURITY_CREDENTIALS_RESOURCE |
| 284 | << " with token returned profile string " << trimmedProfileString); |
| 285 | if (securityCredentials.size() == 0) |
| 286 | { |
| 287 | AWS_LOGSTREAM_WARN(m_logtag.c_str(), "Calling EC2Metadataservice to get profiles failed" ); |
| 288 | return {}; |
| 289 | } |
| 290 | |
| 291 | ss.str("" ); |
| 292 | ss << m_endpoint << EC2_SECURITY_CREDENTIALS_RESOURCE << "/" << securityCredentials[0]; |
| 293 | std::shared_ptr<HttpRequest> credentialsRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, |
| 294 | Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); |
| 295 | credentialsRequest->SetHeaderValue(EC2_IMDS_TOKEN_HEADER, trimmedTokenString); |
| 296 | credentialsRequest->SetUserAgent(userAgentString); |
| 297 | AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource " << ss.str() << " with token." ); |
| 298 | return GetResourceWithAWSWebServiceResult(credentialsRequest).GetPayload(); |
| 299 | } |
| 300 | |
| 301 | Aws::String EC2MetadataClient::GetCurrentRegion() const |
| 302 | { |
| 303 | AWS_LOGSTREAM_TRACE(m_logtag.c_str(), "Getting current region for ec2 instance" ); |
| 304 | |
| 305 | Aws::StringStream ss; |
| 306 | ss << m_endpoint << EC2_REGION_RESOURCE; |
| 307 | std::shared_ptr<HttpRequest> regionRequest(CreateHttpRequest(ss.str(), HttpMethod::HTTP_GET, |
| 308 | Aws::Utils::Stream::DefaultResponseStreamFactoryMethod)); |
| 309 | { |
| 310 | std::lock_guard<std::recursive_mutex> locker(m_tokenMutex); |
| 311 | if (m_tokenRequired) |
| 312 | { |
| 313 | regionRequest->SetHeaderValue(EC2_IMDS_TOKEN_HEADER, m_token); |
| 314 | } |
| 315 | } |
| 316 | regionRequest->SetUserAgent(ComputeUserAgentString()); |
| 317 | Aws::String azString = GetResourceWithAWSWebServiceResult(regionRequest).GetPayload(); |
| 318 | |
| 319 | if (azString.empty()) |
| 320 | { |
| 321 | AWS_LOGSTREAM_INFO(m_logtag.c_str() , |
| 322 | "Unable to pull region from instance metadata service " ); |
| 323 | return {}; |
| 324 | } |
| 325 | |
| 326 | Aws::String trimmedAZString = StringUtils::Trim(azString.c_str()); |
| 327 | AWS_LOGSTREAM_DEBUG(m_logtag.c_str(), "Calling EC2MetadataService resource " |
| 328 | << EC2_REGION_RESOURCE << " , returned credential string " << trimmedAZString); |
| 329 | |
| 330 | Aws::String region; |
| 331 | region.reserve(trimmedAZString.length()); |
| 332 | |
| 333 | bool digitFound = false; |
| 334 | for (auto character : trimmedAZString) |
| 335 | { |
| 336 | if(digitFound && !isdigit(character)) |
| 337 | { |
| 338 | break; |
| 339 | } |
| 340 | if (isdigit(character)) |
| 341 | { |
| 342 | digitFound = true; |
| 343 | } |
| 344 | |
| 345 | region.append(1, character); |
| 346 | } |
| 347 | |
| 348 | AWS_LOGSTREAM_INFO(m_logtag.c_str(), "Detected current region as " << region); |
| 349 | return region; |
| 350 | } |
| 351 | |
| 352 | ECSCredentialsClient::ECSCredentialsClient(const char* resourcePath, const char* endpoint, const char* token) |
| 353 | : AWSHttpResourceClient(ECS_CREDENTIALS_CLIENT_LOG_TAG), |
| 354 | m_resourcePath(resourcePath), m_endpoint(endpoint), m_token(token) |
| 355 | { |
| 356 | } |
| 357 | |
| 358 | ECSCredentialsClient::ECSCredentialsClient(const Aws::Client::ClientConfiguration& clientConfiguration, const char* resourcePath, const char* endpoint, const char* token) |
| 359 | : AWSHttpResourceClient(clientConfiguration, ECS_CREDENTIALS_CLIENT_LOG_TAG), |
| 360 | m_resourcePath(resourcePath), m_endpoint(endpoint), m_token(token) |
| 361 | { |
| 362 | } |
| 363 | |
| 364 | static const char STS_RESOURCE_CLIENT_LOG_TAG[] = "STSResourceClient" ; |
| 365 | STSCredentialsClient::STSCredentialsClient(const Aws::Client::ClientConfiguration& clientConfiguration) |
| 366 | : AWSHttpResourceClient(clientConfiguration, STS_RESOURCE_CLIENT_LOG_TAG) |
| 367 | { |
| 368 | SetErrorMarshaller(Aws::MakeUnique<Aws::Client::XmlErrorMarshaller>(STS_RESOURCE_CLIENT_LOG_TAG)); |
| 369 | |
| 370 | Aws::StringStream ss; |
| 371 | if (clientConfiguration.scheme == Aws::Http::Scheme::HTTP) |
| 372 | { |
| 373 | ss << "http://" ; |
| 374 | } |
| 375 | else |
| 376 | { |
| 377 | ss << "https://" ; |
| 378 | } |
| 379 | |
| 380 | static const int CN_NORTH_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTH_1); |
| 381 | static const int CN_NORTHWEST_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTHWEST_1); |
| 382 | auto hash = Aws::Utils::HashingUtils::HashString(clientConfiguration.region.c_str()); |
| 383 | |
| 384 | ss << "sts." << clientConfiguration.region << ".amazonaws.com" ; |
| 385 | if (hash == CN_NORTH_1_HASH || hash == CN_NORTHWEST_1_HASH) |
| 386 | { |
| 387 | ss << ".cn" ; |
| 388 | } |
| 389 | m_endpoint = ss.str(); |
| 390 | |
| 391 | AWS_LOGSTREAM_INFO(STS_RESOURCE_CLIENT_LOG_TAG, "Creating STS ResourceClient with endpoint: " << m_endpoint); |
| 392 | } |
| 393 | |
| 394 | STSCredentialsClient::STSAssumeRoleWithWebIdentityResult STSCredentialsClient::GetAssumeRoleWithWebIdentityCredentials(const STSAssumeRoleWithWebIdentityRequest& request) |
| 395 | { |
| 396 | //Calculate query string |
| 397 | Aws::StringStream ss; |
| 398 | ss << "/?Action=AssumeRoleWithWebIdentity" |
| 399 | << "&Version=2011-06-15" |
| 400 | << "&RoleSessionName=" << Aws::Utils::StringUtils::URLEncode(request.roleSessionName.c_str()) |
| 401 | << "&RoleArn=" << Aws::Utils::StringUtils::URLEncode(request.roleArn.c_str()) |
| 402 | << "&WebIdentityToken=" << Aws::Utils::StringUtils::URLEncode(request.webIdentityToken.c_str()); |
| 403 | |
| 404 | Aws::String credentialsStr = GetResource(m_endpoint.c_str(), ss.str().c_str()/*query string*/, nullptr/*no auth token needed*/); |
| 405 | |
| 406 | //Parse credentials |
| 407 | STSAssumeRoleWithWebIdentityResult result; |
| 408 | if (credentialsStr.empty()) |
| 409 | { |
| 410 | AWS_LOGSTREAM_WARN(STS_RESOURCE_CLIENT_LOG_TAG, "Get an empty credential from sts" ); |
| 411 | return result; |
| 412 | } |
| 413 | |
| 414 | const Utils::Xml::XmlDocument xmlDocument = XmlDocument::CreateFromXmlString(credentialsStr); |
| 415 | XmlNode rootNode = xmlDocument.GetRootElement(); |
| 416 | XmlNode resultNode = rootNode; |
| 417 | if (!rootNode.IsNull() && (rootNode.GetName() != "AssumeRoleWithWebIdentityResult" )) |
| 418 | { |
| 419 | resultNode = rootNode.FirstChild("AssumeRoleWithWebIdentityResult" ); |
| 420 | } |
| 421 | |
| 422 | if (!resultNode.IsNull()) |
| 423 | { |
| 424 | XmlNode credentialsNode = resultNode.FirstChild("Credentials" ); |
| 425 | if (!credentialsNode.IsNull()) |
| 426 | { |
| 427 | XmlNode accessKeyIdNode = credentialsNode.FirstChild("AccessKeyId" ); |
| 428 | if (!accessKeyIdNode.IsNull()) |
| 429 | { |
| 430 | result.creds.SetAWSAccessKeyId(accessKeyIdNode.GetText()); |
| 431 | } |
| 432 | |
| 433 | XmlNode secretAccessKeyNode = credentialsNode.FirstChild("SecretAccessKey" ); |
| 434 | if (!secretAccessKeyNode.IsNull()) |
| 435 | { |
| 436 | result.creds.SetAWSSecretKey(secretAccessKeyNode.GetText()); |
| 437 | } |
| 438 | |
| 439 | XmlNode sessionTokenNode = credentialsNode.FirstChild("SessionToken" ); |
| 440 | if (!sessionTokenNode.IsNull()) |
| 441 | { |
| 442 | result.creds.SetSessionToken(sessionTokenNode.GetText()); |
| 443 | } |
| 444 | |
| 445 | XmlNode expirationNode = credentialsNode.FirstChild("Expiration" ); |
| 446 | if (!expirationNode.IsNull()) |
| 447 | { |
| 448 | result.creds.SetExpiration(DateTime(StringUtils::Trim(expirationNode.GetText().c_str()).c_str(), DateFormat::ISO_8601)); |
| 449 | } |
| 450 | } |
| 451 | } |
| 452 | return result; |
| 453 | } |
| 454 | |