All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.snowflake.client.core.SessionUtil Maven / Gradle / Ivy

There is a newer version: 3.18.0
Show newest version
/*
 * Copyright (c) 2012-2019 Snowflake Computing Inc. All rights reserved.
 */

package net.snowflake.client.core;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import net.snowflake.client.jdbc.ErrorCode;
import net.snowflake.client.jdbc.SnowflakeDriver;
import net.snowflake.client.jdbc.SnowflakeReauthenticationRequest;
import net.snowflake.client.jdbc.SnowflakeSQLException;
import net.snowflake.client.jdbc.SnowflakeType;
import net.snowflake.client.jdbc.SnowflakeUtil;
import net.snowflake.client.jdbc.telemetryOOB.TelemetryService;
import net.snowflake.client.log.ArgSupplier;
import net.snowflake.client.log.SFLogger;
import net.snowflake.client.log.SFLoggerFactory;
import net.snowflake.common.core.ClientAuthnDTO;
import net.snowflake.common.core.ClientAuthnParameter;
import net.snowflake.common.core.SqlState;
import org.apache.http.HttpHeaders;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.HeaderGroup;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static net.snowflake.client.core.SFTrustManager.resetOCSPResponseCacherServerURL;
import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty;

/**
 * Low level session util
 */
public class SessionUtil
{
  private static final
  SFLogger logger = SFLoggerFactory.getLogger(SessionUtil.class);
  // Response Field Name
  private static final String SF_QUERY_DATABASE = "databaseName";
  private static final String SF_QUERY_SCHEMA = "schemaName";
  private static final String SF_QUERY_WAREHOUSE = "warehouse";
  private static final String SF_QUERY_ROLE = "roleName";

  // Request path
  private static final String SF_PATH_LOGIN_REQUEST =
      "/session/v1/login-request";
  private static final String SF_PATH_TOKEN_REQUEST = "/session/token-request";
  protected static final String SF_PATH_AUTHENTICATOR_REQUEST
      = "/session/authenticator-request";

  public static final String SF_QUERY_SESSION_DELETE = "delete";

  // Headers
  public static final String SF_HEADER_AUTHORIZATION = HttpHeaders.AUTHORIZATION;

  // Authentication type
  private static final String SF_HEADER_BASIC_AUTHTYPE = "Basic";
  private static final String SF_HEADER_SNOWFLAKE_AUTHTYPE = "Snowflake";
  private static final String SF_HEADER_TOKEN_TAG = "Token";
  private static final String CLIENT_STORE_TEMPORARY_CREDENTIAL = "CLIENT_STORE_TEMPORARY_CREDENTIAL";
  private static final String SERVICE_NAME = "SERVICE_NAME";
  private static final String CLIENT_IN_BAND_TELEMETRY_ENABLED = "CLIENT_TELEMETRY_ENABLED";
  private static final String CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED = "CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED";
  private static final String CLIENT_RESULT_COLUMN_CASE_INSENSITIVE = "CLIENT_RESULT_COLUMN_CASE_INSENSITIVE";
  private static final String JDBC_RS_COLUMN_CASE_INSENSITIVE = "JDBC_RS_COLUMN_CASE_INSENSITIVE";
  private static final String CLIENT_RESULT_CHUNK_SIZE_JVM = "net.snowflake.jdbc.clientResultChunkSize";
  public static final String CLIENT_RESULT_CHUNK_SIZE = "CLIENT_RESULT_CHUNK_SIZE";
  public static final String CLIENT_MEMORY_LIMIT_JVM = "net.snowflake.jdbc.clientMemoryLimit";
  public static final String CLIENT_MEMORY_LIMIT = "CLIENT_MEMORY_LIMIT";
  public static final String CLIENT_PREFETCH_THREADS_JVM = "net.snowflake.jdbc.clientPrefetchThreads";
  public static final String CLIENT_PREFETCH_THREADS = "CLIENT_PREFETCH_THREADS";
  public static final String CLIENT_ENABLE_CONSERVATIVE_MEMORY_USAGE_JVM
      = "net.snowflake.jdbc.clientEnableConservativeMemoryUsage";
  public static final String CLIENT_ENABLE_CONSERVATIVE_MEMORY_USAGE = "CLIENT_ENABLE_CONSERVATIVE_MEMORY_USAGE";
  public static final String CLIENT_CONSERVATIVE_MEMORY_ADJUST_STEP = "CLIENT_CONSERVATIVE_MEMORY_ADJUST_STEP";
  public static final String OCSP_FAIL_OPEN_JVM = "net.snowflake.jdbc.ocspFailOpen";
  private static final String OCSP_FAIL_OPEN = "ocspFailOpen";
  public static final String CLIENT_SESSION_KEEP_ALIVE_HEARTBEAT_FREQUENCY =
      "CLIENT_SESSION_KEEP_ALIVE_HEARTBEAT_FREQUENCY";
  public static final String CLIENT_SFSQL = "CLIENT_SFSQL";
  public static final String CLIENT_VALIDATE_DEFAULT_PARAMETERS = "CLIENT_VALIDATE_DEFAULT_PARAMETERS";

  static final String SF_HEADER_SERVICE_NAME = "X-Snowflake-Service";

  private static final String NO_QUERY_ID = "";
  private static final String SF_PATH_SESSION = "/session";
  public static long DEFAULT_CLIENT_MEMORY_LIMIT = 1536; // MB
  public static int DEFAULT_CLIENT_PREFETCH_THREADS = 4;
  public static int MIN_CLIENT_CHUNK_SIZE = 48;
  public static int MAX_CLIENT_CHUNK_SIZE = 160;
  public static Map JVM_PARAMS_TO_PARAMS = Stream.of(
      new String[][]{
          {CLIENT_RESULT_CHUNK_SIZE_JVM, CLIENT_RESULT_CHUNK_SIZE},
          {CLIENT_MEMORY_LIMIT_JVM, CLIENT_MEMORY_LIMIT},
          {CLIENT_PREFETCH_THREADS_JVM, CLIENT_PREFETCH_THREADS},
          {OCSP_FAIL_OPEN_JVM, OCSP_FAIL_OPEN},
          {CLIENT_ENABLE_CONSERVATIVE_MEMORY_USAGE_JVM,
           CLIENT_ENABLE_CONSERVATIVE_MEMORY_USAGE}
      }).collect(Collectors.toMap(data -> data[0], data -> data[1]));
  private static ObjectMapper mapper = ObjectMapperFactory.getObjectMapper();
  private static int DEFAULT_HEALTH_CHECK_INTERVAL = 45; // sec
  private static Set STRING_PARAMS = new HashSet<>(Arrays.asList(
      "TIMEZONE",
      "TIMESTAMP_OUTPUT_FORMAT",
      "TIMESTAMP_NTZ_OUTPUT_FORMAT",
      "TIMESTAMP_LTZ_OUTPUT_FORMAT",
      "TIMESTAMP_TZ_OUTPUT_FORMAT",
      "DATE_OUTPUT_FORMAT",
      "TIME_OUTPUT_FORMAT",
      "BINARY_OUTPUT_FORMAT",
      "CLIENT_TIMESTAMP_TYPE_MAPPING",
      SERVICE_NAME));
  private static final Set INT_PARAMS = new HashSet<>(Arrays.asList(
      CLIENT_PREFETCH_THREADS,
      CLIENT_MEMORY_LIMIT,
      CLIENT_RESULT_CHUNK_SIZE,
      "CLIENT_STAGE_ARRAY_BINDING_THRESHOLD",
      "CLIENT_SESSION_KEEP_ALIVE_HEARTBEAT_FREQUENCY"));
  private static final Set BOOLEAN_PARAMS = new HashSet<>(Arrays.asList(
      CLIENT_SESSION_KEEP_ALIVE_HEARTBEAT_FREQUENCY,
      "CLIENT_HONOR_CLIENT_TZ_FOR_TIMESTAMP_NTZ",
      "CLIENT_DISABLE_INCIDENTS",
      "CLIENT_SESSION_KEEP_ALIVE",
      CLIENT_IN_BAND_TELEMETRY_ENABLED,
      CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED,
      CLIENT_STORE_TEMPORARY_CREDENTIAL,
      "JDBC_USE_JSON_PARSER",
      "AUTOCOMMIT",
      "JDBC_EFFICIENT_CHUNK_STORAGE",
      JDBC_RS_COLUMN_CASE_INSENSITIVE,
      CLIENT_RESULT_COLUMN_CASE_INSENSITIVE,
      "CLIENT_METADATA_REQUEST_USE_CONNECTION_CTX",
      "JDBC_TREAT_DECIMAL_AS_INT",
      "JDBC_ENABLE_COMBINED_DESCRIBE",
      CLIENT_ENABLE_CONSERVATIVE_MEMORY_USAGE,
      CLIENT_VALIDATE_DEFAULT_PARAMETERS));

  /**
   * Returns Authenticator type
   *
   * @param loginInput login information
   * @return Authenticator type
   */
  static private ClientAuthnDTO.AuthenticatorType getAuthenticator(
      SFLoginInput loginInput)
  {
    if (loginInput.getAuthenticator() != null)
    {
      if (loginInput.getAuthenticator().equalsIgnoreCase(
          ClientAuthnDTO.AuthenticatorType.EXTERNALBROWSER.name()))
      {
        // SAML 2.0 compliant service/application
        return ClientAuthnDTO.AuthenticatorType.EXTERNALBROWSER;
      }
      else if (loginInput.getAuthenticator().equalsIgnoreCase(
          ClientAuthnDTO.AuthenticatorType.OAUTH.name()))
      {
        // OAuth Authentication
        return ClientAuthnDTO.AuthenticatorType.OAUTH;
      }
      else if (loginInput.getAuthenticator().equalsIgnoreCase(
          ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT.name()))
      {
        return ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT;
      }
      else if (!loginInput.getAuthenticator().equalsIgnoreCase(
          ClientAuthnDTO.AuthenticatorType.SNOWFLAKE.name()))
      {
        // OKTA authenticator v1.
        return ClientAuthnDTO.AuthenticatorType.OKTA;
      }
    }

    // authenticator is null, then jdbc will decide authenticator depends on
    // if privateKey is specified or not. If yes, authenticator type will be
    // SNOWFLAKE_JWT, otherwise it will use SNOWFLAKE.
    return loginInput.getPrivateKey() != null ?
           ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT :
           ClientAuthnDTO.AuthenticatorType.SNOWFLAKE;
  }

  /**
   * Open a new session
   *
   * @param loginInput login information
   * @return information get after login such as token information
   * @throws SFException           if unexpected uri syntax
   * @throws SnowflakeSQLException if failed to establish connection with snowflake
   */
  static SFLoginOutput openSession(SFLoginInput loginInput)
  throws SFException, SnowflakeSQLException
  {
    AssertUtil.assertTrue(loginInput.getServerUrl() != null,
                          "missing server URL for opening session");

    AssertUtil.assertTrue(loginInput.getAppId() != null,
                          "missing app id for opening session");

    AssertUtil.assertTrue(loginInput.getLoginTimeout() >= 0,
                          "negative login timeout for opening session");

    final ClientAuthnDTO.AuthenticatorType authenticator = getAuthenticator(
        loginInput);
    if (!authenticator.equals(ClientAuthnDTO.AuthenticatorType.OAUTH))
    {
      // OAuth does not require a username
      AssertUtil.assertTrue(loginInput.getUserName() != null,
                            "missing user name for opening session");
    }
    if (authenticator.equals(ClientAuthnDTO.AuthenticatorType.EXTERNALBROWSER))
    {
      // force to set the flag.
      loginInput.getSessionParameters().put(CLIENT_STORE_TEMPORARY_CREDENTIAL, true);
    }
    else
    {
      // TODO: patch for now. We should update mergeProperteis
      // to normalize all parameters using STRING_PARAMS, INT_PARAMS and
      // BOOLEAN_PARAMS.
      Object value = loginInput.getSessionParameters().get(
          CLIENT_STORE_TEMPORARY_CREDENTIAL);
      if (value != null)
      {
        loginInput.getSessionParameters().put(
            CLIENT_STORE_TEMPORARY_CREDENTIAL, asBoolean(value));
      }
    }

    boolean isClientStoreTemporaryCredential = asBoolean(
        loginInput.getSessionParameters().get(CLIENT_STORE_TEMPORARY_CREDENTIAL));
    if (isClientStoreTemporaryCredential && CredentialManager.getInstance().fillCachedIdToken(loginInput))
    {
      try
      {
        return issueSession(loginInput);
      }
      catch (SnowflakeReauthenticationRequest ex)
      {
        logger.debug("The token expired. errorCode. Reauthenticating...");
      }
    }

    return newSession(loginInput);
  }

  static private boolean asBoolean(Object value)
  {
    if (value == null)
    {
      return false;
    }
    switch (value.getClass().getName())
    {
      case "java.lang.Boolean":
        return (Boolean) value;
      case "java.lang.String":
        return Boolean.valueOf((String) value);
    }
    return false;
  }

  static private SFLoginOutput newSession(SFLoginInput loginInput)
  throws SFException, SnowflakeSQLException
  {
    // build URL for login request
    URIBuilder uriBuilder;
    URI loginURI;
    String tokenOrSamlResponse = null;
    String samlProofKey = null;
    boolean consentCacheIdToken = true;

    String sessionToken;
    String masterToken;
    String sessionDatabase;
    String sessionSchema;
    String sessionRole;
    String sessionWarehouse;
    String sessionId;
    long masterTokenValidityInSeconds;
    String idToken;
    String databaseVersion = null;
    int databaseMajorVersion = 0;
    int databaseMinorVersion = 0;
    String newClientForUpgrade;
    int healthCheckInterval = DEFAULT_HEALTH_CHECK_INTERVAL;
    int httpClientSocketTimeout = loginInput.getSocketTimeout();
    final ClientAuthnDTO.AuthenticatorType authenticatorType = getAuthenticator(loginInput);
    Map commonParams;

    try
    {
      uriBuilder = new URIBuilder(loginInput.getServerUrl());

      // add database name and schema name as query parameters
      if (loginInput.getDatabaseName() != null)
      {
        uriBuilder.addParameter(SF_QUERY_DATABASE, loginInput.getDatabaseName());
      }

      if (loginInput.getSchemaName() != null)
      {
        uriBuilder.addParameter(SF_QUERY_SCHEMA, loginInput.getSchemaName());
      }

      if (loginInput.getWarehouse() != null)
      {
        uriBuilder.addParameter(SF_QUERY_WAREHOUSE, loginInput.getWarehouse());
      }

      if (loginInput.getRole() != null)
      {
        uriBuilder.addParameter(SF_QUERY_ROLE, loginInput.getRole());
      }

      if (authenticatorType == ClientAuthnDTO.AuthenticatorType.EXTERNALBROWSER)
      {
        // SAML 2.0 compliant service/application
        SessionUtilExternalBrowser s =
            SessionUtilExternalBrowser.createInstance(loginInput);
        s.authenticate();
        tokenOrSamlResponse = s.getToken();
        samlProofKey = s.getProofKey();
        consentCacheIdToken = s.isConsentCacheIdToken();
      }
      else if (authenticatorType == ClientAuthnDTO.AuthenticatorType.OKTA)
      {
        // okta authenticator v1
        tokenOrSamlResponse = getSamlResponseUsingOkta(loginInput);
      }
      else if (authenticatorType == ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT)
      {
        SessionUtilKeyPair s = new SessionUtilKeyPair(loginInput.getPrivateKey(),
                                                      loginInput.getAccountName(),
                                                      loginInput.getUserName());

        loginInput.setToken(s.issueJwtToken());
      }

      uriBuilder.addParameter(SFSession.SF_QUERY_REQUEST_ID,
                              UUID.randomUUID().toString());


      uriBuilder.setPath(SF_PATH_LOGIN_REQUEST);
      loginURI = uriBuilder.build();
    }
    catch (URISyntaxException ex)
    {
      logger.error("Exception when building URL", ex);

      throw new SFException(ex, ErrorCode.INTERNAL_ERROR,
                            "unexpected URI syntax exception:1");
    }

    if (loginInput.getServerUrl().indexOf(".privatelink.snowflakecomputing.com") > 0)
    {
      // Privatelink uses special OCSP Cache server
      try
      {
        URL url = new URL(loginInput.getServerUrl());
        String host = url.getHost();
        logger.debug("HOST: {}", host);
        String ocspCacheServerUrl = String.format(
            "http://ocsp.%s/%s",
            host,
            SFTrustManager.CACHE_FILE_NAME);
        logger.debug("OCSP Cache Server for Privatelink: {}",
                     ocspCacheServerUrl);
        resetOCSPResponseCacherServerURL(ocspCacheServerUrl);
      }
      catch (IOException ex)
      {
        throw new SFException(ex, ErrorCode.INTERNAL_ERROR,
                              "unexpected URL syntax exception");
      }
    }

    HttpPost postRequest = null;

    try
    {
      ClientAuthnDTO authnData = new ClientAuthnDTO();
      Map data = new HashMap<>();
      data.put(ClientAuthnParameter.CLIENT_APP_ID.name(), loginInput.getAppId());

      /*
       * username is always included regardless of authenticator to identify
       * the user.
       */
      data.put(ClientAuthnParameter.LOGIN_NAME.name(),
               loginInput.getUserName());

      /*
       * only include password information in the request to GS if federated
       * authentication method is not specified.
       * When specified, this password information is really to be used to
       * authenticate with the IDP provider only, and GS should not have any
       * trace for this information.
       */
      if (authenticatorType == ClientAuthnDTO.AuthenticatorType.SNOWFLAKE)
      {
        data.put(ClientAuthnParameter.PASSWORD.name(), loginInput.getPassword());
      }
      else if (authenticatorType == ClientAuthnDTO.AuthenticatorType.EXTERNALBROWSER)
      {
        data.put(ClientAuthnParameter.AUTHENTICATOR.name(),
                 ClientAuthnDTO.AuthenticatorType.EXTERNALBROWSER.name());
        data.put(ClientAuthnParameter.PROOF_KEY.name(), samlProofKey);
        data.put(ClientAuthnParameter.TOKEN.name(), tokenOrSamlResponse);
      }
      else if (authenticatorType == ClientAuthnDTO.AuthenticatorType.OKTA)
      {
        data.put(ClientAuthnParameter.RAW_SAML_RESPONSE.name(), tokenOrSamlResponse);
      }
      else if (authenticatorType == ClientAuthnDTO.AuthenticatorType.OAUTH ||
               authenticatorType == ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT)
      {
        data.put(ClientAuthnParameter.AUTHENTICATOR.name(), authenticatorType.name());
        data.put(ClientAuthnParameter.TOKEN.name(), loginInput.getToken());
      }

      Map clientEnv = new HashMap<>();
      clientEnv.put("OS", systemGetProperty("os.name"));
      clientEnv.put("OS_VERSION", systemGetProperty("os.version"));
      clientEnv.put("JAVA_VERSION", systemGetProperty("java.version"));
      clientEnv.put("JAVA_RUNTIME", systemGetProperty("java.runtime.name"));
      clientEnv.put("JAVA_VM", systemGetProperty("java.vm.name"));
      clientEnv.put("OCSP_MODE", loginInput.getOCSPMode().name());

      if (loginInput.getApplication() != null)
      {
        clientEnv.put("APPLICATION", loginInput.getApplication());
      }
      else
      {
        // When you add new client environment info, please add new keys to
        // messages_en_US.src.json so that they can be displayed properly in UI
        // detect app name
        String appName = systemGetProperty("sun.java.command");
        // remove the arguments
        if (appName != null)
        {
          if (appName.indexOf(" ") > 0)
          {
            appName = appName.substring(0, appName.indexOf(" "));
          }

          clientEnv.put("APPLICATION", appName);
        }
      }

      // SNOW-20103: track additional client info in session
      String clientInfoJSONStr = systemGetProperty("snowflake.client.info");
      if (clientInfoJSONStr != null)
      {
        JsonNode clientInfoJSON = null;

        try
        {
          clientInfoJSON = mapper.readTree(clientInfoJSONStr);
        }
        catch (Throwable ex)
        {
          logger.debug(
              "failed to process snowflake.client.info property as JSON: {}"
              , clientInfoJSONStr, ex);
        }

        if (clientInfoJSON != null)
        {
          Iterator> fields = clientInfoJSON.fields();
          while (fields.hasNext())
          {
            Map.Entry field = fields.next();
            clientEnv.put(field.getKey(), field.getValue().asText());
          }
        }
      }

      data.put(ClientAuthnParameter.CLIENT_ENVIRONMENT.name(), clientEnv);

      // Initialize the session parameters
      Map sessionParameter = loginInput.getSessionParameters();
      if (loginInput.isValidateDefaultParameters())
      {
        sessionParameter.put(CLIENT_VALIDATE_DEFAULT_PARAMETERS, true);
      }

      if (sessionParameter != null)
      {
        data.put(ClientAuthnParameter.SESSION_PARAMETERS.name(), loginInput
            .getSessionParameters());
      }

      if (loginInput.getAccountName() != null)
      {
        data.put(ClientAuthnParameter.ACCOUNT_NAME.name(),
                 loginInput.getAccountName());
      }

      // Second Factor Authentication
      if (loginInput.isPasscodeInPassword())
      {
        data.put(ClientAuthnParameter.EXT_AUTHN_DUO_METHOD.name(), "passcode");
      }
      else if (loginInput.getPasscode() != null)
      {
        data.put(ClientAuthnParameter.EXT_AUTHN_DUO_METHOD.name(), "passcode");
        data.put(ClientAuthnParameter.PASSCODE.name(),
                 loginInput.getPasscode());
      }
      else
      {
        data.put(ClientAuthnParameter.EXT_AUTHN_DUO_METHOD.name(), "push");
      }

      data.put(ClientAuthnParameter.CLIENT_APP_VERSION.name(),
               loginInput.getAppVersion());

      authnData.setData(data);
      String json = mapper.writeValueAsString(authnData);

      postRequest = new HttpPost(loginURI);

      // attach the login info json body to the post request
      StringEntity input = new StringEntity(json, StandardCharsets.UTF_8);
      input.setContentType("application/json");
      postRequest.setEntity(input);

      postRequest.addHeader("accept", "application/json");

      /*
       * HttpClient should take authorization header from char[] instead of
       * String.
       */
      postRequest.setHeader(SF_HEADER_AUTHORIZATION,
                            SF_HEADER_BASIC_AUTHTYPE);

      setServiceNameHeader(loginInput, postRequest);

      String theString = HttpUtil.executeGeneralRequest(
          postRequest,
          loginInput.getLoginTimeout(),
          loginInput.getOCSPMode());

      // general method, same as with data binding
      JsonNode jsonNode = mapper.readTree(theString);

      // check the success field first
      if (!jsonNode.path("success").asBoolean())
      {
        logger.debug("response = {}", theString);

        int errorCode = jsonNode.path("code").asInt();
        throw new SnowflakeSQLException(
            NO_QUERY_ID, jsonNode.path("message").asText(),
            SqlState.SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, errorCode);
      }

      // session token is in the data field of the returned json response
      sessionToken = jsonNode.path("data").path("token").asText();
      masterToken = jsonNode.path("data").path("masterToken").asText();
      idToken = nullStringAsEmptyString(
          jsonNode.path("data").path("idToken").asText());
      masterTokenValidityInSeconds = jsonNode.path("data").
          path("masterValidityInSeconds").asLong();
      String serverVersion =
          jsonNode.path("data").path("serverVersion").asText();
      sessionId = jsonNode.path("data").path("sessionId").asText();

      JsonNode dbNode = jsonNode.path("data").path("sessionInfo").path("databaseName");
      sessionDatabase = dbNode.isNull() ? null : dbNode.asText();
      JsonNode schemaNode = jsonNode.path("data").path("sessionInfo").path("schemaName");
      sessionSchema = schemaNode.isNull() ? null : schemaNode.asText();
      JsonNode roleNode = jsonNode.path("data").path("sessionInfo").path("roleName");
      sessionRole = roleNode.isNull() ? null : roleNode.asText();
      JsonNode warehouseNode = jsonNode.path("data").path("sessionInfo").path("warehouseName");
      sessionWarehouse = warehouseNode.isNull() ? null : warehouseNode.asText();

      commonParams =
          SessionUtil.getCommonParams(jsonNode.path("data").path("parameters"));

      if (serverVersion != null)
      {
        logger.debug("server version = {}", serverVersion);

        if (serverVersion.indexOf(" ") > 0)
        {
          databaseVersion = serverVersion.substring(0,
                                                    serverVersion.indexOf(" "));
        }
        else
        {
          databaseVersion = serverVersion;
        }
      }
      else
      {
        logger.debug("server version is null");
      }

      if (databaseVersion != null)
      {
        String[] components = databaseVersion.split("\\.");
        if (components.length >= 2)
        {
          try
          {
            databaseMajorVersion = Integer.parseInt(components[0]);
            databaseMinorVersion = Integer.parseInt(components[1]);
          }
          catch (Exception ex)
          {
            logger.error("Exception encountered when parsing server "
                         + "version: {} Exception: {}",
                         databaseVersion, ex.getMessage());
          }
        }
      }
      else
      {
        logger.debug("database version is null");
      }


      if (!jsonNode.path("data").path("newClientForUpgrade").isNull())
      {
        newClientForUpgrade =
            jsonNode.path("data").path("newClientForUpgrade").asText();

        logger.debug("new client: {}", newClientForUpgrade);
      }

      // get health check interval and adjust network timeouts if different
      int healthCheckIntervalFromGS =
          jsonNode.path("data").path("healthCheckInterval").asInt();

      logger.debug(
          "health check interval = {}", healthCheckIntervalFromGS);

      if (healthCheckIntervalFromGS > 0 &&
          healthCheckIntervalFromGS != healthCheckInterval)
      {
        // add health check interval to socket timeout
        httpClientSocketTimeout = loginInput.getSocketTimeout() +
                                  (healthCheckIntervalFromGS * 1000);

        final RequestConfig requestConfig = RequestConfig
            .copy(HttpUtil.getRequestConfigWithoutCookies())
            .setConnectTimeout(loginInput.getConnectionTimeout())
            .setSocketTimeout(httpClientSocketTimeout)
            .build();

        HttpUtil.setRequestConfig(requestConfig);

        logger.debug(
            "adjusted connection timeout to = {}",
            loginInput.getConnectionTimeout());

        logger.debug(
            "adjusted socket timeout to = {}", httpClientSocketTimeout);
      }
    }
    catch (SnowflakeSQLException ex)
    {
      throw ex; // must catch here to avoid Throwable to get the exception
    }
    catch (IOException ex)
    {
      logger.error("IOException when creating session: " +
                   postRequest, ex);

      throw new SnowflakeSQLException(ex, SqlState.IO_ERROR,
                                      ErrorCode.NETWORK_ERROR.getMessageCode(),
                                      "Exception encountered when opening connection: " +
                                      ex.getMessage());
    }
    catch (Throwable ex)
    {
      logger.error("Exception when creating session: " +
                   postRequest, ex);

      throw new SnowflakeSQLException(ex,
                                      SqlState.SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,
                                      ErrorCode.CONNECTION_ERROR.getMessageCode(),
                                      ErrorCode.CONNECTION_ERROR.getMessageCode(), ex.getMessage());
    }

    SFLoginOutput ret = new SFLoginOutput(sessionToken,
                                          masterToken,
                                          masterTokenValidityInSeconds,
                                          idToken,
                                          databaseVersion,
                                          databaseMajorVersion,
                                          databaseMinorVersion,
                                          httpClientSocketTimeout,
                                          sessionDatabase,
                                          sessionSchema,
                                          sessionRole,
                                          sessionWarehouse,
                                          sessionId,
                                          commonParams);
    ret.setUpdatedByTokenRequest(false);

    if (consentCacheIdToken)
    {
      CredentialManager.getInstance().writeTemporaryCredential(loginInput, ret);
    }
    return ret;
  }

  private static void setServiceNameHeader(SFLoginInput loginInput, HttpPost postRequest)
  {
    if (!Strings.isNullOrEmpty(loginInput.getServiceName()))
    {
      // service name is used to route a request to appropriate cluster.
      postRequest.setHeader(SF_HEADER_SERVICE_NAME,
                            loginInput.getServiceName());
    }
  }

  static private String nullStringAsEmptyString(String value)
  {
    if (Strings.isNullOrEmpty(value) || "null".equals(value))
    {
      return "";
    }
    return value;
  }

  /**
   * Delete the id token cache
   */
  static public void deleteIdTokenCache()
  {
    CredentialManager.getInstance().deleteIdTokenCache();
  }

  /**
   * Renew a session.
   * 

* Use cases: * - Session and Master tokens are provided. No Id token: * - succeed in getting a new Session token. * - fail and raise SnowflakeReauthenticationRequest because Master * token expires. Since no id token exists, the exception is thrown * to the upstream. * - Session and Id tokens are provided. No Master token: * - fail and raise SnowflakeReauthenticationRequest and * issue a new Session token * - fail and raise SnowflakeReauthenticationRequest and fail * to issue a new Session token as the * * @param loginInput login information * @return login output * @throws SFException if unexpected uri information * @throws SnowflakeSQLException if failed to renew the session */ static SFLoginOutput renewSession(SFLoginInput loginInput) throws SFException, SnowflakeSQLException { try { return tokenRequest(loginInput, TokenRequestType.RENEW); } catch (SnowflakeReauthenticationRequest ex) { if (Strings.isNullOrEmpty(loginInput.getIdToken())) { throw ex; } return tokenRequest(loginInput, TokenRequestType.ISSUE); } } /** * Issue a session * * @param loginInput login information * @return login output * @throws SFException if unexpected uri information * @throws SnowflakeSQLException if failed to renew the session */ static private SFLoginOutput issueSession(SFLoginInput loginInput) throws SFException, SnowflakeSQLException { return tokenRequest(loginInput, TokenRequestType.ISSUE); } static private SFLoginOutput tokenRequest( SFLoginInput loginInput, TokenRequestType requestType) throws SFException, SnowflakeSQLException { AssertUtil.assertTrue(loginInput.getServerUrl() != null, "missing server URL for tokenRequest"); if (requestType == TokenRequestType.RENEW) { AssertUtil.assertTrue(loginInput.getMasterToken() != null, "missing master token for tokenRequest"); AssertUtil.assertTrue(loginInput.getSessionToken() != null, "missing session token for tokenRequest"); } else if (requestType == TokenRequestType.ISSUE) { AssertUtil.assertTrue(loginInput.getIdToken() != null, "missing id token for tokenRequest"); } AssertUtil.assertTrue(loginInput.getLoginTimeout() >= 0, "negative login timeout for tokenRequest"); // build URL for login request URIBuilder uriBuilder; HttpPost postRequest; String sessionToken; String masterToken; try { uriBuilder = new URIBuilder(loginInput.getServerUrl()); uriBuilder.setPath(SF_PATH_TOKEN_REQUEST); uriBuilder.addParameter(SFSession.SF_QUERY_REQUEST_ID, UUID.randomUUID().toString()); postRequest = new HttpPost(uriBuilder.build()); } catch (URISyntaxException ex) { logger.error("Exception when creating http request", ex); throw new SFException(ex, ErrorCode.INTERNAL_ERROR, "unexpected URI syntax exception:3"); } try { // input json with old session token and request type, notice the // session token needs to be quoted. Map payload = new HashMap<>(); String headerToken; if (requestType == TokenRequestType.RENEW) { headerToken = loginInput.getMasterToken(); payload.put("oldSessionToken", loginInput.getSessionToken()); } else { headerToken = loginInput.getIdToken(); payload.put("idToken", loginInput.getIdToken()); } payload.put("requestType", requestType.value); String json = mapper.writeValueAsString(payload); // attach the login info json body to the post request StringEntity input = new StringEntity(json, StandardCharsets.UTF_8); input.setContentType("application/json"); postRequest.setEntity(input); postRequest.addHeader("accept", "application/json"); postRequest.setHeader( SF_HEADER_AUTHORIZATION, SF_HEADER_SNOWFLAKE_AUTHTYPE + " " + SF_HEADER_TOKEN_TAG + "=\"" + headerToken + "\""); setServiceNameHeader(loginInput, postRequest); logger.debug( "request type: {}, old session token: {}, " + "master token: {}, id token: {}", requestType.value, (ArgSupplier) () -> loginInput.getSessionToken() != null ? "******" : null, (ArgSupplier) () -> loginInput.getMasterToken() != null ? "******" : null, (ArgSupplier) () -> loginInput.getIdToken() != null ? "******" : null); String theString = HttpUtil.executeGeneralRequest( postRequest, loginInput.getLoginTimeout(), loginInput.getOCSPMode()); // general method, same as with data binding JsonNode jsonNode = mapper.readTree(theString); // check the success field first if (!jsonNode.path("success").asBoolean()) { logger.debug("response = {}", theString); String errorCode = jsonNode.path("code").asText(); String message = jsonNode.path("message").asText(); EventUtil.triggerBasicEvent( Event.EventType.NETWORK_ERROR, "SessionUtil:renewSession failure, error code=" + errorCode + ", message=" + message, true); SnowflakeUtil.checkErrorAndThrowExceptionIncludingReauth(jsonNode); } // session token is in the data field of the returned json response sessionToken = jsonNode.path("data").path("sessionToken").asText(); masterToken = jsonNode.path("data").path("masterToken").asText(); } catch (IOException ex) { logger.error("IOException when renewing session: " + postRequest, ex); // Any EventType.NETWORK_ERRORs should have been triggered before // exception was thrown. throw new SFException(ex, ErrorCode.NETWORK_ERROR, ex.getMessage()); } SFLoginOutput loginOutput = new SFLoginOutput(); loginOutput .setSessionToken(sessionToken) .setMasterToken(masterToken) .setUpdatedByTokenRequest(true) .setUpdatedByTokenRequestIssue(requestType == TokenRequestType.ISSUE); return loginOutput; } /** * Close a session * * @param loginInput login information * @throws SnowflakeSQLException if failed to close session * @throws SFException if failed to close session */ static void closeSession(SFLoginInput loginInput) throws SFException, SnowflakeSQLException { logger.debug(" public void close() throws SFException"); // assert the following inputs are valid AssertUtil.assertTrue(loginInput.getServerUrl() != null, "missing server URL for closing session"); AssertUtil.assertTrue(loginInput.getSessionToken() != null, "missing session token for closing session"); AssertUtil.assertTrue(loginInput.getLoginTimeout() >= 0, "missing login timeout for closing session"); HttpPost postRequest = null; try { URIBuilder uriBuilder; uriBuilder = new URIBuilder(loginInput.getServerUrl()); uriBuilder.addParameter(SF_QUERY_SESSION_DELETE, Boolean.TRUE.toString()); uriBuilder.addParameter(SFSession.SF_QUERY_REQUEST_ID, UUID.randomUUID().toString()); uriBuilder.setPath(SF_PATH_SESSION); postRequest = new HttpPost(uriBuilder.build()); postRequest.setHeader(SF_HEADER_AUTHORIZATION, SF_HEADER_SNOWFLAKE_AUTHTYPE + " " + SF_HEADER_TOKEN_TAG + "=\"" + loginInput.getSessionToken() + "\""); setServiceNameHeader(loginInput, postRequest); String theString = HttpUtil.executeGeneralRequest( postRequest, loginInput.getLoginTimeout(), loginInput.getOCSPMode()); JsonNode rootNode; logger.debug( "connection close response: {}", theString); rootNode = mapper.readTree(theString); SnowflakeUtil.checkErrorAndThrowException(rootNode); } catch (URISyntaxException ex) { throw new RuntimeException("unexpected URI syntax exception", ex); } catch (IOException ex) { logger.error("unexpected IO exception for: " + postRequest, ex); } catch (SnowflakeSQLException ex) { // ignore exceptions for session expiration exceptions and for // sessions that no longer exist if (ex.getErrorCode() != Constants.SESSION_EXPIRED_GS_CODE && ex.getErrorCode() != Constants.SESSION_GONE) { throw ex; } } } /** * Given access token, query IDP URL snowflake app to get SAML response * We also need to perform important client side validation: * validate the post back url come back with the SAML response * contains the same prefix as the Snowflake's server url, which is the * intended destination url to Snowflake. * Explanation: * This emulates the behavior of IDP initiated login flow in the user * browser where the IDP instructs the browser to POST the SAML * assertion to the specific SP endpoint. This is critical in * preventing a SAML assertion issued to one SP from being sent to * another SP. * * @param loginInput Login Info for the request * @param ssoUrl URL to use for SSO * @param oneTimeToken The token used for SSO * @return The response in HTML form * @throws SnowflakeSQLException Will be thrown if the destination URL in * the SAML assertion does not match */ private static String federatedFlowStep4( SFLoginInput loginInput, String ssoUrl, String oneTimeToken) throws SnowflakeSQLException { String responseHtml = ""; try { final URL url = new URL(ssoUrl); URI oktaGetUri = new URIBuilder() .setScheme(url.getProtocol()) .setHost(url.getHost()) .setPath(url.getPath()) .setParameter("RelayState", "%2Fsome%2Fdeep%2Flink") .setParameter("onetimetoken", oneTimeToken).build(); HttpGet httpGet = new HttpGet(oktaGetUri); HeaderGroup headers = new HeaderGroup(); headers.addHeader(new BasicHeader(HttpHeaders.ACCEPT, "*/*")); httpGet.setHeaders(headers.getAllHeaders()); responseHtml = HttpUtil.executeGeneralRequest( httpGet, loginInput.getLoginTimeout(), loginInput.getOCSPMode()); // step 5 String postBackUrl = getPostBackUrlFromHTML(responseHtml); if (!isPrefixEqual(postBackUrl, loginInput.getServerUrl())) { logger.debug("The specified authenticator {} and the destination URL " + "in the SAML assertion {} do not match.", loginInput.getAuthenticator(), postBackUrl); throw new SnowflakeSQLException( SqlState.SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, ErrorCode.IDP_INCORRECT_DESTINATION.getMessageCode()); } } catch (IOException | URISyntaxException ex) { handleFederatedFlowError(loginInput, ex); } return responseHtml; } /** * Query IDP token url to authenticate and retrieve access token * * @param loginInput The login info for the request * @param tokenUrl The URL used to retrieve the access token * @return Returns the one time token * @throws SnowflakeSQLException Will be thrown if the execute request fails */ private static String federatedFlowStep3(SFLoginInput loginInput, String tokenUrl) throws SnowflakeSQLException { String oneTimeToken = ""; try { URL url = new URL(tokenUrl); URI tokenUri = url.toURI(); final HttpPost postRequest = new HttpPost(tokenUri); String userName; if (Strings.isNullOrEmpty(loginInput.getOKTAUserName())) { userName = loginInput.getUserName(); } else { userName = loginInput.getOKTAUserName(); } StringEntity params = new StringEntity("{\"username\":\"" + userName + "\",\"password\":\"" + loginInput.getPassword() + "\"}"); postRequest.setEntity(params); HeaderGroup headers = new HeaderGroup(); headers.addHeader(new BasicHeader(HttpHeaders.ACCEPT, "application/json")); headers.addHeader(new BasicHeader(HttpHeaders.CONTENT_TYPE, "application/json")); postRequest.setHeaders(headers.getAllHeaders()); final String idpResponse = HttpUtil.executeRequestWithoutCookies( postRequest, loginInput.getLoginTimeout(), 0, null, loginInput.getOCSPMode()); logger.debug("user is authenticated against {}.", loginInput.getAuthenticator()); // session token is in the data field of the returned json response final JsonNode jsonNode = mapper.readTree(idpResponse); oneTimeToken = jsonNode.get("cookieToken").asText(); } catch (IOException | URISyntaxException ex) { handleFederatedFlowError(loginInput, ex); } return oneTimeToken; } /** * Perform important client side validation: * validate both token url and sso url contains same prefix * (protocol + host + port) as the given authenticator url. * Explanation: * This provides a way for the user to 'authenticate' the IDP it is * sending his/her credentials to. Without such a check, the user could * be coerced to provide credentials to an IDP impersonator. * * @param loginInput The login info for the request * @param tokenUrl The token URL * @param ssoUrl The SSO URL * @throws SnowflakeSQLException Will be thrown if the prefix for the * tokenUrl and ssoUrl do not match */ private static void federatedFlowStep2( SFLoginInput loginInput, String tokenUrl, String ssoUrl) throws SnowflakeSQLException { try { if (!isPrefixEqual(loginInput.getAuthenticator(), tokenUrl) || !isPrefixEqual(loginInput.getAuthenticator(), ssoUrl)) { logger.debug("The specified authenticator {} is not supported.", loginInput.getAuthenticator()); throw new SnowflakeSQLException( SqlState.SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, ErrorCode.IDP_CONNECTION_ERROR.getMessageCode()); } } catch (MalformedURLException ex) { handleFederatedFlowError(loginInput, ex); } } /** * Query Snowflake to obtain IDP token url and IDP SSO url * * @param loginInput The login info for the request * @throws SnowflakeSQLException Will be thrown if the execute request step * fails */ private static JsonNode federatedFlowStep1(SFLoginInput loginInput) throws SnowflakeSQLException { JsonNode dataNode = null; try { URIBuilder fedUriBuilder = new URIBuilder(loginInput.getServerUrl()); fedUriBuilder.setPath(SF_PATH_AUTHENTICATOR_REQUEST); URI fedUrlUri = fedUriBuilder.build(); Map data = new HashMap<>(); data.put(ClientAuthnParameter.ACCOUNT_NAME.name(), loginInput.getAccountName()); data.put(ClientAuthnParameter.AUTHENTICATOR.name(), loginInput.getAuthenticator()); data.put(ClientAuthnParameter.CLIENT_APP_ID.name(), loginInput.getAppId()); data.put(ClientAuthnParameter.CLIENT_APP_VERSION.name(), loginInput.getAppVersion()); ClientAuthnDTO authnData = new ClientAuthnDTO(); authnData.setData(data); String json = mapper.writeValueAsString(authnData); // attach the login info json body to the post request StringEntity input = new StringEntity(json, StandardCharsets.UTF_8); input.setContentType("application/json"); HttpPost postRequest = new HttpPost(fedUrlUri); postRequest.setEntity(input); postRequest.addHeader("accept", "application/json"); final String gsResponse = HttpUtil.executeGeneralRequest( postRequest, loginInput.getLoginTimeout(), loginInput.getOCSPMode()); logger.debug("authenticator-request response: {}", gsResponse); JsonNode jsonNode = mapper.readTree(gsResponse); // check the success field first if (!jsonNode.path("success").asBoolean()) { logger.debug("response = {}", gsResponse); int errorCode = jsonNode.path("code").asInt(); throw new SnowflakeSQLException( NO_QUERY_ID, jsonNode.path("message").asText(), SqlState.SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, errorCode); } // session token is in the data field of the returned json response dataNode = jsonNode.path("data"); } catch (IOException | URISyntaxException ex) { handleFederatedFlowError(loginInput, ex); } return dataNode; } /** * Logs an error generated during the federated authentication flow and * re-throws it as a SnowflakeSQLException. * Note that we seperate IOExceptions since those tend to be network related. * * @param loginInput The login info from the request * @param ex The exception to process * @throws SnowflakeSQLException Will be thrown for all calls to this method */ private static void handleFederatedFlowError(SFLoginInput loginInput, Exception ex) throws SnowflakeSQLException { if (ex instanceof IOException) { logger.error("IOException when authenticating with " + loginInput.getAuthenticator(), ex); throw new SnowflakeSQLException(ex, SqlState.IO_ERROR, ErrorCode.NETWORK_ERROR.getMessageCode(), "Exception encountered when opening connection: " + ex.getMessage()); } logger.error("Exception when authenticating with " + loginInput.getAuthenticator(), ex); throw new SnowflakeSQLException(ex, SqlState.SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, ErrorCode.CONNECTION_ERROR.getMessageCode(), ErrorCode.CONNECTION_ERROR.getMessageCode(), ex.getMessage()); } /** * FEDERATED FLOW * See SNOW-27798 for additional details. * * @param loginInput The login info from the request * @return saml response * @throws SnowflakeSQLException Will be thrown if any of the federated * steps fail */ static private String getSamlResponseUsingOkta(SFLoginInput loginInput) throws SnowflakeSQLException { JsonNode dataNode = federatedFlowStep1(loginInput); String tokenUrl = dataNode.path("tokenUrl").asText(); String ssoUrl = dataNode.path("ssoUrl").asText(); federatedFlowStep2(loginInput, tokenUrl, ssoUrl); final String oneTimeToken = federatedFlowStep3(loginInput, tokenUrl); return federatedFlowStep4(loginInput, ssoUrl, oneTimeToken); } /** * Verify if two input urls have the same protocol, host, and port. * * @param aUrlStr a source URL string * @param bUrlStr a target URL string * @return true if matched otherwise false * @throws MalformedURLException raises if a URL string is not valid. */ static boolean isPrefixEqual(String aUrlStr, String bUrlStr) throws MalformedURLException { URL aUrl = new URL(aUrlStr); URL bUrl = new URL(bUrlStr); int aPort = aUrl.getPort(); int bPort = bUrl.getPort(); if (aPort == -1 && "https".equals(aUrl.getProtocol())) { // default port number for HTTPS aPort = 443; } if (bPort == -1 && "https".equals(bUrl.getProtocol())) { // default port number for HTTPS bPort = 443; } // no default port number for HTTP is supported. return aUrl.getHost().equalsIgnoreCase(bUrl.getHost()) && aUrl.getProtocol().equalsIgnoreCase(bUrl.getProtocol()) && aPort == bPort; } /** * Extracts post back url from the HTML returned by the IDP * * @param html The HTML that we are parsing to find the post back url * @return The post back url */ static private String getPostBackUrlFromHTML(String html) { Document doc = Jsoup.parse(html); Elements e1 = doc.getElementsByTag("body"); Elements e2 = e1.get(0).getElementsByTag("form"); return e2.first().attr("action"); } /** * Helper function to parse a JsonNode from a GS response * containing CommonParameters, emitting an EnumMap of parameters * * @param paramsNode parameters in JSON form * @return map object including key and value pairs */ static public Map getCommonParams(JsonNode paramsNode) { Map parameters = new HashMap<>(); for (JsonNode child : paramsNode) { // If there isn't a name then the response from GS must be erroneous. if (!child.hasNonNull("name")) { logger.error("Common Parameter JsonNode encountered with " + "no parameter name!"); continue; } // Look up the parameter based on the "name" attribute of the node. String paramName = child.path("name").asText(); // What type of value is it and what's the value? if (!child.hasNonNull("value")) { logger.debug("No value found for Common Parameter {}", child.path("name").asText()); continue; } if (STRING_PARAMS.contains(paramName.toUpperCase())) { parameters.put(paramName, child.path("value").asText()); } else if (INT_PARAMS.contains(paramName.toUpperCase())) { parameters.put(paramName, child.path("value").asInt()); } else if (BOOLEAN_PARAMS.contains(paramName.toUpperCase())) { parameters.put(paramName, child.path("value").asBoolean()); } else { logger.debug("Unknown Common Parameter: {}", paramName); } logger.debug("Parameter {}: {}", paramName, child.path("value").asText()); } return parameters; } static void updateSfDriverParamValues( Map parameters, SFSession session) { for (Map.Entry entry : parameters.entrySet()) { logger.debug("processing parameter {}", entry.getKey()); if ("CLIENT_DISABLE_INCIDENTS".equalsIgnoreCase(entry.getKey())) { SnowflakeDriver.setDisableIncidents((Boolean) entry.getValue()); } else if ( "CLIENT_SESSION_KEEP_ALIVE".equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setEnableHeartbeat((Boolean) entry.getValue()); } } else if ( "AUTOCOMMIT".equalsIgnoreCase(entry.getKey())) { boolean autoCommit = (Boolean) entry.getValue(); if (session != null && session.getAutoCommit() != autoCommit) { session.setAutoCommit(autoCommit); } } else if ( JDBC_RS_COLUMN_CASE_INSENSITIVE.equalsIgnoreCase(entry.getKey()) || CLIENT_RESULT_COLUMN_CASE_INSENSITIVE.equalsIgnoreCase(entry.getKey())) { if (session != null && !session.isResultColumnCaseInsensitive()) { session.setResultColumnCaseInsensitive((boolean) entry.getValue()); } } else if ("CLIENT_METADATA_REQUEST_USE_CONNECTION_CTX".equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setMetadataRequestUseConnectionCtx((boolean) entry.getValue()); } } else if ("CLIENT_TIMESTAMP_TYPE_MAPPING".equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setTimestampMappedType(SnowflakeType.valueOf( ((String) entry.getValue()).toUpperCase())); } } else if ("JDBC_TREAT_DECIMAL_AS_INT".equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setJdbcTreatDecimalAsInt((boolean) entry.getValue()); } } else if ("JDBC_ENABLE_COMBINED_DESCRIBE".equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setEnableCombineDescribe((boolean) entry.getValue()); } } else if (CLIENT_IN_BAND_TELEMETRY_ENABLED.equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setClientTelemetryEnabled((boolean) entry.getValue()); } } else if ("CLIENT_STAGE_ARRAY_BINDING_THRESHOLD".equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setArrayBindStageThreshold((int) entry.getValue()); } } else if (CLIENT_STORE_TEMPORARY_CREDENTIAL.equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setStoreTemporaryCredential((boolean) entry.getValue()); } } else if (SERVICE_NAME.equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setServiceName((String) entry.getValue()); } } else if (CLIENT_ENABLE_CONSERVATIVE_MEMORY_USAGE.equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setEnableConservativeMemoryUsage((boolean) entry.getValue()); } } else if (CLIENT_CONSERVATIVE_MEMORY_ADJUST_STEP.equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setConservativeMemoryAdjustStep((int) entry.getValue()); } } else if (CLIENT_MEMORY_LIMIT.equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setClientMemoryLimit((int) entry.getValue()); } } else if (CLIENT_RESULT_CHUNK_SIZE.equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setClientResultChunkSize((int) entry.getValue()); } } else if (CLIENT_PREFETCH_THREADS.equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setClientPrefetchThreads((int) entry.getValue()); } } else if (CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED.equalsIgnoreCase(entry.getKey())) { if ((boolean) entry.getValue()) { TelemetryService.getInstance().enable(); } else { TelemetryService.getInstance().disable(); } } else if (CLIENT_VALIDATE_DEFAULT_PARAMETERS.equalsIgnoreCase(entry.getKey())) { if (session != null) { session.setValidateDefaultParameters(SFLoginInput.getBooleanValue(entry.getValue())); } } } } enum TokenRequestType { RENEW("RENEW"), CLONE("CLONE"), ISSUE("ISSUE"); private String value; TokenRequestType(String value) { this.value = value; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy