in src/odbc/rsodbc/iam/RsIamClient.cpp [298:484]
Model::GetClusterCredentialsOutcome RsIamClient::SendClusterCredentialsRequest(
const std::shared_ptr<AWSCredentialsProvider>& in_credentialsProvider)
{
RS_LOG_DEBUG("IAMCLNT", "RsIamClient::SendClusterCredentialRequest");
ClientConfiguration config;
/* infer awsRegion and ClusterId from host name if not provided in the connection string
e.g., redshiftj-iam-test.c2mf5zd3u3sv.us-west-2.redshift.amazonaws.com,
should be tokenized to at least 6 elements
*/
std::vector<rs_string> hostnameTokens = IAMUtils::TokenizeSetting(m_settings.m_host, ".");
rs_string inferredClusterId, inferredAwsRegion;
if (hostnameTokens.size() >= 6)
{
/* e.g., redshiftj-iam-test.c2mf5zd3u3sv.us-west-2.redshift.amazonaws.com
e.g., redshiftj-iam-test.c2mf5zd3u3sv.us-west-2.redshift.amazonaws.com.cn
*/
inferredClusterId = hostnameTokens[0];
inferredAwsRegion = hostnameTokens[2];
}
if(m_settings.m_isCname && inferredAwsRegion.empty() ){
config.region = IAMUtils().GetAwsRegionFromCname(m_settings.m_host);
}
else
{
config.region = m_settings.m_awsRegion.empty() ? inferredAwsRegion : m_settings.m_awsRegion;
config.endpointOverride = IAMUtils::convertToUTF8(m_settings.m_endpointUrl);
}
if (m_settings.m_stsConnectionTimeout > 0)
{
config.httpRequestTimeoutMs = m_settings.m_stsConnectionTimeout * 1000;
config.connectTimeoutMs = m_settings.m_stsConnectionTimeout * 1000;
config.requestTimeoutMs = m_settings.m_stsConnectionTimeout * 1000;
}
RS_LOG_DEBUG("IAMCLNT",
"RsIamClient::SendClusterCredentialRequest",
"httpRequestTimeoutMs: %ld, connectTimeoutMs: %ld, requestTimeoutMs: %ld",
config.httpRequestTimeoutMs,
config.connectTimeoutMs,
config.requestTimeoutMs);
#ifndef _WIN32
// Added CA file to the config for verifying STS server certificate
if (!m_settings.m_caFile.empty())
{
config.caFile = m_settings.m_caFile;
} else if (!m_settings.m_caPath.empty()) {
config.caFile = IAMUtils::convertToUTF8(IAMUtils::GetDefaultCaFile(m_settings.m_caPath));
}
else
{
config.caFile = IAMUtils::convertToUTF8(IAMUtils::GetDefaultCaFile()); // .GetAsPlatformString()
}
#endif
if (!m_settings.m_httpsProxyHost.empty())
{
config.proxyHost = m_settings.m_httpsProxyHost;
config.proxyPort = m_settings.m_httpsProxyPort;
config.proxyUserName = m_settings.m_httpsProxyUsername;
config.proxyPassword = m_settings.m_httpsProxyPassword;
}
Aws::Redshift::RedshiftClient client(in_credentialsProvider, config);
// Compose the ClusterCredentialRequest object
Model::GetClusterCredentialsRequest request;
if(m_settings.m_isCname) {
request.SetCustomDomainName(m_settings.m_host.c_str());
}
else {
request.SetClusterIdentifier(m_settings.m_clusterIdentifer.empty() ?
inferredClusterId.c_str() : m_settings.m_clusterIdentifer.c_str());
}
request.SetDbName(m_settings.m_database);
request.SetDbUser(m_settings.m_dbUser);
request.SetAutoCreate(m_settings.m_userAutoCreate);
if (!IAMUtils::isEmpty(m_settings.m_dbGroups))
{
rs_wstring dbGroupsCaseSensitive(m_settings.m_dbGroups);
if (m_settings.m_forceLowercase)
{
dbGroupsCaseSensitive = IAMUtils::toLower(dbGroupsCaseSensitive);
}
request.SetDbGroups(IAMUtils::TokenizeSetting(dbGroupsCaseSensitive, L","));
}
/* If m_credentials contains dbUser, dbGroups, forceLowercase, and autoCreate,
and if these values are empty in m_settings, use these settings */
const rs_string& dbUser = m_credentials.GetDbUser();
const rs_wstring& dbGroups = IAMUtils::convertStringToWstring(m_credentials.GetDbGroups());
bool forceLowercase = m_credentials.GetForceLowercase();
bool userAutoCreate = m_credentials.GetAutoCreate();
if (m_settings.m_dbUser.empty() && !dbUser.empty())
{
request.SetDbUser(dbUser);
}
if (IAMUtils::isEmpty(m_settings.m_dbGroups) && !IAMUtils::isEmpty(dbGroups))
{
rs_wstring dbGroupsCaseSensitive(dbGroups);
if (m_settings.m_forceLowercase || forceLowercase)
{
dbGroupsCaseSensitive = IAMUtils::toLower(dbGroupsCaseSensitive);
}
request.SetDbGroups(IAMUtils::TokenizeSetting(dbGroupsCaseSensitive, L","));
}
if (!m_settings.m_userAutoCreate)
{
request.SetAutoCreate(userAutoCreate);
}
/*
If we know this is a custom cluster name, we need to fetch the clusterId associated with it
*/
Model::GetClusterCredentialsOutcome outcome;
rs_string tempClusterIdentifier;
try {
if (m_settings.m_isCname) {
Model::DescribeCustomDomainAssociationsRequest describeRequest;
describeRequest.SetCustomDomainName(m_settings.m_host);
const Model::DescribeCustomDomainAssociationsOutcome& describeResponseOutcome = client.DescribeCustomDomainAssociations(describeRequest);
if (describeResponseOutcome.IsSuccess()) {
const Model::DescribeCustomDomainAssociationsResult& describeResponse = describeResponseOutcome.GetResult();
const auto& associations = describeResponse.GetAssociations();
if (!associations.empty()) {
const auto& certificateAssociations = associations[0].GetCertificateAssociations();
if (!certificateAssociations.empty()) {
tempClusterIdentifier = certificateAssociations[0].GetClusterIdentifier();
}
else{
RS_LOG_ERROR("IAMCLNT", "RsIamClient::SendClusterCredentialRequest:CNAME Feature: No certificateAssociations list Found!");
IAMUtils::ThrowConnectionExceptionWithInfo("CNAME FEATURE::No certificateAssociations list Found!");
}
}
else{
RS_LOG_ERROR("IAMCLNT", "RsIamClient::SendClusterCredentialRequest:CNAME Feature: No Associations list Found!");
IAMUtils::ThrowConnectionExceptionWithInfo("CNAME FEATURE::No Associations list Found!");
}
}
else {
RS_LOG_DEBUG("IAMCLNT", "Failed to describe custom domain associations. Assuming cluster incorrectly classified as cname, setting cluster identifier directly.");
request.SetCustomDomainName(""); // Clear the custom domain name
request.SetClusterIdentifier(m_settings.m_clusterIdentifer.empty() ? inferredClusterId.c_str() : m_settings.m_clusterIdentifer.c_str()); // Set the cluster identifier instead
}
}
/* if host is empty describe cluster using cluster id */
if (m_settings.m_host.empty() || m_settings.m_port == 0) {
if (m_settings.m_clusterIdentifer.empty() && tempClusterIdentifier.empty()) {
RS_LOG_ERROR("IAMCLNT", "RsIamClient::SendClusterCredentialRequest: Can not describe cluster: missing clusterId or region.");
IAMUtils::ThrowConnectionExceptionWithInfo("Can not describe cluster: missing clusterId or region.");
}
Model::Endpoint endpoint = DescribeCluster(client, tempClusterIdentifier.empty() ? m_settings.m_clusterIdentifer : tempClusterIdentifier);
m_credentials.SetHost(endpoint.GetAddress());
m_credentials.SetPort(static_cast<short>(endpoint.GetPort()));
}
RS_LOG_DEBUG("IAMCLNT", "RsIamClient::SendClusterCredentialRequest: Before GetClusterCredentials()");
// Send the request using RedshiftClient
outcome = client.GetClusterCredentials(request);
RS_LOG_DEBUG("IAMCLNT", "RsIamClient::SendClusterCredentialRequest: After GetClusterCredentials()");
}
catch (const Aws::Client::AWSError<Aws::Redshift::RedshiftErrors>& ex) {
RS_LOG_DEBUG("IAMCLNT", "Failed to call GetClusterCredentials. Exception: %s", ex.GetMessage().c_str());
throw ex;
}
return outcome;
}