Model::GetClusterCredentialsOutcome RsIamClient::SendClusterCredentialsRequest()

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;
}