Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

#include <aws/core/Core_EXPORTS.h>
#include <aws/core/utils/memory/stl/AWSString.h>
#include <aws/core/auth/AWSCredentials.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 ProfileCredentialsProvider : public AWSCredentialsProvider {
public:
/**
* Initializes with refreshRateMs as the frequency at which the file is reparsed in milliseconds. Defaults to 5 minutes.
*/
ProfileCredentialsProvider(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.
*/
ProfileCredentialsProvider(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;

private:
class ProfileCredentialsProviderImp;
std::shared_ptr<ProfileCredentialsProviderImp> m_impl;
};
} // namespace Auth
} // namespace Aws
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <aws/core/utils/StringUtils.h>
#include <aws/core/utils/logging/LogMacros.h>
#include <aws/core/utils/memory/AWSMemory.h>
#include <aws/core/auth/ProfileCredentialsProvider.h>

using namespace Aws::Auth;
using namespace Aws::Utils::Threading;
Expand Down Expand Up @@ -45,7 +46,7 @@ AWSCredentials AWSCredentialsProviderChain::GetAWSCredentials()
DefaultAWSCredentialsProviderChain::DefaultAWSCredentialsProviderChain() : AWSCredentialsProviderChain()
{
AddProvider(Aws::MakeShared<EnvironmentAWSCredentialsProvider>(DefaultCredentialsProviderChainTag));
AddProvider(Aws::MakeShared<ProfileConfigFileAWSCredentialsProvider>(DefaultCredentialsProviderChainTag));
AddProvider(Aws::MakeShared<ProfileCredentialsProvider>(DefaultCredentialsProviderChainTag));
AddProvider(Aws::MakeShared<ProcessCredentialsProvider>(DefaultCredentialsProviderChainTag));
AddProvider(Aws::MakeShared<STSAssumeRoleWebIdentityCredentialsProvider>(DefaultCredentialsProviderChainTag));
AddProvider(Aws::MakeShared<SSOCredentialsProvider>(DefaultCredentialsProviderChainTag));
Expand Down Expand Up @@ -90,7 +91,7 @@ DefaultAWSCredentialsProviderChain::DefaultAWSCredentialsProviderChain() : AWSCr
DefaultAWSCredentialsProviderChain::DefaultAWSCredentialsProviderChain(const Aws::Client::ClientConfiguration::CredentialProviderConfiguration& config) : AWSCredentialsProviderChain()
{
AddProvider(Aws::MakeShared<EnvironmentAWSCredentialsProvider>(DefaultCredentialsProviderChainTag));
AddProvider(Aws::MakeShared<ProfileConfigFileAWSCredentialsProvider>(DefaultCredentialsProviderChainTag,config.profile.c_str()));
AddProvider(Aws::MakeShared<ProfileCredentialsProvider>(DefaultCredentialsProviderChainTag,config.profile.c_str()));
AddProvider(Aws::MakeShared<ProcessCredentialsProvider>(DefaultCredentialsProviderChainTag,config.profile));
AddProvider(Aws::MakeShared<STSAssumeRoleWebIdentityCredentialsProvider>(DefaultCredentialsProviderChainTag, config));
AddProvider(Aws::MakeShared<SSOCredentialsProvider>(DefaultCredentialsProviderChainTag,config.profile));
Expand Down
111 changes: 111 additions & 0 deletions src/aws-cpp-sdk-core/source/auth/ProfileCredentialsProvider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include <aws/core/auth/AWSCredentialsProvider.h>

#include <aws/core/config/AWSProfileConfigLoader.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 <cstdlib>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need cstdlib/string.h/climits here? they seem sort of suspect imports

#include <string.h>
#include <climits>
#include <aws/core/auth/ProfileCredentialsProvider.h>

using namespace Aws::Auth;

class ProfileCredentialsProvider::ProfileCredentialsProviderImp : public AWSCredentialsProvider {
public:
ProfileCredentialsProviderImp(long refreshRateMs)
: m_profileToUse(Aws::Auth::GetConfigProfileName()),
m_credentialsFileLoader(GetCredentialsProfileFilename()),
m_loadFrequencyMs(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_profileToUse);
}

ProfileCredentialsProviderImp(const char* profile, long refreshRateMs)
: m_profileToUse(profile), m_credentialsFileLoader(GetCredentialsProfileFilename()), m_loadFrequencyMs(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_profileToUse);
}

static Aws::String GetCredentialsProfileFilename() {
auto credentialsFileNameFromVar = Aws::Environment::GetEnv(AWS_CREDENTIALS_FILE);

if (credentialsFileNameFromVar.empty()) {
return Aws::FileSystem::GetHomeDirectory() + PROFILE_DIRECTORY + PATH_DELIM + DEFAULT_CREDENTIALS_FILE;
}
return credentialsFileNameFromVar;
}

static Aws::String 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 {};
}
}

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(const char* profile, long refreshRateMs)
: m_impl(std::make_shared<ProfileCredentialsProviderImp>(profile, refreshRateMs)) {}

Aws::String ProfileCredentialsProvider::GetCredentialsProfileFilename() {
return ProfileCredentialsProviderImp::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,125 @@
#include <aws/core/auth/ProfileCredentialsProvider.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 ProfileCredentialsProviderTests : 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(ProfileCredentialsProviderTests, 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();

ProfileCredentialsProvider provider;
auto credentials = provider.GetAWSCredentials();

EXPECT_STREQ("AKIATEST123", credentials.GetAWSAccessKeyId().c_str());
EXPECT_STREQ("SecretKey456", credentials.GetAWSSecretKey().c_str());
}

TEST_F(ProfileCredentialsProviderTests, 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();

ProfileCredentialsProvider provider("test-profile");
auto credentials = provider.GetAWSCredentials();

EXPECT_STREQ("TestKey", credentials.GetAWSAccessKeyId().c_str());
EXPECT_STREQ("TestSecret", credentials.GetAWSSecretKey().c_str());
}

TEST_F(ProfileCredentialsProviderTests, 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();

ProfileCredentialsProvider 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(ProfileCredentialsProviderTests, 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();

ProfileCredentialsProvider provider("nonexistent");
auto credentials = provider.GetAWSCredentials();

EXPECT_TRUE(credentials.IsEmpty());
}

TEST_F(ProfileCredentialsProviderTests, 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();

ProfileCredentialsProvider provider;
auto credentials = provider.GetAWSCredentials();

EXPECT_STREQ("ProcessKey", credentials.GetAWSAccessKeyId().c_str());
EXPECT_STREQ("ProcessSecret", credentials.GetAWSSecretKey().c_str());
}
Loading