diff --git a/crt/aws-crt-cpp b/crt/aws-crt-cpp index 35dad3a8131..2deaa9120dc 160000 --- a/crt/aws-crt-cpp +++ b/crt/aws-crt-cpp @@ -1 +1 @@ -Subproject commit 35dad3a8131dc9d761a2ccfc24808308d5ce4680 +Subproject commit 2deaa9120dc23c03ab6feb903dd1d584d6cf7025 diff --git a/prefetch_crt_dependency.sh b/prefetch_crt_dependency.sh index 073f8a0d33f..880eac604e6 100755 --- a/prefetch_crt_dependency.sh +++ b/prefetch_crt_dependency.sh @@ -3,21 +3,21 @@ # SPDX-License-Identifier: Apache-2.0. CRT_URI_PREFIX=https://codeload.github.com/awslabs -CRT_URI=${CRT_URI_PREFIX}/aws-crt-cpp/zip/35dad3a8131dc9d761a2ccfc24808308d5ce4680 # v0.35.2 +CRT_URI=${CRT_URI_PREFIX}/aws-crt-cpp/zip/2deaa9120dc23c03ab6feb903dd1d584d6cf7025 # v0.35.3 -AWS_C_AUTH_URI=${CRT_URI_PREFIX}/aws-c-auth/zip/ab03bdd996437d9097953ebb9495de71b6adc537 # v0.9.1 -AWS_C_CAL_URI=${CRT_URI_PREFIX}/aws-c-cal/zip/1b56db8ada9840e0e1036997ff4f247e155e51a5 # v0.9.8 -AWS_C_COMMON_URI=${CRT_URI_PREFIX}/aws-c-common/zip/31578beb2309330fece3fb3a66035a568a2641e7 # v0.12.5 +AWS_C_AUTH_URI=${CRT_URI_PREFIX}/aws-c-auth/zip/672feed19bb91bc389876f49aaa7c538dc879be5 # v0.9.2 +AWS_C_CAL_URI=${CRT_URI_PREFIX}/aws-c-cal/zip/de3b28840a59339f24012f25348f2c70a7ea45d6 # v0.9.11 +AWS_C_COMMON_URI=${CRT_URI_PREFIX}/aws-c-common/zip/95515a8b1ff40d5bb14f965ca4cbbe99ad1843df # v0.12.6 AWS_C_COMPRESSION_URI=${CRT_URI_PREFIX}/aws-c-compression/zip/f951ab2b819fc6993b6e5e6cfef64b1a1554bfc8 # v0.3.1 AWS_C_EVENT_STREAM_URI=${CRT_URI_PREFIX}/aws-c-event-stream/zip/31a44ff9108840a8f3fec54006218f4bc6c505e1 # v0.5.7 -AWS_C_HTTP_URI=${CRT_URI_PREFIX}/aws-c-http/zip/bbfc5a7bcf1a6c238205abcac62d5d14dd0da7ef # v0.10.5 -AWS_C_IO_URI=${CRT_URI_PREFIX}/aws-c-io/zip/1af325b54bba2e95a640a5be5ffe0b27e4ead79c # v0.23.2 +AWS_C_HTTP_URI=${CRT_URI_PREFIX}/aws-c-http/zip/07302aa4a2892adbbf95ee6d458db3bb240030d3 # v0.10.7 +AWS_C_IO_URI=${CRT_URI_PREFIX}/aws-c-io/zip/9cf142c08c28d5b1195aae09d2c05a6d17502e09 # v0.23.3 AWS_C_MQTT_URI=${CRT_URI_PREFIX}/aws-c-mqtt/zip/1d512d92709f60b74e2cafa018e69a2e647f28e9 # v0.13.3 AWS_C_S3_URI=${CRT_URI_PREFIX}/aws-c-s3/zip/332dd22c47a7ed139eee71e7f219b764ef8cdf4c # v0.9.2 AWS_C_SDKUTILS_URI=${CRT_URI_PREFIX}/aws-c-sdkutils/zip/f678bda9e21f7217e4bbf35e0d1ea59540687933 # v0.2.4 AWS_CHECKSUMS_URI=${CRT_URI_PREFIX}/aws-checksums/zip/9978ba2c33a7a259c1a6bd0f62abe26827d03b85 # v0.2.6 -AWS_LC_URI=${CRT_URI_PREFIX}/aws-lc/zip/5a9df2190d9ecab090a62030f94a6ada6789a436 # v1.62.0 -S2N_URI=${CRT_URI_PREFIX}/s2n/zip/30f40f2345a89570ed3c4cee2274942f1ebf85fa # v1.5.27 +AWS_LC_URI=${CRT_URI_PREFIX}/aws-lc/zip/7187ab572ddcdae4fa408e932d3e878c9941137b # v1.64.0 +S2N_URI=${CRT_URI_PREFIX}/s2n/zip/6aefe741f17489211f6c28e837c1a65ee66a1ef2 # v1.6.0 echo "Removing CRT" diff --git a/src/aws-cpp-sdk-core/include/aws/core/auth/CrtCredentialsProvider.h b/src/aws-cpp-sdk-core/include/aws/core/auth/CrtCredentialsProvider.h new file mode 100644 index 00000000000..ddaade76eff --- /dev/null +++ b/src/aws-cpp-sdk-core/include/aws/core/auth/CrtCredentialsProvider.h @@ -0,0 +1,58 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace Aws { +namespace Crt { +namespace Auth { +class ICredentialsProvider; +class Credentials; +} // namespace Auth +} // namespace Crt +} // namespace Aws + +namespace Aws { +namespace Auth { +/** + * A utility class for wrapping a cached crt credentials provider. + */ +class AWS_CORE_API CrtCredentialsProvider : public AWSCredentialsProvider { + public: + explicit CrtCredentialsProvider(const std::function()>& credentialsProviderFactory, + std::chrono::milliseconds providerFuturesTimeoutMs, Aws::Client::UserAgentFeature userAgentFeature, + const Aws::String& providerName); + virtual ~CrtCredentialsProvider(); + + /** + * Retrieves the credentials if found, otherwise returns empty credential set. + */ + AWSCredentials GetAWSCredentials() override; + + private: + enum class STATE { + INITIALIZED, + NOT_INITIALIZED, + }; + + static AWSCredentials ExtractCredentialsFromCrt(const Aws::Crt::Auth::Credentials& crtCredentials); + void Reload() override; + void RefreshIfExpired(); + + std::shared_ptr m_credentialsProvider; + AWSCredentials m_credentials; + std::chrono::milliseconds m_providerFuturesTimeoutMs; + Aws::Client::UserAgentFeature m_userAgentFeature; + Aws::String m_providerName; + STATE m_state{STATE::NOT_INITIALIZED}; +}; +} // namespace Auth +} // namespace Aws \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/aws/core/auth/LoginCredentialsProvider.h b/src/aws-cpp-sdk-core/include/aws/core/auth/LoginCredentialsProvider.h new file mode 100644 index 00000000000..5787ceef342 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/aws/core/auth/LoginCredentialsProvider.h @@ -0,0 +1,23 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +#include + +namespace Aws { +namespace Auth { +/** + * To support retrieving credentials of STS AssumeRole with web identity. + * Note that STS accepts request with protocol of queryxml. Calling GetAWSCredentials() will trigger (if expired) + * a query request using AWSHttpResourceClient under the hood. + */ +class AWS_CORE_API LoginCredentialsProvider : public CrtCredentialsProvider { + public: + LoginCredentialsProvider(const Aws::Client::ClientConfiguration::CredentialProviderConfiguration& config); + ~LoginCredentialsProvider() override; +}; +} // namespace Auth +} // namespace Aws diff --git a/src/aws-cpp-sdk-core/include/aws/core/auth/STSCredentialsProvider.h b/src/aws-cpp-sdk-core/include/aws/core/auth/STSCredentialsProvider.h index 74afbf6882b..f36d60c1e82 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/auth/STSCredentialsProvider.h +++ b/src/aws-cpp-sdk-core/include/aws/core/auth/STSCredentialsProvider.h @@ -3,67 +3,24 @@ * SPDX-License-Identifier: Apache-2.0. */ - #pragma once #include #include - -#include -#include +#include namespace Aws { -namespace Crt { namespace Auth { -class ICredentialsProvider; -class Credentials; -} -} -} - -namespace Aws -{ - namespace Auth - { - /** - * To support retrieving credentials of STS AssumeRole with web identity. - * Note that STS accepts request with protocol of queryxml. Calling GetAWSCredentials() will trigger (if expired) - * a query request using AWSHttpResourceClient under the hood. - */ - class AWS_CORE_API STSAssumeRoleWebIdentityCredentialsProvider : public AWSCredentialsProvider - { - public: - STSAssumeRoleWebIdentityCredentialsProvider(); - STSAssumeRoleWebIdentityCredentialsProvider(Aws::Client::ClientConfiguration::CredentialProviderConfiguration config); - virtual ~STSAssumeRoleWebIdentityCredentialsProvider(); - - /** - * Retrieves the credentials if found, otherwise returns empty credential set. - */ - AWSCredentials GetAWSCredentials() override; - - protected: - void Reload() override; - - private: - enum class STATE { - INITIALIZED, - SHUT_DOWN, - } m_state{STATE::SHUT_DOWN}; - mutable std::mutex m_refreshMutex; - mutable std::condition_variable m_refreshSignal; - std::shared_ptr m_credentialsProvider; - std::chrono::milliseconds m_providerFuturesTimeoutMs; - - // Thread-safe credential fetch coordination - mutable std::atomic m_refreshInProgress{false}; - mutable std::atomic m_refreshDone{false}; - mutable std::shared_ptr m_pendingCredentials; - - // Helper methods for credential retrieval - AWSCredentials waitForSharedCredentials() const; - AWSCredentials extractCredentialsFromCrt(const Aws::Crt::Auth::Credentials& crtCredentials) const; - AWSCredentials fetchCredentialsAsync(); - }; - } // namespace Auth -} // namespace Aws +/** + * To support retrieving credentials of STS AssumeRole with web identity. + * Note that STS accepts request with protocol of queryxml. Calling GetAWSCredentials() will trigger (if expired) + * a query request using AWSHttpResourceClient under the hood. + */ +class AWS_CORE_API STSAssumeRoleWebIdentityCredentialsProvider : public CrtCredentialsProvider { + public: + STSAssumeRoleWebIdentityCredentialsProvider(); + STSAssumeRoleWebIdentityCredentialsProvider(const Aws::Client::ClientConfiguration::CredentialProviderConfiguration& config); + ~STSAssumeRoleWebIdentityCredentialsProvider() override; +}; +} // namespace Auth +} // namespace Aws diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h b/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h index a72809b240e..0d503c86d26 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h @@ -562,6 +562,23 @@ namespace Aws */ std::chrono::milliseconds credentialCacheCacheTTL = std::chrono::minutes(50); } stsCredentialsProviderConfig; + struct LoginProviderConfig { + /** + * ARN for AWS login session. + */ + Aws::String loginSession{}; + + /** + * Overrides the login cache directory. by default the cache directory + * is located at `~/.aws/login/cache`. + */ + Aws::String loginCacheOverride{}; + + /** + * Time out for the credentials future call. + */ + std::chrono::milliseconds retrieveCredentialsFutureTimeout = std::chrono::seconds(10); + } loginCredentialProviderConfig; } credentialProviderConfig; }; diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/UserAgent.h b/src/aws-cpp-sdk-core/include/aws/core/client/UserAgent.h index 9d6120a3f69..59730dd00c7 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/UserAgent.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/UserAgent.h @@ -42,6 +42,7 @@ enum class UserAgentFeature { CREDENTIALS_SSO, CREDENTIALS_SSO_LEGACY, CREDENTIALS_PROFILE_SOURCE_PROFILE, + CREDENTIALS_LOGIN, PROTOCOL_RPC_V2_CBOR, }; diff --git a/src/aws-cpp-sdk-core/source/auth/AWSCredentialsProviderChain.cpp b/src/aws-cpp-sdk-core/source/auth/AWSCredentialsProviderChain.cpp index 55f8a9a5efc..9098602db24 100644 --- a/src/aws-cpp-sdk-core/source/auth/AWSCredentialsProviderChain.cpp +++ b/src/aws-cpp-sdk-core/source/auth/AWSCredentialsProviderChain.cpp @@ -4,13 +4,14 @@ */ #include -#include +#include #include +#include #include #include -#include #include #include +#include using namespace Aws::Auth; using namespace Aws::Utils::Threading; @@ -93,6 +94,7 @@ DefaultAWSCredentialsProviderChain::DefaultAWSCredentialsProviderChain(const Aws AddProvider(Aws::MakeShared(DefaultCredentialsProviderChainTag,config.profile)); AddProvider(Aws::MakeShared(DefaultCredentialsProviderChainTag, config)); AddProvider(Aws::MakeShared(DefaultCredentialsProviderChainTag,config.profile)); + AddProvider(Aws::MakeShared(DefaultCredentialsProviderChainTag, config)); // General HTTP Credentials (prev. known as ECS TaskRole credentials) only available when ENVIRONMENT VARIABLE is set const auto relativeUri = Aws::Environment::GetEnv(GeneralHTTPCredentialsProvider::AWS_CONTAINER_CREDENTIALS_RELATIVE_URI); diff --git a/src/aws-cpp-sdk-core/source/auth/CrtCredentialsProvider.cpp b/src/aws-cpp-sdk-core/source/auth/CrtCredentialsProvider.cpp new file mode 100644 index 00000000000..540f9bad2a1 --- /dev/null +++ b/src/aws-cpp-sdk-core/source/auth/CrtCredentialsProvider.cpp @@ -0,0 +1,91 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include +#include +#include + +using namespace Aws::Auth; +using namespace Aws::Utils; +using namespace Aws::Utils::Threading; + +namespace { +const int FIVE_MINUTES_IN_MILLIS = 5 * 60 * 1000; +} + +CrtCredentialsProvider::CrtCredentialsProvider( + const std::function()>& credentialsProviderFactory, + std::chrono::milliseconds providerFuturesTimeoutMs, Aws::Client::UserAgentFeature userAgentFeature, const Aws::String& providerName) + : m_credentialsProvider{credentialsProviderFactory()}, + m_providerFuturesTimeoutMs{providerFuturesTimeoutMs}, + m_userAgentFeature{userAgentFeature}, + m_providerName{providerName} { + if (m_credentialsProvider && m_credentialsProvider->IsValid()) { + m_state = STATE::INITIALIZED; + } +} + +CrtCredentialsProvider::~CrtCredentialsProvider() = default; + +AWSCredentials CrtCredentialsProvider::GetAWSCredentials() { + if (m_state != STATE::INITIALIZED) { + return AWSCredentials{}; + } + RefreshIfExpired(); + const ReaderLockGuard guard(m_reloadLock); + return m_credentials; +} + +void CrtCredentialsProvider::Reload() { + AWSCredentials credentials{}; + std::mutex refresh_mutex{}; + std::condition_variable refresh_condition; + bool refresh_complete{false}; + m_credentialsProvider->GetCredentials([&credentials, &refresh_mutex, &refresh_complete, &refresh_condition]( + const std::shared_ptr& crtCredentials, int errorCode) -> void { + { + const std::unique_lock lock(refresh_mutex); + (void)errorCode; + credentials = ExtractCredentialsFromCrt(*crtCredentials); + refresh_complete = true; + } + refresh_condition.notify_all(); + }); + + std::unique_lock lock(refresh_mutex); + refresh_condition.wait_for(lock, m_providerFuturesTimeoutMs, [&refresh_complete]() -> bool { return refresh_complete; }); + if (!credentials.IsEmpty()) { + credentials.AddUserAgentFeature(m_userAgentFeature); + } + m_credentials = credentials; +} + +void CrtCredentialsProvider::RefreshIfExpired() { + ReaderLockGuard guard(m_reloadLock); + if (!m_credentials.IsEmpty() && !m_credentials.ExpiresSoon(FIVE_MINUTES_IN_MILLIS)) { + return; + } + + guard.UpgradeToWriterLock(); + // double-checked lock to avoid refreshing twice + if (!m_credentials.IsEmpty() && !m_credentials.ExpiresSoon(FIVE_MINUTES_IN_MILLIS)) { + return; + } + + Reload(); +} + +AWSCredentials CrtCredentialsProvider::ExtractCredentialsFromCrt(const Aws::Crt::Auth::Credentials& crtCredentials) { + AWSCredentials credentials{}; + const auto accountIdCursor = crtCredentials.GetAccessKeyId(); + credentials.SetAWSAccessKeyId({reinterpret_cast(accountIdCursor.ptr), accountIdCursor.len}); + const auto secretKeyCursor = crtCredentials.GetSecretAccessKey(); + credentials.SetAWSSecretKey({reinterpret_cast(secretKeyCursor.ptr), secretKeyCursor.len}); + const auto expiration = crtCredentials.GetExpirationTimepointInSeconds(); + credentials.SetExpiration(DateTime{static_cast(expiration)}); + const auto sessionTokenCursor = crtCredentials.GetSessionToken(); + credentials.SetSessionToken({reinterpret_cast(sessionTokenCursor.ptr), sessionTokenCursor.len}); + return credentials; +} \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/source/auth/LoginCredentialsProvider.cpp b/src/aws-cpp-sdk-core/source/auth/LoginCredentialsProvider.cpp new file mode 100644 index 00000000000..6c1fe9e3286 --- /dev/null +++ b/src/aws-cpp-sdk-core/source/auth/LoginCredentialsProvider.cpp @@ -0,0 +1,41 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include +#include +#include +#include + +using namespace Aws::Auth; +using namespace Aws::Utils; + +namespace { +std::shared_ptr GetLoginCrtProvider( + const Aws::Client::ClientConfiguration::CredentialProviderConfiguration& credentialsConfig, + Aws::Crt::Io::ClientBootstrap* defaultClientBootstrap) { + Aws::Crt::Auth::CredentialsProviderLoginConfig loginConfig{}; + loginConfig.Bootstrap = defaultClientBootstrap; + Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(); + const Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT); + const auto tlsOptions = Aws::GetDefaultTlsConnectionOptions(); + if (tlsOptions) { + loginConfig.TlsConnectionOptions = *tlsOptions; + } + loginConfig.LoginSession = credentialsConfig.loginCredentialProviderConfig.loginSession.c_str(); + loginConfig.LoginCacheOverride = credentialsConfig.loginCredentialProviderConfig.loginCacheOverride.c_str(); + loginConfig.LoginRegion = credentialsConfig.region.c_str(); + return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderLogin(loginConfig); +} +} // namespace + +LoginCredentialsProvider::LoginCredentialsProvider( + const Aws::Client::ClientConfiguration::CredentialProviderConfiguration& credentialsConfig) + : CrtCredentialsProvider{[&credentialsConfig]() -> std::shared_ptr { + return GetLoginCrtProvider(credentialsConfig, GetDefaultClientBootstrap()); + }, + credentialsConfig.loginCredentialProviderConfig.retrieveCredentialsFutureTimeout, + Aws::Client::UserAgentFeature::CREDENTIALS_LOGIN, "LoginCredentialsProvider"} {} + +LoginCredentialsProvider::~LoginCredentialsProvider() = default; \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/source/auth/STSCredentialsProvider.cpp b/src/aws-cpp-sdk-core/source/auth/STSCredentialsProvider.cpp index cbe70fca6a6..86833a144e2 100644 --- a/src/aws-cpp-sdk-core/source/auth/STSCredentialsProvider.cpp +++ b/src/aws-cpp-sdk-core/source/auth/STSCredentialsProvider.cpp @@ -13,15 +13,11 @@ using namespace Aws::Auth; using namespace Aws::Utils; namespace { -const char* STS_LOG_TAG = "STSAssumeRoleWebIdentityCredentialsProvider"; -} - -STSAssumeRoleWebIdentityCredentialsProvider::STSAssumeRoleWebIdentityCredentialsProvider( - Aws::Client::ClientConfiguration::CredentialProviderConfiguration credentialsConfig) - : m_credentialsProvider(nullptr), m_providerFuturesTimeoutMs(credentialsConfig.stsCredentialsProviderConfig.retrieveCredentialsFutureTimeout) -{ +std::shared_ptr GetSTSCrtProvider( + const Aws::Client::ClientConfiguration::CredentialProviderConfiguration& credentialsConfig, + Aws::Crt::Io::ClientBootstrap* defaultClientBootstrap) { Aws::Crt::Auth::CredentialsProviderSTSWebIdentityConfig stsConfig{}; - stsConfig.Bootstrap = GetDefaultClientBootstrap(); + stsConfig.Bootstrap = defaultClientBootstrap; Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(); const Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT); const auto tlsOptions = Aws::GetDefaultTlsConnectionOptions(); @@ -36,33 +32,24 @@ STSAssumeRoleWebIdentityCredentialsProvider::STSAssumeRoleWebIdentityCredentials return credentialsConfig.stsCredentialsProviderConfig.sessionName; } return UUID::RandomUUID(); - }().c_str(); - - // Create underlying STS provider - auto stsProvider = Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderSTSWebIdentity(stsConfig); - if (!stsProvider || !stsProvider->IsValid()) { - AWS_LOGSTREAM_WARN(STS_LOG_TAG, "Failed to create underlying STS credentials provider"); - return; - } + }() + .c_str(); - // Wrap with caching provider - Aws::Crt::Auth::CredentialsProviderCachedConfig cachedConfig; - cachedConfig.Provider = stsProvider; - cachedConfig.CachedCredentialTTL = credentialsConfig.stsCredentialsProviderConfig.credentialCacheCacheTTL; - - m_credentialsProvider = Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderCached(cachedConfig); - if (m_credentialsProvider && m_credentialsProvider->IsValid()) { - m_state = STATE::INITIALIZED; - AWS_LOGSTREAM_INFO(STS_LOG_TAG, - "STS credentials provider initialized with cache TTL " << cachedConfig.CachedCredentialTTL.count() << " ms"); - } else { - AWS_LOGSTREAM_WARN(STS_LOG_TAG, "Failed to create cached STS credentials provider"); - } + return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderSTSWebIdentity(stsConfig); } +} // namespace + +STSAssumeRoleWebIdentityCredentialsProvider::STSAssumeRoleWebIdentityCredentialsProvider( + const Aws::Client::ClientConfiguration::CredentialProviderConfiguration& credentialsConfig) + : CrtCredentialsProvider{[&credentialsConfig]() -> std::shared_ptr { + return GetSTSCrtProvider(credentialsConfig, GetDefaultClientBootstrap()); + }, + credentialsConfig.stsCredentialsProviderConfig.retrieveCredentialsFutureTimeout, + Aws::Client::UserAgentFeature::CREDENTIALS_STS_WEB_IDENTITY_TOKEN, + "STSAssumeRoleWebIdentityCredentialsProvider"} {} Aws::String GetLegacySettingFromEnvOrProfile(const Aws::String& envVar, - std::function profileFetchFunction) -{ + std::function profileFetchFunction) { auto value = Aws::Environment::GetEnv(envVar.c_str()); if (value.empty()) { auto profile = Aws::Config::GetCachedConfigProfile(Aws::Auth::GetConfigProfileName()); @@ -72,108 +59,19 @@ Aws::String GetLegacySettingFromEnvOrProfile(const Aws::String& envVar, } STSAssumeRoleWebIdentityCredentialsProvider::STSAssumeRoleWebIdentityCredentialsProvider() - : STSAssumeRoleWebIdentityCredentialsProvider( - Aws::Client::ClientConfiguration::CredentialProviderConfiguration{ - Aws::Auth::GetConfigProfileName(), - GetLegacySettingFromEnvOrProfile("AWS_DEFAULT_REGION", - [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetRegion(); }), - {}, - { - GetLegacySettingFromEnvOrProfile("AWS_ROLE_ARN", - [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetRoleArn(); }), - GetLegacySettingFromEnvOrProfile("AWS_ROLE_SESSION_NAME", - [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetValue("role_session_name"); }), - GetLegacySettingFromEnvOrProfile("AWS_WEB_IDENTITY_TOKEN_FILE", - [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetValue("web_identity_token_file"); }) - }}) -{} - -STSAssumeRoleWebIdentityCredentialsProvider::~STSAssumeRoleWebIdentityCredentialsProvider() = default; - -AWSCredentials STSAssumeRoleWebIdentityCredentialsProvider::GetAWSCredentials() { - if (m_state != STATE::INITIALIZED) { - AWS_LOGSTREAM_DEBUG(STS_LOG_TAG, "STSCredentialsProvider is not initialized, returning empty credentials"); - return AWSCredentials{}; - } - - // Thread-safe check: If another thread is already fetching, wait for its result - auto expected = false; - if (!m_refreshInProgress.compare_exchange_strong(expected, true)) { - return waitForSharedCredentials(); - } - - // This thread will fetch the credentials - auto credentials = fetchCredentialsAsync(); - - if (!credentials.IsEmpty()) { - credentials.AddUserAgentFeature(Aws::Client::UserAgentFeature::CREDENTIALS_STS_WEB_IDENTITY_TOKEN); - } - - return credentials; -} - -void STSAssumeRoleWebIdentityCredentialsProvider::Reload() { - AWS_LOGSTREAM_DEBUG(STS_LOG_TAG, "Calling reload on STSCredentialsProvider is a no-op and no longer in the call path"); -} - -AWSCredentials STSAssumeRoleWebIdentityCredentialsProvider::waitForSharedCredentials() const { - AWS_LOGSTREAM_DEBUG(STS_LOG_TAG, "Another thread is fetching credentials, waiting for result"); - std::unique_lock lock{m_refreshMutex}; - m_refreshSignal.wait_for(lock, m_providerFuturesTimeoutMs, [this]() -> bool { return !m_refreshInProgress.load(); }); - - if (m_pendingCredentials) { - return *m_pendingCredentials; - } - - AWS_LOGSTREAM_WARN(STS_LOG_TAG, "Failed to get shared credentials after timeout"); - return AWSCredentials{}; -} - -AWSCredentials STSAssumeRoleWebIdentityCredentialsProvider::extractCredentialsFromCrt( - const Aws::Crt::Auth::Credentials& crtCredentials) const { - AWSCredentials credentials{}; - const auto accountIdCursor = crtCredentials.GetAccessKeyId(); - credentials.SetAWSAccessKeyId({reinterpret_cast(accountIdCursor.ptr), accountIdCursor.len}); - const auto secretKeyCursor = crtCredentials.GetSecretAccessKey(); - credentials.SetAWSSecretKey({reinterpret_cast(secretKeyCursor.ptr), secretKeyCursor.len}); - const auto expiration = crtCredentials.GetExpirationTimepointInSeconds(); - credentials.SetExpiration(DateTime{static_cast(expiration)}); - const auto sessionTokenCursor = crtCredentials.GetSessionToken(); - credentials.SetSessionToken({reinterpret_cast(sessionTokenCursor.ptr), sessionTokenCursor.len}); - return credentials; -} - -AWSCredentials STSAssumeRoleWebIdentityCredentialsProvider::fetchCredentialsAsync() { - AWS_LOGSTREAM_DEBUG(STS_LOG_TAG, "Initiating credential fetch from STS/cache"); - m_refreshDone.store(false); - - AWSCredentials credentials{}; - - m_credentialsProvider->GetCredentials( - [this, &credentials](std::shared_ptr crtCredentials, int errorCode) -> void { - std::unique_lock lock{m_refreshMutex}; - if (errorCode != AWS_ERROR_SUCCESS) { - m_pendingCredentials.reset(); - } else { - credentials = extractCredentialsFromCrt(*crtCredentials); - - // Store for other waiting threads - m_pendingCredentials = Aws::MakeShared(STS_LOG_TAG, credentials); - } - m_refreshDone.store(true); - m_refreshInProgress.store(false); - m_refreshSignal.notify_all(); - }); - - // Wait for completion - std::unique_lock lock{m_refreshMutex}; - auto completed = m_refreshSignal.wait_for(lock, m_providerFuturesTimeoutMs, [this]() -> bool { return m_refreshDone.load(); }); - - if (!completed) { - AWS_LOGSTREAM_ERROR(STS_LOG_TAG, "Credential fetch timed out after " << m_providerFuturesTimeoutMs.count() << "ms"); - m_refreshInProgress.store(false); - m_refreshSignal.notify_all(); - } - - return credentials; -} + : STSAssumeRoleWebIdentityCredentialsProvider(Aws::Client::ClientConfiguration::CredentialProviderConfiguration{ + Aws::Auth::GetConfigProfileName(), + GetLegacySettingFromEnvOrProfile("AWS_DEFAULT_REGION", + [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetRegion(); }), + {}, + {GetLegacySettingFromEnvOrProfile("AWS_ROLE_ARN", + [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetRoleArn(); }), + GetLegacySettingFromEnvOrProfile( + "AWS_ROLE_SESSION_NAME", + [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetValue("role_session_name"); }), + GetLegacySettingFromEnvOrProfile( + "AWS_WEB_IDENTITY_TOKEN_FILE", + [](const Aws::Config::Profile& profile) -> Aws::String { return profile.GetValue("web_identity_token_file"); })}, + {}}) {} + +STSAssumeRoleWebIdentityCredentialsProvider::~STSAssumeRoleWebIdentityCredentialsProvider() = default; diff --git a/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp b/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp index 3b427fb7ae4..124184b5383 100644 --- a/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp +++ b/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp @@ -53,6 +53,8 @@ static const char* AWS_IAM_ROLE_SESSION_NAME_ENV_VAR_COMPAT = "AWS_IAM_ROLE_SESS static const char* AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION = "role_session_name"; static const char* AWS_WEB_IDENTITY_TOKEN_FILE_ENV_VAR = "AWS_WEB_IDENTITY_TOKEN_FILE"; static const char* AWS_WEB_IDENTITY_TOKEN_FILE_CONFIG_FILE_OPTION = "web_identity_token_file"; +static const char* AWS_LOGIN_SESSION_FILE_OPTION = "login_session"; +static const char* AWS_LOGIN_CACHE_DIRECTORY_ENV_VAR = "AWS_LOGIN_CACHE_DIRECTORY"; using RequestChecksumConfigurationEnumMapping = std::pair; static const std::array REQUEST_CHECKSUM_CONFIG_MAPPING = {{ @@ -357,6 +359,12 @@ void setConfigFromEnvOrProfile(ClientConfiguration &config) AWS_WEB_IDENTITY_TOKEN_FILE_ENV_VAR, config.profileName, AWS_WEB_IDENTITY_TOKEN_FILE_CONFIG_FILE_OPTION, {}, /* allowed values */ "" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; }); + + config.credentialProviderConfig.loginCredentialProviderConfig.loginSession = + Aws::Config::GetCachedConfigValue(config.profileName, AWS_LOGIN_SESSION_FILE_OPTION); + + config.credentialProviderConfig.loginCredentialProviderConfig.loginCacheOverride = + Aws::Environment::GetEnv(AWS_LOGIN_CACHE_DIRECTORY_ENV_VAR); } ClientConfiguration::ClientConfiguration() diff --git a/src/aws-cpp-sdk-core/source/client/UserAgent.cpp b/src/aws-cpp-sdk-core/source/client/UserAgent.cpp index 58a1cce077a..909184b447a 100644 --- a/src/aws-cpp-sdk-core/source/client/UserAgent.cpp +++ b/src/aws-cpp-sdk-core/source/client/UserAgent.cpp @@ -38,7 +38,7 @@ const std::pair BUSINESS_METRIC_MAPPING[] = { {UserAgentFeature::FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED, "b"}, {UserAgentFeature::FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED, "c"}, {UserAgentFeature::ACCOUNT_ID_MODE_PREFERRED, "P"}, - {UserAgentFeature::ACCOUNT_ID_MODE_DISABLED , "Q"}, + {UserAgentFeature::ACCOUNT_ID_MODE_DISABLED, "Q"}, {UserAgentFeature::ACCOUNT_ID_MODE_REQUIRED, "R"}, {UserAgentFeature::RESOLVED_ACCOUNT_ID, "T"}, {UserAgentFeature::GZIP_REQUEST_COMPRESSION, "L"}, @@ -52,6 +52,7 @@ const std::pair BUSINESS_METRIC_MAPPING[] = { {UserAgentFeature::CREDENTIALS_SSO, "s"}, {UserAgentFeature::CREDENTIALS_SSO_LEGACY, "u"}, {UserAgentFeature::CREDENTIALS_PROFILE_SOURCE_PROFILE, "p"}, + {UserAgentFeature::CREDENTIALS_LOGIN, "AD"}, {UserAgentFeature::PROTOCOL_RPC_V2_CBOR, "M"}, }; diff --git a/tests/aws-cpp-sdk-core-tests/aws/auth/CrtCredentialsProviderTest.cpp b/tests/aws-cpp-sdk-core-tests/aws/auth/CrtCredentialsProviderTest.cpp new file mode 100644 index 00000000000..445b15d2b50 --- /dev/null +++ b/tests/aws-cpp-sdk-core-tests/aws/auth/CrtCredentialsProviderTest.cpp @@ -0,0 +1,89 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include +#include +#include + +namespace { +const char* CRT_CREDS_TEST_LOG = "CrtCredentialsProviderTest"; +} + +class MockCrtCredentialsProvider : public Aws::Crt::Auth::ICredentialsProvider { + public: + MockCrtCredentialsProvider() = default; + ~MockCrtCredentialsProvider() override = default; + + void Clear() { + m_numCalls = 0; + Aws::Queue>().swap(m_credentials); + }; + + int GetNumCalls() const { return m_numCalls; } + + void AddCredentialForReturn(std::shared_ptr credentials) { m_credentials.push(credentials); } + + bool GetCredentials(const Aws::Crt::Auth::OnCredentialsResolved& onCredentialsResolved) const override { + m_numCalls++; + if (m_credentials.empty()) { + onCredentialsResolved({}, AWS_OP_ERR); + return false; + } + const auto creds = m_credentials.front(); + onCredentialsResolved(creds, AWS_OP_SUCCESS); + m_credentials.pop(); + return true; + } + aws_credentials_provider* GetUnderlyingHandle() const noexcept override { return nullptr; } + bool IsValid() const noexcept override { return true; }; + + private: + mutable int m_numCalls = 0; + mutable Aws::Queue> m_credentials; +}; + +class MockedCredsProvider : public Aws::Auth::CrtCredentialsProvider { + public: + MockedCredsProvider(std::shared_ptr provider) + : Aws::Auth::CrtCredentialsProvider([provider]() -> std::shared_ptr { return provider; }, + std::chrono::milliseconds(10000), Aws::Client::UserAgentFeature::CREDENTIALS_LOGIN, + "MockedCredsProvider"), + m_provider{provider} {} + + private: + std::shared_ptr m_provider; +}; + +class CrtCredentialsProviderTest : public Aws::Testing::AwsCppSdkGTestSuite {}; + +TEST_F(CrtCredentialsProviderTest, ShouldCache) { + auto underlying_mock = Aws::MakeShared(CRT_CREDS_TEST_LOG); + underlying_mock->AddCredentialForReturn(Aws::MakeShared( + CRT_CREDS_TEST_LOG, Aws::Crt::ByteCursorFromCString("access"), Aws::Crt::ByteCursorFromCString("secret"), + Aws::Crt::ByteCursorFromCString("token"), + static_cast((Aws::Utils::DateTime::Now() + std::chrono::minutes(100)).Seconds()))); + MockedCredsProvider provider(underlying_mock); + EXPECT_FALSE(provider.GetAWSCredentials().IsExpiredOrEmpty()); + EXPECT_FALSE(provider.GetAWSCredentials().IsExpiredOrEmpty()); + EXPECT_FALSE(provider.GetAWSCredentials().IsExpiredOrEmpty()); + EXPECT_FALSE(provider.GetAWSCredentials().IsExpiredOrEmpty()); + EXPECT_FALSE(provider.GetAWSCredentials().IsExpiredOrEmpty()); + EXPECT_EQ(underlying_mock->GetNumCalls(), 1); +} + +TEST_F(CrtCredentialsProviderTest, ShouldRefreshIfNearExpiration) { + auto underlying_mock = Aws::MakeShared(CRT_CREDS_TEST_LOG); + underlying_mock->AddCredentialForReturn(Aws::MakeShared( + CRT_CREDS_TEST_LOG, Aws::Crt::ByteCursorFromCString("access"), Aws::Crt::ByteCursorFromCString("secret"), + Aws::Crt::ByteCursorFromCString("token"), static_cast((Aws::Utils::DateTime::Now() + std::chrono::minutes(3)).Seconds()))); + underlying_mock->AddCredentialForReturn(Aws::MakeShared( + CRT_CREDS_TEST_LOG, Aws::Crt::ByteCursorFromCString("updated_access"), Aws::Crt::ByteCursorFromCString("updated_access"), + Aws::Crt::ByteCursorFromCString("updated_access"), + static_cast((Aws::Utils::DateTime::Now() + std::chrono::minutes(3)).Seconds()))); + MockedCredsProvider provider(underlying_mock); + EXPECT_FALSE(provider.GetAWSCredentials().IsExpiredOrEmpty()); + EXPECT_FALSE(provider.GetAWSCredentials().IsExpiredOrEmpty()); + EXPECT_EQ(underlying_mock->GetNumCalls(), 2); +}