Skip to content
Merged
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
18 changes: 15 additions & 3 deletions src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
#pragma once

#include <aws/core/Core_EXPORTS.h>
#include <aws/core/http/Scheme.h>
#include <aws/core/http/Version.h>
#include <aws/core/Region.h>
#include <aws/core/utils/memory/stl/AWSString.h>
#include <aws/core/http/HttpTypes.h>
#include <aws/core/http/Scheme.h>
#include <aws/core/http/Version.h>
#include <aws/core/utils/Array.h>
#include <aws/core/utils/StringUtils.h>
#include <aws/core/utils/memory/stl/AWSString.h>
#include <aws/crt/Optional.h>
#include <smithy/tracing/TelemetryProvider.h>

#include <memory>

namespace Aws
Expand Down Expand Up @@ -447,6 +449,16 @@ namespace Aws
static Aws::String LoadConfigFromEnvOrProfile(const Aws::String& envKey, const Aws::String& profile,
const Aws::String& profileProperty, const Aws::Vector<Aws::String>& allowedValues,
const Aws::String& defaultValue);
/**
* A helper function to read config value from env variable or aws profile config. Addresses a problem in
* LoadConfigFromEnvOrProfile where env variables values are always mapped to their lower case equivalent.
* This fails for cases where ENV vars need to be case sensitive in instances like AWS_ROLE_ARN can have
* camel case values.
*/
static Aws::String LoadConfigFromEnvOrProfileCaseSensitive(
const Aws::String& envKey, const Aws::String& profile, const Aws::String& profileProperty,
const Aws::Vector<Aws::String>& allowedValues, const Aws::String& defaultValue,
const std::function<Aws::String(const char*)>& envValueMapping = Utils::StringUtils::ToLower);

/**
* A wrapper for interfacing with telemetry functionality. Defaults to Noop provider.
Expand Down
99 changes: 59 additions & 40 deletions src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ static const char* AWS_METADATA_SERVICE_TIMEOUT_ENV_VAR = "AWS_METADATA_SERVICE_
static const char* AWS_METADATA_SERVICE_TIMEOUT_CONFIG_VAR = "metadata_service_timeout";
static const char* AWS_METADATA_SERVICE_NUM_ATTEMPTS_ENV_VAR = "AWS_METADATA_SERVICE_NUM_ATTEMPTS";
static const char* AWS_METADATA_SERVICE_NUM_ATTEMPTS_CONFIG_VAR = "metadata_service_num_attempts";
static const char* AWS_IAM_ROLE_ARN_ENV_VAR = "AWS_IAM_ROLE_ARN";
static const char* AWS_IAM_ROLE_ARN_ENV_VAR = "AWS_ROLE_ARN";
static const char* AWS_IAM_ROLE_ARN_ENV_VAR_COMPAT = "AWS_IAM_ROLE_ARN";
static const char* AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION = "role_arn";
static const char* AWS_IAM_ROLE_SESSION_NAME_ENV_VAR = "AWS_IAM_ROLE_SESSION_NAME";
static const char* AWS_IAM_ROLE_SESSION_NAME_ENV_VAR = "AWS_ROLE_SESSION_NAME";
static const char* AWS_IAM_ROLE_SESSION_NAME_ENV_VAR_COMPAT = "AWS_IAM_ROLE_SESSION_NAME";
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";
Expand Down Expand Up @@ -327,23 +329,34 @@ void setConfigFromEnvOrProfile(ClientConfiguration &config)
// Uses default retry mode with the specified max attempts from metadata_service_num_attempts
config.credentialProviderConfig.imdsConfig.imdsRetryStrategy = InitRetryStrategy(attempts, "");

config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_IAM_ROLE_ARN_ENV_VAR,
config.profileName,
AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION,
{}, /* allowed values */
"" /* default value */);

config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_IAM_ROLE_SESSION_NAME_ENV_VAR,
config.profileName,
AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION,
{}, /* allowed values */
"" /* default value */);

config.credentialProviderConfig.stsCredentialsProviderConfig.tokenFilePath = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_WEB_IDENTITY_TOKEN_FILE_ENV_VAR,
config.profileName,
AWS_WEB_IDENTITY_TOKEN_FILE_CONFIG_FILE_OPTION,
{}, /* allowed values */
"" /* default value */);
config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
AWS_IAM_ROLE_ARN_ENV_VAR_COMPAT, config.profileName, AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION, {}, /* allowed values */
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });

// there was a typo in the original environment variable, this exists for backwards compatibility
if (config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn.empty()) {
config.credentialProviderConfig.stsCredentialsProviderConfig.roleArn = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
AWS_IAM_ROLE_ARN_ENV_VAR, config.profileName, AWS_IAM_ROLE_ARN_CONFIG_FILE_OPTION, {}, /* allowed values */
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });
}

config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName = ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
AWS_IAM_ROLE_SESSION_NAME_ENV_VAR_COMPAT, config.profileName, AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION, {}, /* allowed values */
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });

// there was a typo in the original environment variable, this exists for backwards compatibility
if (config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName.empty()) {
config.credentialProviderConfig.stsCredentialsProviderConfig.sessionName =
ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
AWS_IAM_ROLE_SESSION_NAME_ENV_VAR, config.profileName, AWS_IAM_ROLE_SESSION_NAME_CONFIG_FILE_OPTION, {}, /* allowed values */
"" /* default value */, [](const Aws::String& envValue) -> Aws::String { return envValue; });
}

config.credentialProviderConfig.stsCredentialsProviderConfig.tokenFilePath =
ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(
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; });
}

ClientConfiguration::ClientConfiguration()
Expand Down Expand Up @@ -558,29 +571,35 @@ Aws::String ClientConfiguration::LoadConfigFromEnvOrProfile(const Aws::String& e
const Aws::Vector<Aws::String>& allowedValues,
const Aws::String& defaultValue)
{
Aws::String option = Aws::Environment::GetEnv(envKey.c_str());
if (option.empty()) {
option = Aws::Config::GetCachedConfigValue(profile, profileProperty);
}
option = Aws::Utils::StringUtils::ToLower(option.c_str());
if (option.empty()) {
return defaultValue;
}

if (!allowedValues.empty() && std::find(allowedValues.cbegin(), allowedValues.cend(), option) == allowedValues.cend()) {
Aws::OStringStream expectedStr;
expectedStr << "[";
for(const auto& allowed : allowedValues) {
expectedStr << allowed << ";";
}
expectedStr << "]";
return LoadConfigFromEnvOrProfileCaseSensitive(envKey, profile, profileProperty, allowedValues, defaultValue);
}
Aws::String ClientConfiguration::LoadConfigFromEnvOrProfileCaseSensitive(const Aws::String& envKey, const Aws::String& profile,
const Aws::String& profileProperty,
const Aws::Vector<Aws::String>& allowedValues,
const Aws::String& defaultValue,
const std::function<Aws::String(const char*)>& envValueMapping) {
Aws::String option = Aws::Environment::GetEnv(envKey.c_str());
if (option.empty()) {
option = Aws::Config::GetCachedConfigValue(profile, profileProperty);
}
option = envValueMapping(option.c_str());
if (option.empty()) {
return defaultValue;
}

AWS_LOGSTREAM_WARN(CLIENT_CONFIG_TAG, "Unrecognised value for " << envKey << ": " << option <<
". Using default instead: " << defaultValue <<
". Expected empty or one of: " << expectedStr.str());
option = defaultValue;
if (!allowedValues.empty() && std::find(allowedValues.cbegin(), allowedValues.cend(), option) == allowedValues.cend()) {
Aws::OStringStream expectedStr;
expectedStr << "[";
for (const auto& allowed : allowedValues) {
expectedStr << allowed << ";";
}
return option;
expectedStr << "]";

AWS_LOGSTREAM_WARN(CLIENT_CONFIG_TAG, "Unrecognised value for " << envKey << ": " << option << ". Using default instead: "
<< defaultValue << ". Expected empty or one of: " << expectedStr.str());
option = defaultValue;
}
return option;
}

} // namespace Client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
#include <aws/cognito-identity/model/SetIdentityPoolRolesRequest.h>
#include <aws/core/Aws.h>
#include <aws/core/auth/STSCredentialsProvider.h>
#include <aws/core/utils/FileSystemUtils.h>
#include <aws/core/client/ClientConfiguration.h>
#include <aws/core/platform/Environment.h>
#include <aws/core/utils/FileSystemUtils.h>
#include <aws/iam/IAMClient.h>
#include <aws/iam/model/CreateRoleRequest.h>
#include <aws/iam/model/DeleteRolePolicyRequest.h>
Expand All @@ -16,10 +17,12 @@
#include <aws/sts/STSClient.h>
#include <aws/sts/model/AssumeRoleRequest.h>
#include <aws/testing/AwsTestHelpers.h>
#include <aws/testing/platform/PlatformTesting.h>
#include <gtest/gtest.h>

using namespace Aws;
using namespace Aws::Client;
using namespace Aws::Environment;
using namespace Aws::Auth;
using namespace Aws::Utils;
using namespace Aws::IAM;
Expand Down Expand Up @@ -170,3 +173,43 @@ TEST_F(STSWebIdentityProviderIntegrationTest, ShouldWork) {
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
EXPECT_FALSE(credentials.IsEmpty());
}

TEST_F(STSWebIdentityProviderIntegrationTest, ShouldWorkWithEnvVar) {
CognitoIdentitySetup testResourcesRAII{UUID::RandomUUID()};
const EnvironmentRAII environmentRAII{
{{"AWS_ROLE_ARN", testResourcesRAII.GetRoleArn()}, {"AWS_WEB_IDENTITY_TOKEN_FILE", testResourcesRAII.GetTokenFileName()}}};
const ClientConfiguration config{};
STSAssumeRoleWebIdentityCredentialsProvider provider{config.credentialProviderConfig};
AWSCredentials credentials{};
size_t attempts = 0;
bool shouldSleep = false;
do {
if (shouldSleep) {
std::this_thread::sleep_for(IAM_CONSISTENCY_SLEEP);
}
credentials = provider.GetAWSCredentials();
shouldSleep = true;
attempts++;
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
EXPECT_FALSE(credentials.IsEmpty());
}

TEST_F(STSWebIdentityProviderIntegrationTest, ShouldWorkWithEnvVarBackwardsCompat) {
CognitoIdentitySetup testResourcesRAII{UUID::RandomUUID()};
const EnvironmentRAII environmentRAII{
{{"AWS_IAM_ROLE_ARN", testResourcesRAII.GetRoleArn()}, {"AWS_WEB_IDENTITY_TOKEN_FILE", testResourcesRAII.GetTokenFileName()}}};
const ClientConfiguration config{};
STSAssumeRoleWebIdentityCredentialsProvider provider{config.credentialProviderConfig};
AWSCredentials credentials{};
size_t attempts = 0;
bool shouldSleep = false;
do {
if (shouldSleep) {
std::this_thread::sleep_for(IAM_CONSISTENCY_SLEEP);
}
credentials = provider.GetAWSCredentials();
shouldSleep = true;
attempts++;
} while (credentials.IsEmpty() && attempts < MAX_IAM_CONSISTENCY_RETRIES);
EXPECT_FALSE(credentials.IsEmpty());
}