Skip to content

Commit 56af23d

Browse files
committed
Adding ProfileCredentialsProvider refresh parameter and comprehensive credential chain tests
1 parent da860d4 commit 56af23d

6 files changed

Lines changed: 759 additions & 47 deletions

File tree

src/aws-cpp-sdk-core/include/aws/core/auth/ProfileCredentialsProvider.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#pragma once
22

33
#include <aws/core/Core_EXPORTS.h>
4-
#include <aws/core/utils/memory/stl/AWSString.h>
54
#include <aws/core/auth/AWSCredentials.h>
65
#include <aws/core/auth/AWSCredentialsProvider.h>
6+
#include <aws/core/utils/memory/stl/AWSString.h>
7+
78
#include <memory>
89

910
namespace Aws {
@@ -17,13 +18,13 @@ class AWS_CORE_API ProfileCredentialsProvider : public AWSCredentialsProvider {
1718
/**
1819
* Initializes with refreshRateMs as the frequency at which the file is reparsed in milliseconds. Defaults to 5 minutes.
1920
*/
20-
ProfileCredentialsProvider();
21+
ProfileCredentialsProvider(long refreshRateMs = REFRESH_THRESHOLD);
2122

2223
/**
2324
* Initializes with a profile override and
2425
* refreshRateMs as the frequency at which the file is reparsed in milliseconds. Defaults to 5 minutes.
2526
*/
26-
ProfileCredentialsProvider(const char* profile);
27+
ProfileCredentialsProvider(const char* profile, long refreshRateMs = REFRESH_THRESHOLD);
2728

2829
/**
2930
* Retrieves the credentials if found, otherwise returns empty credential set.
@@ -40,6 +41,9 @@ class AWS_CORE_API ProfileCredentialsProvider : public AWSCredentialsProvider {
4041
*/
4142
static Aws::String GetProfileDirectory();
4243

44+
protected:
45+
void Reload() override;
46+
4347
private:
4448
class ProfileCredentialsProviderImp;
4549
std::shared_ptr<ProfileCredentialsProviderImp> m_impl;

src/aws-cpp-sdk-core/source/auth/ProfileCredentialsProvider.cpp

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
#include <aws/core/auth/CrtCredentialsProvider.h>
2+
#include <aws/core/auth/ProfileCredentialsProvider.h>
3+
#include <aws/core/client/UserAgent.h>
14
#include <aws/core/platform/Environment.h>
25
#include <aws/core/platform/FileSystem.h>
3-
#include <aws/core/client/UserAgent.h>
4-
#include <aws/core/auth/ProfileCredentialsProvider.h>
5-
#include <aws/core/auth/CrtCredentialsProvider.h>
66
#include <aws/crt/auth/Credentials.h>
77

88
using namespace Aws::Auth;
@@ -14,58 +14,59 @@ namespace {
1414
const char PROFILE_AWS_CREDENTIALS_FILE[] = "AWS_SHARED_CREDENTIALS_FILE";
1515
const char PROFILE_DEFAULT_CREDENTIALS_FILE[] = "credentials";
1616
const char PROFILE_PROFILE_DIRECTORY[] = ".aws";
17-
}
17+
const long DEFAULT_REFRESH_RATE_MS = 50000;
18+
} // namespace
1819

1920
class ProfileCredentialsProvider::ProfileCredentialsProviderImp : public CrtCredentialsProvider {
20-
public:
21-
ProfileCredentialsProviderImp()
22-
:CrtCredentialsProvider(
21+
public:
22+
ProfileCredentialsProviderImp()
23+
: CrtCredentialsProvider(
2324
[]() -> std::shared_ptr<ICredentialsProvider> {
24-
CredentialsProviderProfileConfig config;
25-
return CredentialsProvider::CreateCredentialsProviderProfile(config);
25+
CredentialsProviderProfileConfig config;
26+
return CredentialsProvider::CreateCredentialsProviderProfile(config);
2627
},
27-
std::chrono::milliseconds(5000),
28-
UserAgentFeature::CREDENTIALS_PROFILE,
29-
"ProfileCredentialsProvider"){}
28+
std::chrono::milliseconds(DEFAULT_REFRESH_RATE_MS), UserAgentFeature::CREDENTIALS_PROFILE, "ProfileCredentialsProvider") {}
3029

31-
ProfileCredentialsProviderImp(const char* profile)
32-
:CrtCredentialsProvider(
33-
[profile]()-> std::shared_ptr<ICredentialsProvider> {
34-
CredentialsProviderProfileConfig config;
35-
if (profile && profile[0] !='\0') {
36-
config.ProfileNameOverride = Aws::Crt::ByteCursorFromCString(profile);
37-
}
38-
return CredentialsProvider::CreateCredentialsProviderProfile(config);
30+
ProfileCredentialsProviderImp(const char* profile)
31+
: CrtCredentialsProvider(
32+
[profile]() -> std::shared_ptr<ICredentialsProvider> {
33+
CredentialsProviderProfileConfig config;
34+
config.ProfileNameOverride = Aws::Crt::ByteCursorFromCString(profile);
35+
return CredentialsProvider::CreateCredentialsProviderProfile(config);
3936
},
40-
std::chrono::milliseconds(5000),
41-
Aws::Client::UserAgentFeature::CREDENTIALS_PROFILE,
42-
"ProfileCredentialsProvider"){}
37+
std::chrono::milliseconds(DEFAULT_REFRESH_RATE_MS), Aws::Client::UserAgentFeature::CREDENTIALS_PROFILE,
38+
"ProfileCredentialsProvider") {}
4339

44-
static Aws::String GetCredentialsProfileFilename() {
45-
auto credentialsFileNameFromVar = Aws::Environment::GetEnv(PROFILE_AWS_CREDENTIALS_FILE);
40+
static Aws::String GetCredentialsProfileFilename() {
41+
auto credentialsFileNameFromVar = Aws::Environment::GetEnv(PROFILE_AWS_CREDENTIALS_FILE);
4642

47-
if (credentialsFileNameFromVar.empty()) {
48-
return GetHomeDirectory() + PROFILE_PROFILE_DIRECTORY + PATH_DELIM + PROFILE_DEFAULT_CREDENTIALS_FILE;
49-
}
50-
return credentialsFileNameFromVar;
43+
if (credentialsFileNameFromVar.empty()) {
44+
return GetHomeDirectory() + PROFILE_PROFILE_DIRECTORY + PATH_DELIM + PROFILE_DEFAULT_CREDENTIALS_FILE;
5145
}
46+
return credentialsFileNameFromVar;
47+
}
5248

53-
static Aws::String GetProfileDirectory() {
54-
Aws::String credentialsFileName = GetCredentialsProfileFilename();
55-
auto lastSeparator = credentialsFileName.find_last_of(PATH_DELIM);
56-
if (lastSeparator != std::string::npos) {
57-
return credentialsFileName.substr(0, lastSeparator);
58-
} else {
59-
return {};
60-
}
49+
static Aws::String GetProfileDirectory() {
50+
Aws::String credentialsFileName = GetCredentialsProfileFilename();
51+
auto lastSeparator = credentialsFileName.find_last_of(PATH_DELIM);
52+
if (lastSeparator != std::string::npos) {
53+
return credentialsFileName.substr(0, lastSeparator);
54+
} else {
55+
return {};
6156
}
57+
}
6258
};
6359

64-
ProfileCredentialsProvider::ProfileCredentialsProvider()
65-
: m_impl(std::make_shared<ProfileCredentialsProviderImp>()) {}
60+
ProfileCredentialsProvider::ProfileCredentialsProvider(long refreshRateMs) : m_impl(std::make_shared<ProfileCredentialsProviderImp>()) {
61+
(void)refreshRateMs;
62+
}
63+
64+
ProfileCredentialsProvider::ProfileCredentialsProvider(const char* profile, long refreshRateMs)
65+
: m_impl(std::make_shared<ProfileCredentialsProviderImp>(profile)) {
66+
(void)refreshRateMs;
67+
}
6668

67-
ProfileCredentialsProvider::ProfileCredentialsProvider(const char* profile)
68-
: m_impl(std::make_shared<ProfileCredentialsProviderImp>(profile)) {}
69+
void ProfileCredentialsProvider::Reload() {}
6970

7071
Aws::String ProfileCredentialsProvider::GetCredentialsProfileFilename() {
7172
return ProfileCredentialsProviderImp::GetCredentialsProfileFilename();

tests/aws-cpp-sdk-core-integration-tests/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ endif()
1616
enable_testing()
1717

1818
if(PLATFORM_ANDROID AND BUILD_SHARED_LIBS)
19-
add_library(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/STSWebIdentityProviderIntegrationTest.cpp)
19+
add_library(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/STSWebIdentityProviderIntegrationTest.cpp ${CMAKE_CURRENT_SOURCE_DIR} / DefaultCredentialsProviderChainIntegrationTest.cpp)
2020
else()
21-
add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/STSWebIdentityProviderIntegrationTest.cpp)
21+
add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/STSWebIdentityProviderIntegrationTest.cpp ${CMAKE_CURRENT_SOURCE_DIR} / DefaultCredentialsProviderChainIntegrationTest.cpp)
2222
endif()
2323

2424
set_compiler_flags(${PROJECT_NAME})
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
#include <aws/core/Aws.h>
7+
#include <aws/core/auth/AWSCredentialsProviderChain.h>
8+
#include <aws/core/platform/Environment.h>
9+
#include <aws/core/utils/FileSystemUtils.h>
10+
#include <aws/testing/AwsTestHelpers.h>
11+
#include <aws/testing/platform/PlatformTesting.h>
12+
#include <gtest/gtest.h>
13+
14+
using namespace Aws;
15+
using namespace Aws::Auth;
16+
using namespace Aws::Environment;
17+
using namespace Aws::Utils;
18+
19+
/**
20+
* Integration tests for DefaultAWSCredentialsProviderChain
21+
* These tests use real credentials from the environment (e.g., via ada)
22+
*/
23+
class DefaultCredentialsProviderChainIntegrationTest : public testing::Test
24+
{
25+
protected:
26+
DefaultCredentialsProviderChainIntegrationTest()
27+
{
28+
m_options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Debug;
29+
InitAPI(m_options);
30+
}
31+
32+
~DefaultCredentialsProviderChainIntegrationTest()
33+
{
34+
ShutdownAPI(m_options);
35+
}
36+
37+
SDKOptions m_options;
38+
};
39+
40+
TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainResolvesCredentialsFromEnvironment)
41+
{
42+
// This test assumes AWS credentials are set via environment (e.g., ada)
43+
DefaultAWSCredentialsProviderChain chain;
44+
auto creds = chain.GetAWSCredentials();
45+
46+
// Should get valid credentials from environment
47+
EXPECT_FALSE(creds.IsEmpty());
48+
EXPECT_FALSE(creds.GetAWSAccessKeyId().empty());
49+
EXPECT_FALSE(creds.GetAWSSecretKey().empty());
50+
}
51+
52+
TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainCachesCredentials)
53+
{
54+
DefaultAWSCredentialsProviderChain chain;
55+
56+
// First call
57+
auto creds1 = chain.GetAWSCredentials();
58+
EXPECT_FALSE(creds1.IsEmpty());
59+
60+
// Second call should return same cached credentials
61+
auto creds2 = chain.GetAWSCredentials();
62+
EXPECT_FALSE(creds2.IsEmpty());
63+
EXPECT_EQ(creds1.GetAWSAccessKeyId(), creds2.GetAWSAccessKeyId());
64+
EXPECT_EQ(creds1.GetAWSSecretKey(), creds2.GetAWSSecretKey());
65+
}
66+
67+
TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainWithCustomProfile)
68+
{
69+
// Create temporary credentials file with custom profile
70+
TempFile credsFile{std::ios_base::out | std::ios_base::trunc};
71+
credsFile << "[custom-test-profile]\n";
72+
credsFile << "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n";
73+
credsFile << "aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n";
74+
credsFile.close();
75+
76+
// Create temporary config file
77+
TempFile configFile{std::ios_base::out | std::ios_base::trunc};
78+
configFile << "[profile custom-test-profile]\n";
79+
configFile << "region = us-west-2\n";
80+
configFile.close();
81+
82+
const EnvironmentRAII environmentRAII{{
83+
{"AWS_SHARED_CREDENTIALS_FILE", credsFile.GetFileName()},
84+
{"AWS_CONFIG_FILE", configFile.GetFileName()},
85+
{"AWS_ACCESS_KEY_ID", ""},
86+
{"AWS_SECRET_ACCESS_KEY", ""},
87+
}};
88+
89+
Aws::Config::ReloadCachedConfigFile();
90+
91+
Client::ClientConfiguration::CredentialProviderConfiguration config;
92+
config.profile = "custom-test-profile";
93+
DefaultAWSCredentialsProviderChain chain(config);
94+
95+
auto creds = chain.GetAWSCredentials();
96+
97+
EXPECT_STREQ("AKIAIOSFODNN7EXAMPLE", creds.GetAWSAccessKeyId().c_str());
98+
EXPECT_STREQ("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", creds.GetAWSSecretKey().c_str());
99+
}
100+
101+
TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainWithProcessCredentials)
102+
{
103+
// Create temporary config file with credential_process
104+
TempFile configFile{std::ios_base::out | std::ios_base::trunc};
105+
configFile << "[default]\n";
106+
configFile << "credential_process = echo '{\"Version\": 1, \"AccessKeyId\": \"AKIAPROCESSEXAMPLE\", \"SecretAccessKey\": \"ProcessSecretKeyExample\"}'\n";
107+
configFile.close();
108+
109+
// Create empty credentials file to override ~/.aws/credentials
110+
TempFile credsFile{std::ios_base::out | std::ios_base::trunc};
111+
credsFile << "[default]\n";
112+
credsFile.close();
113+
114+
const EnvironmentRAII environmentRAII{{
115+
{"AWS_CONFIG_FILE", configFile.GetFileName()},
116+
{"AWS_SHARED_CREDENTIALS_FILE", credsFile.GetFileName()},
117+
{"AWS_ACCESS_KEY_ID", ""},
118+
{"AWS_SECRET_ACCESS_KEY", ""},
119+
{"AWS_SESSION_TOKEN", ""},
120+
{"AWS_EC2_METADATA_DISABLED", "true"},
121+
}};
122+
123+
Aws::Config::ReloadCachedConfigFile();
124+
125+
DefaultAWSCredentialsProviderChain chain;
126+
auto creds = chain.GetAWSCredentials();
127+
128+
EXPECT_STREQ("AKIAPROCESSEXAMPLE", creds.GetAWSAccessKeyId().c_str());
129+
EXPECT_STREQ("ProcessSecretKeyExample", creds.GetAWSSecretKey().c_str());
130+
}
131+
132+
TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainPrecedenceEnvironmentOverProfile)
133+
{
134+
// Create temporary credentials file
135+
TempFile credsFile{std::ios_base::out | std::ios_base::trunc};
136+
credsFile << "[default]\n";
137+
credsFile << "aws_access_key_id = AKIAPROFILEEXAMPLE\n";
138+
credsFile << "aws_secret_access_key = ProfileSecretKeyExample\n";
139+
credsFile.close();
140+
141+
const EnvironmentRAII environmentRAII{{
142+
{"AWS_SHARED_CREDENTIALS_FILE", credsFile.GetFileName()},
143+
{"AWS_ACCESS_KEY_ID", "AKIAENVEXAMPLE"},
144+
{"AWS_SECRET_ACCESS_KEY", "EnvSecretKeyExample"},
145+
}};
146+
147+
Aws::Config::ReloadCachedConfigFile();
148+
149+
DefaultAWSCredentialsProviderChain chain;
150+
auto creds = chain.GetAWSCredentials();
151+
152+
// Environment should take precedence
153+
EXPECT_STREQ("AKIAENVEXAMPLE", creds.GetAWSAccessKeyId().c_str());
154+
EXPECT_STREQ("EnvSecretKeyExample", creds.GetAWSSecretKey().c_str());
155+
}
156+
157+
TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainHandlesSessionToken)
158+
{
159+
// Create temporary credentials file with session token
160+
TempFile credsFile{std::ios_base::out | std::ios_base::trunc};
161+
credsFile << "[default]\n";
162+
credsFile << "aws_access_key_id = ASIATEMPORARYEXAMPLE\n";
163+
credsFile << "aws_secret_access_key = TempSecretKeyExample\n";
164+
credsFile << "aws_session_token = TempSessionTokenExample123\n";
165+
credsFile.close();
166+
167+
const EnvironmentRAII environmentRAII{{
168+
{"AWS_SHARED_CREDENTIALS_FILE", credsFile.GetFileName()},
169+
{"AWS_ACCESS_KEY_ID", ""},
170+
{"AWS_SECRET_ACCESS_KEY", ""},
171+
{"AWS_EC2_METADATA_DISABLED", "true"},
172+
}};
173+
174+
Aws::Config::ReloadCachedConfigFile();
175+
176+
DefaultAWSCredentialsProviderChain chain;
177+
auto creds = chain.GetAWSCredentials();
178+
179+
EXPECT_STREQ("ASIATEMPORARYEXAMPLE", creds.GetAWSAccessKeyId().c_str());
180+
EXPECT_STREQ("TempSecretKeyExample", creds.GetAWSSecretKey().c_str());
181+
EXPECT_STREQ("TempSessionTokenExample123", creds.GetSessionToken().c_str());
182+
}
183+
184+
TEST_F(DefaultCredentialsProviderChainIntegrationTest, ChainReturnsEmptyWhenNoCredentialsAvailable)
185+
{
186+
const EnvironmentRAII environmentRAII{{
187+
{"AWS_ACCESS_KEY_ID", ""},
188+
{"AWS_SECRET_ACCESS_KEY", ""},
189+
{"AWS_SHARED_CREDENTIALS_FILE", "/nonexistent/credentials"},
190+
{"AWS_CONFIG_FILE", "/nonexistent/config"},
191+
{"AWS_EC2_METADATA_DISABLED", "true"},
192+
}};
193+
194+
Aws::Config::ReloadCachedConfigFile();
195+
196+
DefaultAWSCredentialsProviderChain chain;
197+
auto creds = chain.GetAWSCredentials();
198+
199+
EXPECT_TRUE(creds.IsEmpty());
200+
}

0 commit comments

Comments
 (0)