SSOCredentialsProvider.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/core/auth/SSOCredentialsProvider.h>
  6. #include <aws/core/config/AWSProfileConfigLoader.h>
  7. #include <aws/core/internal/AWSHttpResourceClient.h>
  8. #include <aws/core/platform/Environment.h>
  9. #include <aws/core/platform/FileSystem.h>
  10. #include <aws/core/utils/logging/LogMacros.h>
  11. #include <aws/core/utils/StringUtils.h>
  12. #include <aws/core/utils/FileSystemUtils.h>
  13. #include <aws/core/client/SpecifiedRetryableErrorsRetryStrategy.h>
  14. #include <aws/core/utils/UUID.h>
  15. #include <aws/core/utils/HashingUtils.h>
  16. #include <aws/core/utils/json/JsonSerializer.h>
  17. using namespace Aws::Utils;
  18. using namespace Aws::Utils::Logging;
  19. using namespace Aws::Auth;
  20. using namespace Aws::Internal;
  21. using namespace Aws::FileSystem;
  22. using namespace Aws::Client;
  23. using Aws::Utils::Threading::ReaderLockGuard;
  24. static const char SSO_CREDENTIALS_PROVIDER_LOG_TAG[] = "SSOCredentialsProvider";
  25. SSOCredentialsProvider::SSOCredentialsProvider() : m_profileToUse(GetConfigProfileName())
  26. {
  27. AWS_LOGSTREAM_INFO(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Setting sso credentials provider to read config from " << m_profileToUse);
  28. }
  29. SSOCredentialsProvider::SSOCredentialsProvider(const Aws::String& profile) : m_profileToUse(profile),
  30. m_bearerTokenProvider(profile)
  31. {
  32. AWS_LOGSTREAM_INFO(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Setting sso credentials provider to read config from " << m_profileToUse);
  33. }
  34. AWSCredentials SSOCredentialsProvider::GetAWSCredentials()
  35. {
  36. RefreshIfExpired();
  37. ReaderLockGuard guard(m_reloadLock);
  38. return m_credentials;
  39. }
  40. void SSOCredentialsProvider::Reload()
  41. {
  42. auto profile = Aws::Config::GetCachedConfigProfile(m_profileToUse);
  43. const auto accessToken = [&]() -> Aws::String {
  44. // If we have an SSO Session set, use the refreshed token.
  45. if (profile.IsSsoSessionSet()) {
  46. m_ssoRegion = profile.GetSsoSession().GetSsoRegion();
  47. auto token = m_bearerTokenProvider.GetAWSBearerToken();
  48. m_expiresAt = token.GetExpiration();
  49. return token.GetToken();
  50. }
  51. Aws::String hashedStartUrl = Aws::Utils::HashingUtils::HexEncode(Aws::Utils::HashingUtils::CalculateSHA1(profile.GetSsoStartUrl()));
  52. auto profileDirectory = ProfileConfigFileAWSCredentialsProvider::GetProfileDirectory();
  53. Aws::StringStream ssToken;
  54. ssToken << profileDirectory;
  55. ssToken << PATH_DELIM << "sso" << PATH_DELIM << "cache" << PATH_DELIM << hashedStartUrl << ".json";
  56. auto ssoTokenPath = ssToken.str();
  57. AWS_LOGSTREAM_DEBUG(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Loading token from: " << ssoTokenPath)
  58. m_ssoRegion = profile.GetSsoRegion();
  59. return LoadAccessTokenFile(ssoTokenPath);
  60. }();
  61. if (accessToken.empty()) {
  62. AWS_LOGSTREAM_TRACE(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Access token for SSO not available");
  63. return;
  64. }
  65. if (m_expiresAt < Aws::Utils::DateTime::Now()) {
  66. AWS_LOGSTREAM_ERROR(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Cached Token expired at " << m_expiresAt.ToGmtString(DateFormat::ISO_8601));
  67. return;
  68. }
  69. SSOCredentialsClient::SSOGetRoleCredentialsRequest request;
  70. request.m_ssoAccountId = profile.GetSsoAccountId();
  71. request.m_ssoRoleName = profile.GetSsoRoleName();
  72. request.m_accessToken = accessToken;
  73. Aws::Client::ClientConfiguration config;
  74. config.scheme = Aws::Http::Scheme::HTTPS;
  75. config.region = m_ssoRegion;
  76. AWS_LOGSTREAM_DEBUG(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Passing config to client for region: " << m_ssoRegion);
  77. Aws::Vector<Aws::String> retryableErrors;
  78. retryableErrors.push_back("TooManyRequestsException");
  79. config.retryStrategy = Aws::MakeShared<SpecifiedRetryableErrorsRetryStrategy>(SSO_CREDENTIALS_PROVIDER_LOG_TAG, retryableErrors, 3/*maxRetries*/);
  80. m_client = Aws::MakeUnique<Aws::Internal::SSOCredentialsClient>(SSO_CREDENTIALS_PROVIDER_LOG_TAG, config);
  81. AWS_LOGSTREAM_TRACE(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Requesting credentials with AWS_ACCESS_KEY: " << m_ssoAccountId);
  82. auto result = m_client->GetSSOCredentials(request);
  83. AWS_LOGSTREAM_TRACE(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Successfully retrieved credentials with AWS_ACCESS_KEY: " << result.creds.GetAWSAccessKeyId());
  84. m_credentials = result.creds;
  85. }
  86. void SSOCredentialsProvider::RefreshIfExpired()
  87. {
  88. ReaderLockGuard guard(m_reloadLock);
  89. if (!m_credentials.IsExpiredOrEmpty())
  90. {
  91. return;
  92. }
  93. guard.UpgradeToWriterLock();
  94. if (!m_credentials.IsExpiredOrEmpty()) // double-checked lock to avoid refreshing twice
  95. {
  96. return;
  97. }
  98. Reload();
  99. }
  100. Aws::String SSOCredentialsProvider::LoadAccessTokenFile(const Aws::String& ssoAccessTokenPath)
  101. {
  102. AWS_LOGSTREAM_DEBUG(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Preparing to load token from: " << ssoAccessTokenPath);
  103. Aws::IFStream inputFile(ssoAccessTokenPath.c_str());
  104. if(inputFile)
  105. {
  106. AWS_LOGSTREAM_DEBUG(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Reading content from token file: " << ssoAccessTokenPath);
  107. Json::JsonValue tokenDoc(inputFile);
  108. if (!tokenDoc.WasParseSuccessful())
  109. {
  110. AWS_LOGSTREAM_ERROR(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Failed to parse token file: " << ssoAccessTokenPath);
  111. return "";
  112. }
  113. Utils::Json::JsonView tokenView(tokenDoc);
  114. Aws::String tmpAccessToken, expirationStr;
  115. tmpAccessToken = tokenView.GetString("accessToken");
  116. expirationStr = tokenView.GetString("expiresAt");
  117. DateTime expiration(expirationStr, DateFormat::ISO_8601);
  118. AWS_LOGSTREAM_TRACE(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Token cache file contains accessToken [" << tmpAccessToken << "], expiration [" << expirationStr << "]");
  119. if (tmpAccessToken.empty() || !expiration.WasParseSuccessful()) {
  120. AWS_LOG_ERROR(SSO_CREDENTIALS_PROVIDER_LOG_TAG, R"(The SSO session associated with this profile has expired or is otherwise invalid. To refresh this SSO session run aws sso login with the corresponding profile.)");
  121. AWS_LOGSTREAM_TRACE(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Token cache file failed because "
  122. << (tmpAccessToken.empty()?"AccessToken was empty ":"")
  123. << (!expiration.WasParseSuccessful()? "failed to parse expiration":""));
  124. return "";
  125. }
  126. m_expiresAt = expiration;
  127. return tmpAccessToken;
  128. }
  129. else
  130. {
  131. AWS_LOGSTREAM_INFO(SSO_CREDENTIALS_PROVIDER_LOG_TAG,"Unable to open token file on path: " << ssoAccessTokenPath);
  132. return "";
  133. }
  134. }