-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Crt profile credentials provider #3721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c14eda9
3dcacf1
288b730
3a600b2
0d459ee
5eb8769
444c9d8
2f38818
da5b2f0
65e5eb9
621afbf
00e8616
69959c7
233c9c0
d2bca9f
ed61b12
575f570
8a7967c
29b61af
dd2dc8e
1b0c159
01057cd
bb2223e
7fcc13a
da860d4
6b650e4
56af23d
a85bead
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,43 +1,41 @@ | ||
| #include <aws/core/auth/AWSCredentialsProvider.h> | ||
|
|
||
| #include <aws/core/config/AWSProfileConfigLoader.h> | ||
| #include <aws/core/auth/CrtCredentialsProvider.h> | ||
| #include <aws/core/auth/ProfileCredentialsProvider.h> | ||
| #include <aws/core/client/UserAgent.h> | ||
| #include <aws/core/platform/Environment.h> | ||
| #include <aws/core/platform/FileSystem.h> | ||
| #include <aws/core/utils/logging/LogMacros.h> | ||
| #include <aws/core/client/UserAgent.h> | ||
| #include <aws/core/auth/ProfileCredentialsProvider.h> | ||
| #include <aws/crt/auth/Credentials.h> | ||
|
|
||
| using namespace Aws::Auth; | ||
| using namespace Aws::Client; | ||
| using namespace Aws::FileSystem; | ||
| using namespace Aws::Utils::Threading; | ||
| using namespace Aws::Crt::Auth; | ||
|
|
||
| namespace { | ||
| const char PROFILE_PROVIDER_LOG_TAG[] = "ProfileCredentialsProvider"; | ||
| const char PROFILE_AWS_CREDENTIALS_FILE[] = "AWS_SHARED_CREDENTIALS_FILE"; | ||
| const char PROFILE_DEFAULT_CREDENTIALS_FILE[] = "credentials"; | ||
| const char PROFILE_PROFILE_DIRECTORY[] = ".aws"; | ||
| } | ||
| const long DEFAULT_REFRESH_RATE_MS = 50000; | ||
| } // namespace | ||
|
|
||
| class ProfileCredentialsProvider::ProfileCredentialsProviderImp : public AWSCredentialsProvider { | ||
| class ProfileCredentialsProvider::ProfileCredentialsProviderImp : public CrtCredentialsProvider { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
chefs kiss |
||
| public: | ||
| ProfileCredentialsProviderImp(long refreshRateMs) | ||
| : m_profileToUse(Aws::Auth::GetConfigProfileName()), | ||
| m_credentialsFileLoader(GetCredentialsProfileFilename()), | ||
| m_loadFrequencyMs(refreshRateMs) { | ||
| AWS_LOGSTREAM_INFO(PROFILE_PROVIDER_LOG_TAG, "Setting provider to read credentials from " | ||
| << GetCredentialsProfileFilename() << " for credentials file" | ||
| << " and " << GetConfigProfileFilename() << " for the config file " | ||
| << ", for use with profile " << m_profileToUse); | ||
| } | ||
|
|
||
| ProfileCredentialsProviderImp(const char* profile, long refreshRateMs) | ||
| : m_profileToUse(profile), m_credentialsFileLoader(GetCredentialsProfileFilename()), m_loadFrequencyMs(refreshRateMs) { | ||
| AWS_LOGSTREAM_INFO(PROFILE_PROVIDER_LOG_TAG, "Setting provider to read credentials from " | ||
| << GetCredentialsProfileFilename() << " for credentials file" | ||
| << " and " << GetConfigProfileFilename() << " for the config file " | ||
| << ", for use with profile " << m_profileToUse); | ||
| } | ||
| ProfileCredentialsProviderImp() | ||
| : CrtCredentialsProvider( | ||
| []() -> std::shared_ptr<ICredentialsProvider> { | ||
| CredentialsProviderProfileConfig config; | ||
| return CredentialsProvider::CreateCredentialsProviderProfile(config); | ||
| }, | ||
| std::chrono::milliseconds(DEFAULT_REFRESH_RATE_MS), UserAgentFeature::CREDENTIALS_PROFILE, "ProfileCredentialsProvider") {} | ||
|
|
||
| ProfileCredentialsProviderImp(const char* profile) | ||
| : CrtCredentialsProvider( | ||
| [profile]() -> std::shared_ptr<ICredentialsProvider> { | ||
| CredentialsProviderProfileConfig config; | ||
| config.ProfileNameOverride = Aws::Crt::ByteCursorFromCString(profile); | ||
| return CredentialsProvider::CreateCredentialsProviderProfile(config); | ||
| }, | ||
| std::chrono::milliseconds(DEFAULT_REFRESH_RATE_MS), Aws::Client::UserAgentFeature::CREDENTIALS_PROFILE, | ||
| "ProfileCredentialsProvider") {} | ||
|
|
||
| static Aws::String GetCredentialsProfileFilename() { | ||
| auto credentialsFileNameFromVar = Aws::Environment::GetEnv(PROFILE_AWS_CREDENTIALS_FILE); | ||
|
|
@@ -57,55 +55,18 @@ class ProfileCredentialsProvider::ProfileCredentialsProviderImp : public AWSCred | |
| return {}; | ||
| } | ||
| } | ||
|
|
||
| AWSCredentials GetAWSCredentials() override { | ||
| RefreshIfExpired(); | ||
| ReaderLockGuard guard(m_reloadLock); | ||
| const Aws::Map<Aws::String, Aws::Config::Profile>& profiles = m_credentialsFileLoader.GetProfiles(); | ||
| auto credsFileProfileIter = profiles.find(m_profileToUse); | ||
|
|
||
| if (credsFileProfileIter != profiles.end()) { | ||
| AWSCredentials credentials = credsFileProfileIter->second.GetCredentials(); | ||
| if (!credentials.IsEmpty()) { | ||
| credentials.AddUserAgentFeature(UserAgentFeature::CREDENTIALS_PROFILE); | ||
| } | ||
| return credentials; | ||
| } | ||
|
|
||
| return AWSCredentials(); | ||
| } | ||
|
|
||
| void Reload() override { | ||
| m_credentialsFileLoader.Load(); | ||
| AWSCredentialsProvider::Reload(); | ||
| } | ||
|
|
||
| private: | ||
| Aws::String m_profileToUse; | ||
| Aws::Config::AWSConfigFileProfileConfigLoader m_credentialsFileLoader; | ||
| long m_loadFrequencyMs; | ||
|
|
||
| void RefreshIfExpired() { | ||
| ReaderLockGuard guard(m_reloadLock); | ||
| if (!IsTimeToRefresh(m_loadFrequencyMs)) { | ||
| return; | ||
| } | ||
|
|
||
| guard.UpgradeToWriterLock(); | ||
| if (!IsTimeToRefresh(m_loadFrequencyMs)) // double-checked lock to avoid refreshing twice | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| Reload(); | ||
| } | ||
| }; | ||
|
|
||
| ProfileCredentialsProvider::ProfileCredentialsProvider(long refreshRateMs) | ||
| : m_impl(std::make_shared<ProfileCredentialsProviderImp>(refreshRateMs)) {} | ||
| ProfileCredentialsProvider::ProfileCredentialsProvider(long refreshRateMs) : m_impl(std::make_shared<ProfileCredentialsProviderImp>()) { | ||
| (void)refreshRateMs; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit use the macro |
||
| } | ||
|
|
||
| ProfileCredentialsProvider::ProfileCredentialsProvider(const char* profile, long refreshRateMs) | ||
| : m_impl(std::make_shared<ProfileCredentialsProviderImp>(profile, refreshRateMs)) {} | ||
| : m_impl(std::make_shared<ProfileCredentialsProviderImp>(profile)) { | ||
| (void)refreshRateMs; | ||
| } | ||
|
|
||
| void ProfileCredentialsProvider::Reload() {} | ||
|
|
||
| Aws::String ProfileCredentialsProvider::GetCredentialsProfileFilename() { | ||
| return ProfileCredentialsProviderImp::GetCredentialsProfileFilename(); | ||
|
|
@@ -114,5 +75,3 @@ Aws::String ProfileCredentialsProvider::GetCredentialsProfileFilename() { | |
| Aws::String ProfileCredentialsProvider::GetProfileDirectory() { return ProfileCredentialsProviderImp::GetProfileDirectory(); } | ||
|
|
||
| AWSCredentials ProfileCredentialsProvider::GetAWSCredentials() { return m_impl->GetAWSCredentials(); } | ||
|
|
||
| void ProfileCredentialsProvider::Reload() { m_impl->Reload(); } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| /** | ||
| * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| * SPDX-License-Identifier: Apache-2.0. | ||
| */ | ||
|
|
||
| #include <aws/core/Aws.h> | ||
| #include <aws/core/auth/AWSCredentialsProviderChain.h> | ||
| #include <aws/core/platform/Environment.h> | ||
| #include <aws/core/utils/FileSystemUtils.h> | ||
| #include <aws/testing/AwsTestHelpers.h> | ||
| #include <aws/testing/platform/PlatformTesting.h> | ||
| #include <gtest/gtest.h> | ||
|
|
||
| using namespace Aws; | ||
| using namespace Aws::Auth; | ||
| using namespace Aws::Environment; | ||
| using namespace Aws::Utils; | ||
|
|
||
| /** | ||
| * Integration tests for DefaultAWSCredentialsProviderChain | ||
| * These tests use real credentials from the environment (e.g., via ada) | ||
| */ | ||
| class DefaultCredentialsProviderChainIntegrationTest : public testing::Test | ||
| { | ||
| protected: | ||
| DefaultCredentialsProviderChainIntegrationTest() | ||
| { | ||
| m_options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Debug; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we really aught to make this a official construct, i write this code at least times a week. not here for this PR, but something we should about. might help people use the SDK correctly better
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🫡 |
||
| InitAPI(m_options); | ||
| } | ||
|
|
||
| ~DefaultCredentialsProviderChainIntegrationTest() | ||
| { | ||
| ShutdownAPI(m_options); | ||
| } | ||
|
|
||
| SDKOptions m_options; | ||
| }; | ||
|
|
||
| TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainResolvesCredentialsFromEnvironment) | ||
| { | ||
| // This test assumes AWS credentials are set via environment (e.g., ada) | ||
| DefaultAWSCredentialsProviderChain chain; | ||
| auto creds = chain.GetAWSCredentials(); | ||
|
|
||
| // Should get valid credentials from environment | ||
| EXPECT_FALSE(creds.IsEmpty()); | ||
| EXPECT_FALSE(creds.GetAWSAccessKeyId().empty()); | ||
| EXPECT_FALSE(creds.GetAWSSecretKey().empty()); | ||
| } | ||
|
|
||
| TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainCachesCredentials) | ||
| { | ||
| DefaultAWSCredentialsProviderChain chain; | ||
|
|
||
| // First call | ||
| auto creds1 = chain.GetAWSCredentials(); | ||
| EXPECT_FALSE(creds1.IsEmpty()); | ||
|
|
||
| // Second call should return same cached credentials | ||
| auto creds2 = chain.GetAWSCredentials(); | ||
| EXPECT_FALSE(creds2.IsEmpty()); | ||
| EXPECT_EQ(creds1.GetAWSAccessKeyId(), creds2.GetAWSAccessKeyId()); | ||
| EXPECT_EQ(creds1.GetAWSSecretKey(), creds2.GetAWSSecretKey()); | ||
| } | ||
|
|
||
| TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainWithCustomProfile) | ||
| { | ||
| // Create temporary credentials file with custom profile | ||
| TempFile credsFile{std::ios_base::out | std::ios_base::trunc}; | ||
| credsFile << "[custom-test-profile]\n"; | ||
| credsFile << "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n"; | ||
| credsFile << "aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n"; | ||
| credsFile.close(); | ||
|
|
||
| // Create temporary config file | ||
| TempFile configFile{std::ios_base::out | std::ios_base::trunc}; | ||
| configFile << "[profile custom-test-profile]\n"; | ||
| configFile << "region = us-west-2\n"; | ||
| configFile.close(); | ||
|
|
||
| const EnvironmentRAII environmentRAII{{ | ||
| {"AWS_SHARED_CREDENTIALS_FILE", credsFile.GetFileName()}, | ||
| {"AWS_CONFIG_FILE", configFile.GetFileName()}, | ||
| {"AWS_ACCESS_KEY_ID", ""}, | ||
| {"AWS_SECRET_ACCESS_KEY", ""}, | ||
| }}; | ||
|
|
||
| Aws::Config::ReloadCachedConfigFile(); | ||
|
|
||
| Client::ClientConfiguration::CredentialProviderConfiguration config; | ||
| config.profile = "custom-test-profile"; | ||
| DefaultAWSCredentialsProviderChain chain(config); | ||
|
|
||
| auto creds = chain.GetAWSCredentials(); | ||
|
|
||
| EXPECT_STREQ("AKIAIOSFODNN7EXAMPLE", creds.GetAWSAccessKeyId().c_str()); | ||
| EXPECT_STREQ("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", creds.GetAWSSecretKey().c_str()); | ||
| } | ||
|
|
||
| TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainWithProcessCredentials) | ||
| { | ||
| // Create temporary config file with credential_process | ||
| TempFile configFile{std::ios_base::out | std::ios_base::trunc}; | ||
| configFile << "[default]\n"; | ||
| configFile << "credential_process = echo '{\"Version\": 1, \"AccessKeyId\": \"AKIAPROCESSEXAMPLE\", \"SecretAccessKey\": \"ProcessSecretKeyExample\"}'\n"; | ||
| configFile.close(); | ||
|
|
||
| // Create empty credentials file to override ~/.aws/credentials | ||
| TempFile credsFile{std::ios_base::out | std::ios_base::trunc}; | ||
| credsFile << "[default]\n"; | ||
| credsFile.close(); | ||
|
|
||
| const EnvironmentRAII environmentRAII{{ | ||
| {"AWS_CONFIG_FILE", configFile.GetFileName()}, | ||
| {"AWS_SHARED_CREDENTIALS_FILE", credsFile.GetFileName()}, | ||
| {"AWS_ACCESS_KEY_ID", ""}, | ||
| {"AWS_SECRET_ACCESS_KEY", ""}, | ||
| {"AWS_SESSION_TOKEN", ""}, | ||
| {"AWS_EC2_METADATA_DISABLED", "true"}, | ||
| }}; | ||
|
|
||
| Aws::Config::ReloadCachedConfigFile(); | ||
|
|
||
| DefaultAWSCredentialsProviderChain chain; | ||
| auto creds = chain.GetAWSCredentials(); | ||
|
|
||
| EXPECT_STREQ("AKIAPROCESSEXAMPLE", creds.GetAWSAccessKeyId().c_str()); | ||
| EXPECT_STREQ("ProcessSecretKeyExample", creds.GetAWSSecretKey().c_str()); | ||
| } | ||
|
|
||
| TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainPrecedenceEnvironmentOverProfile) | ||
| { | ||
| // Create temporary credentials file | ||
| TempFile credsFile{std::ios_base::out | std::ios_base::trunc}; | ||
| credsFile << "[default]\n"; | ||
| credsFile << "aws_access_key_id = AKIAPROFILEEXAMPLE\n"; | ||
| credsFile << "aws_secret_access_key = ProfileSecretKeyExample\n"; | ||
| credsFile.close(); | ||
|
|
||
| const EnvironmentRAII environmentRAII{{ | ||
| {"AWS_SHARED_CREDENTIALS_FILE", credsFile.GetFileName()}, | ||
| {"AWS_ACCESS_KEY_ID", "AKIAENVEXAMPLE"}, | ||
| {"AWS_SECRET_ACCESS_KEY", "EnvSecretKeyExample"}, | ||
| }}; | ||
|
|
||
| Aws::Config::ReloadCachedConfigFile(); | ||
|
|
||
| DefaultAWSCredentialsProviderChain chain; | ||
| auto creds = chain.GetAWSCredentials(); | ||
|
|
||
| // Environment should take precedence | ||
| EXPECT_STREQ("AKIAENVEXAMPLE", creds.GetAWSAccessKeyId().c_str()); | ||
| EXPECT_STREQ("EnvSecretKeyExample", creds.GetAWSSecretKey().c_str()); | ||
| } | ||
|
|
||
| TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainHandlesSessionToken) | ||
| { | ||
| // Create temporary credentials file with session token | ||
| TempFile credsFile{std::ios_base::out | std::ios_base::trunc}; | ||
| credsFile << "[default]\n"; | ||
| credsFile << "aws_access_key_id = ASIATEMPORARYEXAMPLE\n"; | ||
| credsFile << "aws_secret_access_key = TempSecretKeyExample\n"; | ||
| credsFile << "aws_session_token = TempSessionTokenExample123\n"; | ||
| credsFile.close(); | ||
|
|
||
| const EnvironmentRAII environmentRAII{{ | ||
| {"AWS_SHARED_CREDENTIALS_FILE", credsFile.GetFileName()}, | ||
| {"AWS_ACCESS_KEY_ID", ""}, | ||
| {"AWS_SECRET_ACCESS_KEY", ""}, | ||
| {"AWS_EC2_METADATA_DISABLED", "true"}, | ||
| }}; | ||
|
|
||
| Aws::Config::ReloadCachedConfigFile(); | ||
|
|
||
| DefaultAWSCredentialsProviderChain chain; | ||
| auto creds = chain.GetAWSCredentials(); | ||
|
|
||
| EXPECT_STREQ("ASIATEMPORARYEXAMPLE", creds.GetAWSAccessKeyId().c_str()); | ||
| EXPECT_STREQ("TempSecretKeyExample", creds.GetAWSSecretKey().c_str()); | ||
| EXPECT_STREQ("TempSessionTokenExample123", creds.GetSessionToken().c_str()); | ||
| } | ||
|
|
||
| TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainReturnsEmptyWhenNoCredentialsAvailable) | ||
| { | ||
| const EnvironmentRAII environmentRAII{{ | ||
| {"AWS_ACCESS_KEY_ID", ""}, | ||
| {"AWS_SECRET_ACCESS_KEY", ""}, | ||
| {"AWS_SHARED_CREDENTIALS_FILE", "/nonexistent/credentials"}, | ||
| {"AWS_CONFIG_FILE", "/nonexistent/config"}, | ||
| {"AWS_EC2_METADATA_DISABLED", "true"}, | ||
| }}; | ||
|
|
||
| Aws::Config::ReloadCachedConfigFile(); | ||
|
|
||
| DefaultAWSCredentialsProviderChain chain; | ||
| auto creds = chain.GetAWSCredentials(); | ||
|
|
||
| EXPECT_TRUE(creds.IsEmpty()); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: no actual change in file so lets from it from the PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alright