/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/core/config/AWSProfileConfigLoader.h>
#include <aws/core/utils/memory/stl/AWSSet.h>
#include <aws/core/utils/memory/stl/AWSStreamFwd.h>
#include <aws/core/utils/StringUtils.h>
#include <aws/core/utils/logging/LogMacros.h>
#include <fstream>
namespace Aws
{
namespace Config
{
using namespace Aws::Utils;
using namespace Aws::Auth;
static const char REGION_KEY[] = "region";
static const char ACCESS_KEY_ID_KEY[] = "aws_access_key_id";
static const char SECRET_KEY_KEY[] = "aws_secret_access_key";
static const char SESSION_TOKEN_KEY[] = "aws_session_token";
static const char SSO_START_URL_KEY[] = "sso_start_url";
static const char SSO_REGION_KEY[] = "sso_region";
static const char SSO_ACCOUNT_ID_KEY[] = "sso_account_id";
static const char SSO_ROLE_NAME_KEY[] = "sso_role_name";
static const char SSO_SESSION_KEY[] = "sso_session";
static const char ROLE_ARN_KEY[] = "role_arn";
static const char EXTERNAL_ID_KEY[] = "external_id";
static const char CREDENTIAL_PROCESS_COMMAND[] = "credential_process";
static const char SOURCE_PROFILE_KEY[] = "source_profile";
static const char PROFILE_SECTION[] = "profile";
static const char DEFAULT[] = "default";
static const char SSO_SESSION_SECTION[] = "sso-session";
static const char DEFAULTS_MODE_KEY[] = "defaults_mode";
static const char EQ = '=';
static const char LEFT_BRACKET = '[';
static const char RIGHT_BRACKET = ']';
static const char PARSER_TAG[] = "Aws::Config::ConfigFileProfileFSM";
// generated by python from identifier regex pattern from the spec: R"([A-Za-z0-9_\-/.%@:\+]+)":
// #py: ''.join(chr(i) for i in range(128) if re.match("[A-Za-z0-9_\-\/.%@:\+]", chr(i)))
const char IDENTIFIER_ALLOWED_CHARACTERS[] = R"(%+-./0123456789:@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz)";
static const size_t IDENTIFIER_ALLOWED_CHARACTERS_SZ = sizeof(IDENTIFIER_ALLOWED_CHARACTERS) - 1;
const char WHITESPACE_CHARACTERS[] = "\t ";
static const size_t WHITESPACE_CHARACTERS_SZ = sizeof(WHITESPACE_CHARACTERS) - 1;
const char COMMENT_START[] = "#;";
static const size_t COMMENT_START_SZ = sizeof(COMMENT_START) - 1;
struct ProfilePropertyAccessFunctions
{
const char* PropertyKey;
std::function<void(Profile&, const Aws::String&)> Setter;
std::function<const Aws::String&(const Profile&)> Getter;
};
static const ProfilePropertyAccessFunctions PROFILE_PROPERTY_FUNCS[] =
{{REGION_KEY, &Profile::SetRegion, &Profile::GetRegion},
//ACCESS_KEY_ID_KEY, - AwsCredentials require special handling
//SECRET_KEY_KEY,
//SESSION_TOKEN_KEY,
{SSO_START_URL_KEY, &Profile::SetSsoStartUrl, &Profile::GetSsoStartUrl},
{SSO_REGION_KEY, &Profile::SetSsoRegion, &Profile::GetSsoRegion},
{SSO_ACCOUNT_ID_KEY, &Profile::SetSsoAccountId, &Profile::GetSsoAccountId},
{SSO_ROLE_NAME_KEY, &Profile::SetSsoRoleName, &Profile::GetSsoRoleName},
//SSO_SESSION_KEY - SsoSession requires special handling
{ROLE_ARN_KEY, &Profile::SetRoleArn, &Profile::GetRoleArn},
{EXTERNAL_ID_KEY, &Profile::SetExternalId, &Profile::GetExternalId},
{CREDENTIAL_PROCESS_COMMAND, &Profile::SetCredentialProcess, &Profile::GetCredentialProcess},
{SOURCE_PROFILE_KEY, &Profile::SetSourceProfile, &Profile::GetSourceProfile},
{DEFAULTS_MODE_KEY, &Profile::SetDefaultsMode, &Profile::GetDefaultsMode}};
template<typename EntryT, size_t N>
const EntryT* FindInStaticArray(const EntryT (&array)[N], const Aws::String& searchKey)
{
const EntryT* found = std::find_if(array, array + N,
[&searchKey](const EntryT& entry)
{
return searchKey == entry.PropertyKey;
});
if(!!found && found != array + N)
return found;
return nullptr;
}
static const char* PROFILE_KEY_SPECIAL_HANDLING[] =
{ACCESS_KEY_ID_KEY, SECRET_KEY_KEY, SESSION_TOKEN_KEY, SSO_SESSION_KEY};
static const size_t PROFILE_KEY_SPECIAL_HANDLING_SZ = sizeof(PROFILE_KEY_SPECIAL_HANDLING) / sizeof(PROFILE_KEY_SPECIAL_HANDLING[0]);
struct SsoSessionPropertyAccessFunctions
{
const char* PropertyKey;
std::function<void(Profile::SsoSession&, const Aws::String&)> Setter;
std::function<const Aws::String&(const Profile::SsoSession&)> Getter;
};
static const SsoSessionPropertyAccessFunctions SSO_SESSION_PROPERTY_FUNCS[] =
{{SSO_REGION_KEY, &Profile::SsoSession::SetSsoRegion, &Profile::SsoSession::GetSsoRegion},
{SSO_START_URL_KEY, &Profile::SsoSession::SetSsoStartUrl, &Profile::SsoSession::GetSsoStartUrl}};
class ConfigFileProfileFSM
{
public:
ConfigFileProfileFSM(bool useProfilePrefix)
: m_useProfilePrefix(useProfilePrefix)
{}
const Aws::Map<String, Profile>& GetProfiles() const { return m_foundProfiles; }
void ParseStream(Aws::IStream& stream)
{
static const size_t ASSUME_EMPTY_LEN = 3;
State currentState = START;
Aws::String currentSectionName;
Aws::Map<Aws::String, Aws::String> currentKeyValues;
Aws::String rawLine;
while(std::getline(stream, rawLine) && currentState != FAILURE)
{
Aws::String line = rawLine.substr(0, rawLine.find_first_of(COMMENT_START)); // ignore comments
if (line.empty() || line.length() < ASSUME_EMPTY_LEN || line.find_first_not_of(WHITESPACE_CHARACTERS) == Aws::String::npos)
{
continue;
}
auto openPos = line.find(LEFT_BRACKET);
auto closePos = line.find(RIGHT_BRACKET);
if(openPos != std::string::npos && closePos != std::string::npos)
{
FlushSection(currentState, currentSectionName, currentKeyValues);
currentKeyValues.clear();
ParseSectionDeclaration(line, currentSectionName, currentState);
continue;
}
if(PROFILE_FOUND == currentState || SSO_SESSION_FOUND == currentState)
{
auto equalsPos = line.find(EQ);
if (equalsPos != std::string::npos)
{
auto key = StringUtils::Trim(line.substr(0, equalsPos).c_str());
auto value = StringUtils::Trim(line.substr(equalsPos + 1).c_str());
currentKeyValues[key] = value;
continue;
}
}
if(UNKNOWN_SECTION_FOUND == currentState)
{
// skip any unknown sections
continue;
}
AWS_LOGSTREAM_ERROR(PARSER_TAG, "Unexpected line in the aws shared profile: " << rawLine);
currentState = FAILURE;
break;
}
FlushSection(currentState, currentSectionName, currentKeyValues);
// Put sso-sessions into profiles
for(auto& profile : m_foundProfiles)
{
const Aws::String& profileSsoSessionName = profile.second.GetValue(SSO_SESSION_KEY);
if(!profileSsoSessionName.empty())
{
auto ssoSessionIt = m_foundSsoSessions.find(profileSsoSessionName);
if(ssoSessionIt == m_foundSsoSessions.end())
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "AWS profile has reference to a missing sso_session: " << profileSsoSessionName);
currentState = FAILURE;
continue;
}
auto ssoSession = ssoSessionIt->second;
auto prof = profile.second;
// If sso session and profile have conflicting start url or region, fail to parse
// the session/sso specific profile properties
auto hasConflictingStartUrls = !ssoSession.GetSsoStartUrl().empty()
&& !prof.GetSsoStartUrl().empty()
&& ssoSession.GetSsoStartUrl() != prof.GetSsoStartUrl();
auto hasConflictingRegions = !ssoSession.GetSsoRegion().empty()
&& !prof.GetSsoRegion().empty()
&& ssoSession.GetSsoRegion() != prof.GetSsoRegion();
if (hasConflictingStartUrls || hasConflictingRegions) {
AWS_LOGSTREAM_ERROR(PARSER_TAG,
"SSO profile has a start url or region conflict with sso session");
prof.SetSsoStartUrl("");
prof.SetSsoRegion("");
prof.SetSsoAccountId("");
prof.SetSsoRoleName("");
continue;
}
profile.second.SetSsoSession(ssoSessionIt->second);
}
}
if(FAILURE == currentState)
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "AWS shared profile config parsing failed");
}
}
private:
// true means Shared Config parsing, false means Shared Credentials parsing
bool m_useProfilePrefix = false;
enum State
{
START = 0,
PROFILE_FOUND,
SSO_SESSION_FOUND,
UNKNOWN_SECTION_FOUND,
FAILURE
};
/**
* Helper function to parse a single word (aka section identifier) containing allowed characters from a line and a pos
* i.e. line="[ profile default ]";identifierBegin=10 will return "default"
* @param line, a section definition line being parsed
* @param identifierBegin, an Aws::String position to start parsing
* @param oErrorMsg, a reference to Aws::String to store error message in case of a parsing error.
* @return Aws::String, e.g. "default"
*/
Aws::String ParseIdentifier(const Aws::String& line, Aws::String::size_type identifierBegin, Aws::String& oErrorMsg)
{
// pos at the beginning of section Identifier (or sso_session section keyword)
Aws::String::size_type identifierLength = 0;
Aws::String::size_type pos = identifierBegin;
while(pos < line.length())
{
if(std::find(IDENTIFIER_ALLOWED_CHARACTERS,
IDENTIFIER_ALLOWED_CHARACTERS + IDENTIFIER_ALLOWED_CHARACTERS_SZ,
line[pos]) != IDENTIFIER_ALLOWED_CHARACTERS + IDENTIFIER_ALLOWED_CHARACTERS_SZ)
{
identifierLength++;
pos++;
}
else
{
break;
}
}
const Aws::String SECTION_END_CHARS_TO_SKIP = Aws::String(WHITESPACE_CHARACTERS) + RIGHT_BRACKET;
if(identifierLength == 0)
{
oErrorMsg = "identifier is missing";
return "";
}
if(pos >= line.size() || SECTION_END_CHARS_TO_SKIP.find(line[pos]) == Aws::String::npos) {
oErrorMsg = "a blank space character or closing bracket is expected after Identifier";
return "";
}
Aws::String sectionIdentifier = line.substr(identifierBegin, identifierLength);
return sectionIdentifier;
}
/**
* A helper function to parse config section declaration line
* @param line, an input line, e.g. "[profile default]"
* @param ioSectionName, a return argument representing parsed section Identifier, e.g. "default"
* @param ioState, a return argument representing parser state, e.g. PROFILE_FOUND
*/
void ParseSectionDeclaration(const Aws::String& line,
Aws::String& ioSectionName,
State& ioState)
{
do { // goto in a form of "do { break; } while(0);"
Aws::String::size_type pos = 0;
pos = line.find_first_not_of(WHITESPACE_CHARACTERS, pos);
if(pos != Aws::String::npos && LEFT_BRACKET != line[pos])
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "First non-blank space character of a section definition must be [, line:" << line);
break;
}
pos++;
pos = line.find_first_not_of(WHITESPACE_CHARACTERS, pos);
if(pos == Aws::String::npos || pos >= line.size())
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "Unknown section found in the aws config file: " << line);
break;
}
bool defaultProfileOrSsoSectionRequired = false;
if (m_useProfilePrefix)
{
// in configuration files, the profile name must start with profile. (eg. [profile profile-name]),
// except where the profile name is default. When the profile name is default it may start with profile
static const size_t PROFILE_KEYWORD_LENGTH = 7;
if(line.rfind(PROFILE_SECTION, pos + PROFILE_KEYWORD_LENGTH) != Aws::String::npos)
{
// skipping required (optional for default) profile keyword
pos += PROFILE_KEYWORD_LENGTH;
if(pos >= line.size() ||
std::find(WHITESPACE_CHARACTERS,
WHITESPACE_CHARACTERS + WHITESPACE_CHARACTERS_SZ,
line[pos]) == WHITESPACE_CHARACTERS + WHITESPACE_CHARACTERS_SZ)
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "Expected a blank space after \"profile\" keyword: " << line);
break;
}
pos = line.find_first_not_of(WHITESPACE_CHARACTERS, pos);
}
else
{
defaultProfileOrSsoSectionRequired = true;
}
}
Aws::String errorMsg;
Aws::String sectionIdentifier = ParseIdentifier(line, pos, errorMsg);
if (!errorMsg.empty())
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "Failed to parse section identifier: " << errorMsg << " " << line);
break;
}
pos += sectionIdentifier.length();
if(defaultProfileOrSsoSectionRequired)
{
if (sectionIdentifier != DEFAULT && sectionIdentifier != SSO_SESSION_SECTION)
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "In configuration files, the profile name must start with "
"profile keyword (except default profile): " << line);
break;
}
if (sectionIdentifier != SSO_SESSION_SECTION)
{
// profile found, still pending check for closing bracket
ioState = PROFILE_FOUND;
ioSectionName = sectionIdentifier;
}
}
if(!m_useProfilePrefix || sectionIdentifier != SSO_SESSION_SECTION)
{
// profile found, still pending check for closing bracket
ioState = PROFILE_FOUND;
ioSectionName = sectionIdentifier;
}
if(m_useProfilePrefix && sectionIdentifier == SSO_SESSION_SECTION)
{
// "[sso_session..." found, continue parsing for sso_session identifier
pos = line.find_first_not_of(WHITESPACE_CHARACTERS, pos);
if(pos == Aws::String::npos)
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "Expected a blank space after \"sso_session\" keyword: " << line);
break;
}
sectionIdentifier = ParseIdentifier(line, pos, errorMsg);
if (!errorMsg.empty())
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "Failed to parse section identifier: " << errorMsg << " " << line);
break;
}
pos += sectionIdentifier.length();
// sso_session found, still pending check for closing bracket
ioState = SSO_SESSION_FOUND;
ioSectionName = sectionIdentifier;
}
pos = line.find_first_not_of(WHITESPACE_CHARACTERS, pos);
if(pos == Aws::String::npos)
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "Expected a non-blank space after section identifier (i.e. missing \"]\"): " << line);
break;
}
if(line[pos] != RIGHT_BRACKET)
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "Missing closing bracket after Section Identifier "
"(i.e. missing \"]\" or extra non-blank characters before \"]\"): " << line);
break;
}
pos++;
pos = line.find_first_not_of(WHITESPACE_CHARACTERS, pos);
if(pos != Aws::String::npos &&
std::find(COMMENT_START, COMMENT_START + COMMENT_START_SZ, line[pos]) == COMMENT_START + COMMENT_START_SZ)
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "Found unexpected characters after closing bracket of Section Identifier " << line);
break;
}
// the rest is a comment, and we don't care about it.
if ((ioState != SSO_SESSION_FOUND && ioState != PROFILE_FOUND) || ioSectionName.empty())
{
AWS_LOGSTREAM_FATAL(PARSER_TAG, "Unexpected parser state after attempting to parse section " << line);
break;
}
return;
} while(0); // end of goto in a form of "do { break; } while(0);"
ioSectionName.erase();
ioState = UNKNOWN_SECTION_FOUND;
return;
}
/**
* A helper function to store currently being parsed section along with its properties
* (i.e. [profile default] and its key1=val1 under).
* @param currentState, a current parser State, e.g. PROFILE_FOUND
* @param currentSectionName, a current section identifier, e.g. "default"
* @param currentKeyValues, a map of parsed key-value properties of a section definition being recorded
*/
void FlushSection(const State currentState, const Aws::String& currentSectionName, Aws::Map<Aws::String, Aws::String>& currentKeyValues)
{
if(START == currentState || currentSectionName.empty())
{
return; //nothing to flush
}
if(PROFILE_FOUND == currentState)
{
Profile& profile = m_foundProfiles[currentSectionName];
for(const auto& keyVal : currentKeyValues)
{
auto setterFuncPtr = FindInStaticArray(PROFILE_PROPERTY_FUNCS, keyVal.first);
if(setterFuncPtr)
{
AWS_LOGSTREAM_DEBUG(PARSER_TAG, "Found " << setterFuncPtr->PropertyKey << " " << keyVal.second);
setterFuncPtr->Setter(profile, keyVal.second);
}
else
{
auto specialPropertyKey = std::find_if(PROFILE_KEY_SPECIAL_HANDLING, PROFILE_KEY_SPECIAL_HANDLING + PROFILE_KEY_SPECIAL_HANDLING_SZ,
[&keyVal](const char* entry)
{
return !!entry && keyVal.first == entry;
});
if (specialPropertyKey && specialPropertyKey != PROFILE_KEY_SPECIAL_HANDLING + PROFILE_KEY_SPECIAL_HANDLING_SZ)
{
AWS_LOGSTREAM_INFO(PARSER_TAG, "Unknown property: " << keyVal.first << " in the profile: " << currentSectionName);
}
}
}
auto accessKeyIdIter = currentKeyValues.find(ACCESS_KEY_ID_KEY);
Aws::String accessKey, secretKey, sessionToken;
if (accessKeyIdIter != currentKeyValues.end())
{
accessKey = accessKeyIdIter->second;
AWS_LOGSTREAM_DEBUG(PARSER_TAG, "found access key " << accessKey);
auto secretAccessKeyIter = currentKeyValues.find(SECRET_KEY_KEY);
auto sessionTokenIter = currentKeyValues.find(SESSION_TOKEN_KEY);
if (secretAccessKeyIter != currentKeyValues.end())
{
secretKey = secretAccessKeyIter->second;
}
else
{
AWS_LOGSTREAM_ERROR(PARSER_TAG, "No secret access key found even though an access key was specified. This will cause all signed AWS calls to fail.");
}
if (sessionTokenIter != currentKeyValues.end())
{
sessionToken = sessionTokenIter->second;
}
profile.SetCredentials(Aws::Auth::AWSCredentials(accessKey, secretKey, sessionToken));
}
if (!profile.GetSsoStartUrl().empty() || !profile.GetSsoRegion().empty()
|| !profile.GetSsoAccountId().empty() || !profile.GetSsoRoleName().empty())
{
// If there is no sso session, all fields are required. If an SSO session is present,
// then only account id and sso role name are required.
auto hasSession = currentKeyValues.find(SSO_SESSION_KEY) != currentKeyValues.end();
auto hasInvalidProfileWithoutSession = !hasSession &&
(profile.GetSsoStartUrl().empty()
|| profile.GetSsoRegion().empty()
|| profile.GetSsoAccountId().empty()
|| profile.GetSsoRoleName().empty());
auto hasInvalidProfileWithSession = hasSession &&
(profile.GetSsoAccountId().empty()
|| profile.GetSsoRoleName().empty());
if (hasInvalidProfileWithoutSession || hasInvalidProfileWithSession) {
profile.SetSsoStartUrl("");
profile.SetSsoRegion("");
profile.SetSsoAccountId("");
profile.SetSsoRoleName("");
AWS_LOGSTREAM_ERROR(PARSER_TAG, "invalid SSO configuration for aws profile " << currentSectionName);
}
}
profile.SetName(currentSectionName);
profile.SetAllKeyValPairs(std::move(currentKeyValues));
}
else if (SSO_SESSION_FOUND == currentState) {
Profile::SsoSession& ssoSession = m_foundSsoSessions[currentSectionName];
for(const auto& keyVal : currentKeyValues)
{
auto setterFuncPtr = FindInStaticArray(SSO_SESSION_PROPERTY_FUNCS, keyVal.first);
if(setterFuncPtr)
{
AWS_LOGSTREAM_DEBUG(PARSER_TAG, "Found sso-session property " << setterFuncPtr->PropertyKey << " " << keyVal.second);
setterFuncPtr->Setter(ssoSession, keyVal.second);
}
else
{
AWS_LOGSTREAM_INFO(PARSER_TAG, "Unknown property: " << keyVal.first << " in the sso-session: " << currentSectionName);
}
}
ssoSession.SetName(currentSectionName);
ssoSession.SetAllKeyValPairs(std::move(currentKeyValues));
}
else
{
AWS_LOGSTREAM_FATAL(PARSER_TAG, "Unknown parser error: unexpected state " << currentState);
}
}
Aws::Map<String, Profile> m_foundProfiles;
Aws::Map<String, Profile::SsoSession> m_foundSsoSessions;
};
static const char* const CONFIG_FILE_LOADER = "Aws::Config::AWSConfigFileProfileConfigLoader";
AWSConfigFileProfileConfigLoader::AWSConfigFileProfileConfigLoader(const Aws::String& fileName, bool useProfilePrefix) :
m_fileName(fileName), m_useProfilePrefix(useProfilePrefix)
{
AWS_LOGSTREAM_INFO(CONFIG_FILE_LOADER, "Initializing config loader against fileName "
<< fileName << " and using profilePrefix = " << useProfilePrefix);
}
bool AWSConfigFileProfileConfigLoader::LoadInternal()
{
m_profiles.clear();
Aws::IFStream inputFile(m_fileName.c_str());
if(inputFile)
{
ConfigFileProfileFSM parser(m_useProfilePrefix);
parser.ParseStream(inputFile);
m_profiles = parser.GetProfiles();
return m_profiles.size() > 0;
}
AWS_LOGSTREAM_INFO(CONFIG_FILE_LOADER, "Unable to open config file " << m_fileName << " for reading.");
return false;
}
bool AWSConfigFileProfileConfigLoader::PersistInternal(const Aws::Map<Aws::String, Profile>& profiles)
{
Aws::OFStream outputFile(m_fileName.c_str(), std::ios_base::out | std::ios_base::trunc);
if(outputFile)
{
Aws::UnorderedMap<Aws::String, std::reference_wrapper<const Profile::SsoSession>> ssoSessionsToDump;
for(const auto& profile : profiles)
{
Aws::String prefix = m_useProfilePrefix ? PROFILE_SECTION : "";
AWS_LOGSTREAM_DEBUG(CONFIG_FILE_LOADER, "Writing profile " << profile.first << " to disk.");
outputFile << LEFT_BRACKET << prefix << " " << profile.second.GetName() << RIGHT_BRACKET << std::endl;
const Aws::Auth::AWSCredentials& credentials = profile.second.GetCredentials();
if (!credentials.GetAWSAccessKeyId().empty()) {
outputFile << ACCESS_KEY_ID_KEY << EQ << credentials.GetAWSAccessKeyId() << std::endl;
}
if (!credentials.GetAWSSecretKey().empty()) {
outputFile << SECRET_KEY_KEY << EQ << credentials.GetAWSSecretKey() << std::endl;
}
if(!credentials.GetSessionToken().empty()) {
outputFile << SESSION_TOKEN_KEY << EQ << credentials.GetSessionToken() << std::endl;
}
// credentials.GetExpiration().Millis() <- is not present in a config.
for(const auto& profilePropertyPair : PROFILE_PROPERTY_FUNCS)
{
const auto& profilePropertyValue = profilePropertyPair.Getter(profile.second);
if(!profilePropertyValue.empty())
{
outputFile << profilePropertyPair.PropertyKey << EQ << profilePropertyValue << std::endl;
}
}
if(profile.second.IsSsoSessionSet())
{
const auto& ssoSession = profile.second.GetSsoSession();
const auto alreadyScheduledForDumpIt = ssoSessionsToDump.find(ssoSession.GetName());
if (alreadyScheduledForDumpIt != ssoSessionsToDump.end() &&
alreadyScheduledForDumpIt->second.get() != ssoSession)
{
AWS_LOGSTREAM_WARN(CONFIG_FILE_LOADER, "2 or more profiles reference 'sso-session' section "
"with the same name but different properties: " << ssoSession.GetName());
}
else
{
ssoSessionsToDump.insert({ssoSession.GetName(), std::cref(ssoSession)});
}
outputFile << SSO_SESSION_KEY << EQ << ssoSession.GetName() << std::endl;
}
outputFile << std::endl;
}
for(const auto& ssoSessionPair : ssoSessionsToDump)
{
AWS_LOGSTREAM_DEBUG(CONFIG_FILE_LOADER, "Writing sso-session " << ssoSessionPair.first << " to disk.");
const Profile::SsoSession& ssoSession = ssoSessionPair.second.get();
outputFile << LEFT_BRACKET << SSO_SESSION_SECTION << " " << ssoSession.GetName() << RIGHT_BRACKET << std::endl;
for(const auto& ssoSessionPropertyPair : SSO_SESSION_PROPERTY_FUNCS)
{
const auto& profilePropertyValue = ssoSessionPropertyPair.Getter(ssoSession);
if(!profilePropertyValue.empty())
{
outputFile << ssoSessionPropertyPair.PropertyKey << EQ << profilePropertyValue << std::endl;
}
}
outputFile << std::endl;
}
AWS_LOGSTREAM_INFO(CONFIG_FILE_LOADER, "Profiles written to config file " << m_fileName);
return true;
}
AWS_LOGSTREAM_WARN(CONFIG_FILE_LOADER, "Unable to open config file " << m_fileName << " for writing.");
return false;
}
} // Config namespace
} // Aws namespace