Model::GetClusterCredentialsWithIAMOutcome RsIamClient::SendClusterCredentialsWithIAMRequest()

in src/odbc/rsodbc/iam/RsIamClient.cpp [487:635]


Model::GetClusterCredentialsWithIAMOutcome RsIamClient::SendClusterCredentialsWithIAMRequest(
	const std::shared_ptr<AWSCredentialsProvider>& in_credentialsProvider)
{
	RS_LOG_DEBUG("IAMCLNT", "RsIamClient::SendClusterCredentialsWithIAMRequest");

	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::SendClusterCredentialsWithIAMRequest",
		"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
	{
		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;
	}

	RedshiftClient client(in_credentialsProvider, config);

	// Compose the ClusterCredentialRequest object
	Model::GetClusterCredentialsWithIAMRequest request;
    if(m_settings.m_isCname) {
        //suppose to call the request.SetCustomDomainName() API but its not available.
        //request.SetCustomDomainName();
        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);
	// m_accessDuration setting corresponds to IAMDuration option
	request.SetDurationSeconds(m_settings.m_accessDuration);

     /*
     If we know this is a custom cluster name, we need to fetch the clusterId associated with it
    */
    Model::GetClusterCredentialsWithIAMOutcome outcome;
    rs_string tempClusterIdentifierIAM;

    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()) {
                        tempClusterIdentifierIAM = certificateAssociations[0].GetClusterIdentifier();
                    }
                    else{
                        RS_LOG_ERROR("IAMCLNT", "RsIamClient::SendClusterCredentialWithIAMRequest:CNAME Feature: No certificateAssociations list Found!");
                       IAMUtils::ThrowConnectionExceptionWithInfo("CNAME FEATURE::No certificateAssociations list Found!");
                    }
                }
                else{
                        RS_LOG_ERROR("IAMCLNT", "RsIamClient::SendClusterCredentialWithIAMRequest: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() && tempClusterIdentifierIAM.empty()) {
                RS_LOG_ERROR("IAMCLNT", "RsIamClient::SendClusterCredentialsithIAMRequest: Can not describe cluster: missing clusterId or region.");
                IAMUtils::ThrowConnectionExceptionWithInfo("Can not describe cluster: missing clusterId or region.");
            }

            Model::Endpoint endpoint = DescribeCluster(client, tempClusterIdentifierIAM.empty() ? m_settings.m_clusterIdentifer : tempClusterIdentifierIAM);
            m_credentials.SetHost(endpoint.GetAddress());
            m_credentials.SetPort(static_cast<short>(endpoint.GetPort()));
        }

        RS_LOG_DEBUG("IAMCLNT", "RsIamClient::SendClusterCredentialswithIAMRequest: Before GetClusterCredentialswithIAM()");

        // Send the request using RedshiftClient
        outcome = client.GetClusterCredentialsWithIAM(request);

        RS_LOG_DEBUG("IAMCLNT", "RsIamClient::SendClusterCredentialswithIAMRequest: After GetClusterCredentialswithIAM()");
    } 
    catch (const Aws::Client::AWSError<Aws::Redshift::RedshiftErrors>& ex) {
        RS_LOG_DEBUG("IAMCLNT", "Failed to call GetClusterCredentialsWithIAM. Exception:%s", ex.GetMessage().c_str());
        throw ex;
    }

    return outcome;
}