-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Adding CRTProfileCredentialsProvider for CRT-based profile credential #3713
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
Changes from 4 commits
c14eda9
3dcacf1
288b730
3a600b2
0d459ee
5eb8769
444c9d8
2f38818
da5b2f0
65e5eb9
621afbf
00e8616
69959c7
233c9c0
d2bca9f
ed61b12
575f570
8a7967c
29b61af
dd2dc8e
1b0c159
01057cd
bb2223e
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 |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #pragma once | ||
|
|
||
| #include <aws/core/Core_EXPORTS.h> | ||
| #include <aws/core/auth/AWSCredentialsProvider.h> | ||
| #include <memory> | ||
|
|
||
| namespace Aws | ||
| { | ||
| namespace Auth | ||
| { | ||
| /** | ||
| * CRT-based credentials provider that sources credentials from config files with full SEP compliance. | ||
| * Supports assume role, credential_source, role chaining, and all SEP scenarios. | ||
| */ | ||
| class AWS_CORE_API CRTProfileCredentialsProvider : public AWSCredentialsProvider | ||
| { | ||
| public: | ||
| /** | ||
| * Initializes with refreshRateMs as the frequency at which the file is reparsed in milliseconds. Defaults to 5 minutes. | ||
| */ | ||
| CRTProfileCredentialsProvider(long refreshRateMs = REFRESH_THRESHOLD); | ||
|
|
||
| /** | ||
| * Initializes with a profile override and | ||
| * refreshRateMs as the frequency at which the file is reparsed in milliseconds. Defaults to 5 minutes. | ||
| */ | ||
| CRTProfileCredentialsProvider(const char* profile, long refreshRateMs = REFRESH_THRESHOLD); | ||
|
|
||
| /** | ||
| * Retrieves the credentials if found, otherwise returns empty credential set. | ||
| */ | ||
| AWSCredentials GetAWSCredentials() override; | ||
|
|
||
| /** | ||
| * Returns the fullpath of the calculated credentials profile file | ||
| */ | ||
| static Aws::String GetCredentialsProfileFilename(); | ||
|
|
||
| /** | ||
| * Returns the directory storing the profile file. | ||
| */ | ||
| static Aws::String GetProfileDirectory(); | ||
|
|
||
| protected: | ||
| void Reload() override; | ||
| class CRTProfileCredentialsProviderImp; | ||
| std::shared_ptr<CRTProfileCredentialsProviderImp> m_impl; | ||
|
Collaborator
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 |
||
|
|
||
| private: | ||
| void RefreshIfExpired(); | ||
|
Collaborator
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. why do we still need this private method? cant we jsut move it to the impl? |
||
| }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| #include <aws/core/auth/CRTProfileCredentialsProvider.h> | ||
| #include <aws/core/Globals.h> | ||
| #include <aws/core/platform/Environment.h> | ||
| #include <condition_variable> | ||
| #include <mutex> | ||
| #include <aws/crt/auth/Credentials.h> | ||
|
|
||
| using namespace Aws::Auth; | ||
|
|
||
| class CRTProfileCredentialsProvider::CRTProfileCredentialsProviderImp { | ||
| public: | ||
| Aws::String m_profileToUse; | ||
| Aws::Config::AWSConfigFileProfileConfigLoader m_credentialsFileLoader; | ||
| long m_loadFrequencyMs; | ||
|
|
||
| CRTProfileCredentialsProviderImp(long refreshRateMs) : | ||
| m_profileToUse(Aws::Auth::GetConfigProfileName()), | ||
|
Collaborator
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 should still keep data members as private. ideally the impl should just be the interface of a aws credentials provider |
||
| m_credentialsFileLoader(GetCredentialsProfileFilename()), | ||
| m_loadFrequencyMs(refreshRateMs){} | ||
|
|
||
| CRTProfileCredentialsProviderImp(const char* profile, long refreshRateMs) : | ||
| m_profileToUse(profile), | ||
|
Collaborator
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 formatting -- does clang format like this? if so diregard |
||
| m_credentialsFileLoader(GetCredentialsProfileFilename()), | ||
| m_loadFrequencyMs(refreshRateMs){} | ||
| }; | ||
|
|
||
| Aws::String CRTProfileCredentialsProvider::GetCredentialsProfileFilename() | ||
| { | ||
| auto credentialsFileNameFromVar = Aws::Environment::GetEnv(AWS_CREDENTIALS_FILE); | ||
|
|
||
| if (credentialsFileNameFromVar.empty()) | ||
| { | ||
| return Aws::FileSystem::GetHomeDirectory() + PROFILE_DIRECTORY + PATH_DELIM + DEFAULT_CREDENTIALS_FILE; | ||
| } | ||
| else | ||
| { | ||
| return credentialsFileNameFromVar; | ||
| } | ||
| } | ||
|
|
||
| Aws::String CRTProfileCredentialsProvider::GetProfileDirectory() | ||
| { | ||
| Aws::String credentialsFileName = GetCredentialsProfileFilename(); | ||
| auto lastSeparator = credentialsFileName.find_last_of(PATH_DELIM); | ||
| if (lastSeparator != std::string::npos) | ||
| { | ||
| return credentialsFileName.substr(0, lastSeparator); | ||
| } | ||
| else | ||
| { | ||
| return {}; | ||
| } | ||
| } | ||
|
|
||
| CRTProfileCredentialsProvider::CRTProfileCredentialsProvider(long refreshRateMs): | ||
| m_impl(std::make_shared<CRTProfileCredentialsProviderImp>(refreshRateMs)) | ||
| { | ||
| AWS_LOGSTREAM_INFO(PROFILE_LOG_TAG, "Setting provider to read credentials from " << GetCredentialsProfileFilename() << " for credentials file" | ||
| << " and " << GetConfigProfileFilename() << " for the config file " | ||
| << ", for use with profile " << m_impl->m_profileToUse); | ||
| } | ||
|
|
||
| CRTProfileCredentialsProvider::CRTProfileCredentialsProvider(const char* profile, long refreshRateMs) : | ||
| m_impl(std::make_shared<CRTProfileCredentialsProviderImp>(profile, refreshRateMs)) | ||
| { | ||
| AWS_LOGSTREAM_INFO(PROFILE_LOG_TAG, "Setting provider to read credentials from " << GetCredentialsProfileFilename() << " for credentials file" | ||
| << " and " << GetConfigProfileFilename() << " for the config file " | ||
| << ", for use with profile " << m_impl->m_profileToUse); | ||
| } | ||
|
|
||
| AWSCredentials CRTProfileCredentialsProvider::GetAWSCredentials() | ||
| { | ||
| RefreshIfExpired(); | ||
|
Collaborator
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. this should just call |
||
| ReaderLockGuard guard(m_reloadLock); | ||
| const Aws::Map<Aws::String, Aws::Config::Profile>& profiles = m_impl->m_credentialsFileLoader.GetProfiles(); | ||
| auto credsFileProfileIter = profiles.find(m_impl->m_profileToUse); | ||
|
|
||
| if(credsFileProfileIter != profiles.end()) | ||
| { | ||
| AWSCredentials credentials = credsFileProfileIter->second.GetCredentials(); | ||
| if (!credentials.IsEmpty()) { | ||
| credentials.AddUserAgentFeature(UserAgentFeature::CREDENTIALS_PROFILE); | ||
| } | ||
| return credentials; | ||
| } | ||
|
|
||
| return AWSCredentials(); | ||
| } | ||
|
|
||
|
|
||
| void CRTProfileCredentialsProvider::Reload() | ||
| { | ||
| m_impl->m_credentialsFileLoader.Load(); | ||
|
Collaborator
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. this should just call |
||
| AWSCredentialsProvider::Reload(); | ||
| } | ||
|
|
||
| void CRTProfileCredentialsProvider::RefreshIfExpired() | ||
| { | ||
| ReaderLockGuard guard(m_reloadLock); | ||
|
Collaborator
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. this should just call |
||
| if (!IsTimeToRefresh(m_impl->m_loadFrequencyMs)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| guard.UpgradeToWriterLock(); | ||
| if (!IsTimeToRefresh(m_impl->m_loadFrequencyMs)) // double-checked lock to avoid refreshing twice | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| Reload(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| #include <aws/core/auth/CRTProfileCredentialsProvider.h> | ||
| #include <aws/core/platform/Environment.h> | ||
| #include <aws/core/platform/FileSystem.h> | ||
| #include <aws/core/utils/FileSystemUtils.h> | ||
| #include <aws/testing/AwsCppSdkGTestSuite.h> | ||
| #include <fstream> | ||
|
|
||
| using namespace Aws::Auth; | ||
|
|
||
| class CRTProfileCredentialsProviderTest : public Aws::Testing::AwsCppSdkGTestSuite { | ||
| protected: | ||
| void SetUp() override { | ||
| SaveEnv("AWS_CONFIG_FILE"); | ||
| SaveEnv("AWS_SHARED_CREDENTIALS_FILE"); | ||
| SaveEnv("AWS_PROFILE"); | ||
|
|
||
| Aws::FileSystem::CreateDirectoryIfNotExists(GetTestDir().c_str()); | ||
| m_configFile = GetTestDir() + "/config"; | ||
| m_credsFile = GetTestDir() + "/credentials"; | ||
|
|
||
| Aws::Environment::SetEnv("AWS_CONFIG_FILE", m_configFile.c_str(), 1); | ||
| Aws::Environment::SetEnv("AWS_SHARED_CREDENTIALS_FILE", m_credsFile.c_str(), 1); | ||
| } | ||
|
|
||
| void TearDown() override { | ||
| Aws::FileSystem::RemoveFileIfExists(m_configFile.c_str()); | ||
| Aws::FileSystem::RemoveFileIfExists(m_credsFile.c_str()); | ||
| RestoreEnv(); | ||
| } | ||
|
|
||
| void SaveEnv(const char* name) { | ||
| m_savedEnv.emplace_back(name, Aws::Environment::GetEnv(name)); | ||
| } | ||
|
|
||
| void RestoreEnv() { | ||
| for (const auto& pair : m_savedEnv) { | ||
| if (pair.second.empty()) { | ||
| Aws::Environment::UnSetEnv(pair.first); | ||
| } else { | ||
| Aws::Environment::SetEnv(pair.first, pair.second.c_str(), 1); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Aws::String GetTestDir() { | ||
| return Aws::FileSystem::GetHomeDirectory() + "/.aws_test_" + | ||
| Aws::Utils::StringUtils::to_string(std::this_thread::get_id()); | ||
| } | ||
|
|
||
| Aws::String m_configFile; | ||
| Aws::String m_credsFile; | ||
| Aws::Vector<std::pair<const char*, Aws::String>> m_savedEnv; | ||
| }; | ||
|
|
||
| TEST_F(CRTProfileCredentialsProviderTest, LoadStaticCredentials) { | ||
| std::ofstream creds(m_credsFile.c_str()); | ||
| creds << "[default]\n"; | ||
| creds << "aws_access_key_id = AKIATEST123\n"; | ||
| creds << "aws_secret_access_key = SecretKey456\n"; | ||
| creds.close(); | ||
|
|
||
| CRTProfileCredentialsProvider provider; | ||
| auto credentials = provider.GetAWSCredentials(); | ||
|
|
||
| EXPECT_STREQ("AKIATEST123", credentials.GetAWSAccessKeyId().c_str()); | ||
| EXPECT_STREQ("SecretKey456", credentials.GetAWSSecretKey().c_str()); | ||
| } | ||
|
|
||
| TEST_F(CRTProfileCredentialsProviderTest, LoadNamedProfile) { | ||
| std::ofstream creds(m_credsFile.c_str()); | ||
| creds << "[default]\n"; | ||
| creds << "aws_access_key_id = DefaultKey\n"; | ||
| creds << "aws_secret_access_key = DefaultSecret\n"; | ||
| creds << "\n"; | ||
| creds << "[test-profile]\n"; | ||
| creds << "aws_access_key_id = TestKey\n"; | ||
| creds << "aws_secret_access_key = TestSecret\n"; | ||
| creds.close(); | ||
|
|
||
| CRTProfileCredentialsProvider provider("test-profile"); | ||
| auto credentials = provider.GetAWSCredentials(); | ||
|
|
||
| EXPECT_STREQ("TestKey", credentials.GetAWSAccessKeyId().c_str()); | ||
| EXPECT_STREQ("TestSecret", credentials.GetAWSSecretKey().c_str()); | ||
| } | ||
|
|
||
| TEST_F(CRTProfileCredentialsProviderTest, LoadWithSessionToken) { | ||
| std::ofstream creds(m_credsFile.c_str()); | ||
| creds << "[default]\n"; | ||
| creds << "aws_access_key_id = AKIATEST\n"; | ||
| creds << "aws_secret_access_key = SecretKey\n"; | ||
| creds << "aws_session_token = SessionToken123\n"; | ||
| creds.close(); | ||
|
|
||
| CRTProfileCredentialsProvider provider; | ||
| auto credentials = provider.GetAWSCredentials(); | ||
|
|
||
| EXPECT_STREQ("AKIATEST", credentials.GetAWSAccessKeyId().c_str()); | ||
| EXPECT_STREQ("SecretKey", credentials.GetAWSSecretKey().c_str()); | ||
| EXPECT_STREQ("SessionToken123", credentials.GetSessionToken().c_str()); | ||
| } | ||
|
|
||
| TEST_F(CRTProfileCredentialsProviderTest, MissingProfileReturnsEmpty) { | ||
| std::ofstream creds(m_credsFile.c_str()); | ||
| creds << "[default]\n"; | ||
| creds << "aws_access_key_id = DefaultKey\n"; | ||
| creds << "aws_secret_access_key = DefaultSecret\n"; | ||
| creds.close(); | ||
|
|
||
| CRTProfileCredentialsProvider provider("nonexistent"); | ||
| auto credentials = provider.GetAWSCredentials(); | ||
|
|
||
| EXPECT_TRUE(credentials.IsEmpty()); | ||
| } | ||
|
|
||
| TEST_F(CRTProfileCredentialsProviderTest, DISABLED_ProcessCredentials) { | ||
| std::ofstream config(m_configFile.c_str()); | ||
| config << "[default]\n"; | ||
| config << "credential_process = echo '{\"Version\": 1, \"AccessKeyId\": \"ProcessKey\", \"SecretAccessKey\": \"ProcessSecret\"}'\n"; | ||
| config.close(); | ||
|
|
||
| CRTProfileCredentialsProvider provider; | ||
| auto credentials = provider.GetAWSCredentials(); | ||
|
|
||
| EXPECT_STREQ("ProcessKey", credentials.GetAWSAccessKeyId().c_str()); | ||
| EXPECT_STREQ("ProcessSecret", credentials.GetAWSSecretKey().c_str()); | ||
| } |
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.
lets not name this
CRTProfileCredentialsProviderbecause its just going to be the newProfileCredentialsProvider. lets just name itProfileCredentialsProvider. also please add aAWS_DEPRECATEDto the old class so that people using it directly will get a warning to update to the new one.