From 85e5cd755d4a29244b546016aee68461623e6077 Mon Sep 17 00:00:00 2001 From: Eric Simmerman Date: Tue, 2 Jul 2024 11:21:11 -0400 Subject: [PATCH 1/7] SNOW-618478: Support accepting a private key as a base64 encoded string with the new session property key "private_key_base64" --- .../snowflake/client/core/SFLoginInput.java | 16 ++ .../net/snowflake/client/core/SFSession.java | 14 +- .../client/core/SFSessionProperty.java | 1 + .../snowflake/client/core/SessionUtil.java | 33 ++- .../client/core/SessionUtilKeyPair.java | 78 ++++--- .../client/jdbc/SnowflakeBasicDataSource.java | 14 +- .../client/core/SessionUtilLatestIT.java | 16 +- .../client/jdbc/ConnectionLatestIT.java | 199 ++++++++++++++++++ .../client/jdbc/telemetry/TelemetryIT.java | 14 +- 9 files changed, 352 insertions(+), 33 deletions(-) diff --git a/src/main/java/net/snowflake/client/core/SFLoginInput.java b/src/main/java/net/snowflake/client/core/SFLoginInput.java index 18ebfaa57..d096c24ec 100644 --- a/src/main/java/net/snowflake/client/core/SFLoginInput.java +++ b/src/main/java/net/snowflake/client/core/SFLoginInput.java @@ -45,6 +45,7 @@ public class SFLoginInput { private OCSPMode ocspMode; private HttpClientSettingsKey httpClientKey; private String privateKeyFile; + private String privateKeyBase64; private String privateKeyFilePwd; private String inFlightCtx; // Opaque string sent for Snowsight account activation @@ -314,6 +315,11 @@ SFLoginInput setPrivateKey(PrivateKey privateKey) { return this; } + SFLoginInput setPrivateKeyBase64(String privateKeyBase64) { + this.privateKeyBase64 = privateKeyBase64; + return this; + } + SFLoginInput setPrivateKeyFile(String privateKeyFile) { this.privateKeyFile = privateKeyFile; return this; @@ -328,10 +334,20 @@ String getPrivateKeyFile() { return privateKeyFile; } + String getPrivateKeyBase64() { + return privateKeyBase64; + } + String getPrivateKeyFilePwd() { return privateKeyFilePwd; } + boolean isPrivateKeyProvided() { + return (getPrivateKey() != null + || getPrivateKeyFile() != null + || getPrivateKeyBase64() != null); + } + public String getApplication() { return application; } diff --git a/src/main/java/net/snowflake/client/core/SFSession.java b/src/main/java/net/snowflake/client/core/SFSession.java index b8723dc8d..8c93dad2c 100644 --- a/src/main/java/net/snowflake/client/core/SFSession.java +++ b/src/main/java/net/snowflake/client/core/SFSession.java @@ -86,6 +86,7 @@ public class SFSession extends SFBaseSession { private String idToken; private String mfaToken; private String privateKeyFileLocation; + private String privateKeyBase64; private String privateKeyPassword; private PrivateKey privateKey; @@ -445,6 +446,12 @@ public void addSFSessionProperty(String propertyName, Object propertyValue) thro } break; + case PRIVATE_KEY_BASE64: + if (propertyValue != null) { + privateKeyBase64 = (String) propertyValue; + } + break; + case PRIVATE_KEY_FILE_PWD: if (propertyValue != null) { privateKeyPassword = (String) propertyValue; @@ -617,6 +624,8 @@ public synchronized void open() throws SFException, SnowflakeSQLException { .setSessionParameters(sessionParametersMap) .setPrivateKey((PrivateKey) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY)) .setPrivateKeyFile((String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE)) + .setPrivateKeyBase64( + (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_BASE64)) .setPrivateKeyFilePwd( (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD)) .setApplication((String) connectionPropertiesMap.get(SFSessionProperty.APPLICATION)) @@ -735,7 +744,10 @@ private boolean isSnowflakeAuthenticator() { Map connectionPropertiesMap = getConnectionPropertiesMap(); String authenticator = (String) connectionPropertiesMap.get(SFSessionProperty.AUTHENTICATOR); PrivateKey privateKey = (PrivateKey) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY); - return (authenticator == null && privateKey == null && privateKeyFileLocation == null) + return (authenticator == null + && privateKey == null + && privateKeyFileLocation == null + && privateKeyBase64 == null) || ClientAuthnDTO.AuthenticatorType.SNOWFLAKE.name().equalsIgnoreCase(authenticator); } diff --git a/src/main/java/net/snowflake/client/core/SFSessionProperty.java b/src/main/java/net/snowflake/client/core/SFSessionProperty.java index 4f99e4616..f68685450 100644 --- a/src/main/java/net/snowflake/client/core/SFSessionProperty.java +++ b/src/main/java/net/snowflake/client/core/SFSessionProperty.java @@ -54,6 +54,7 @@ public enum SFSessionProperty { VALIDATE_DEFAULT_PARAMETERS("validateDefaultParameters", false, Boolean.class), INJECT_WAIT_IN_PUT("inject_wait_in_put", false, Integer.class), PRIVATE_KEY_FILE("private_key_file", false, String.class), + PRIVATE_KEY_BASE64("private_key_base64", false, String.class), PRIVATE_KEY_FILE_PWD("private_key_file_pwd", false, String.class), CLIENT_INFO("snowflakeClientInfo", false, String.class), ALLOW_UNDERSCORES_IN_HOST("allowUnderscoresInHost", false, Boolean.class), diff --git a/src/main/java/net/snowflake/client/core/SessionUtil.java b/src/main/java/net/snowflake/client/core/SessionUtil.java index 6a9db988f..430c05b75 100644 --- a/src/main/java/net/snowflake/client/core/SessionUtil.java +++ b/src/main/java/net/snowflake/client/core/SessionUtil.java @@ -240,7 +240,7 @@ private static ClientAuthnDTO.AuthenticatorType getAuthenticator(SFLoginInput lo // 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 || loginInput.getPrivateKeyFile() != null) + return loginInput.isPrivateKeyProvided() ? ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT : ClientAuthnDTO.AuthenticatorType.SNOWFLAKE; } @@ -421,6 +421,7 @@ private static SFLoginOutput newSession( new SessionUtilKeyPair( loginInput.getPrivateKey(), loginInput.getPrivateKeyFile(), + loginInput.getPrivateKeyBase64(), loginInput.getPrivateKeyFilePwd(), loginInput.getAccountName(), loginInput.getUserName()); @@ -676,6 +677,7 @@ private static SFLoginOutput newSession( new SessionUtilKeyPair( loginInput.getPrivateKey(), loginInput.getPrivateKeyFile(), + loginInput.getPrivateKeyBase64(), loginInput.getPrivateKeyFilePwd(), loginInput.getAccountName(), loginInput.getUserName()); @@ -1723,6 +1725,7 @@ public static void resetOCSPUrlIfNecessary(String serverUrl) throws IOException * * @param privateKey private key * @param privateKeyFile path to private key file + * @param privateKeyBase64 base64 encoded content of the private key file * @param privateKeyFilePwd password for private key file * @param accountName account name * @param userName user name @@ -1732,13 +1735,39 @@ public static void resetOCSPUrlIfNecessary(String serverUrl) throws IOException public static String generateJWTToken( PrivateKey privateKey, String privateKeyFile, + String privateKeyBase64, String privateKeyFilePwd, String accountName, String userName) throws SFException { SessionUtilKeyPair s = new SessionUtilKeyPair( - privateKey, privateKeyFile, privateKeyFilePwd, accountName, userName); + privateKey, privateKeyFile, privateKeyBase64, privateKeyFilePwd, accountName, userName); + return s.issueJwtToken(); + } + + /** + * Helper function to generate a JWT token + * + * @param privateKey private key + * @param privateKeyFile path to private key file + * @param privateKeyFilePwd password for private key file + * @param accountName account name + * @param userName user name + * @return JWT token + * @throws SFException if Snowflake error occurs + */ + @Deprecated() + public static String generateJWTToken( + PrivateKey privateKey, + String privateKeyFile, + String privateKeyFilePwd, + String accountName, + String userName) + throws SFException { + SessionUtilKeyPair s = + new SessionUtilKeyPair( + privateKey, privateKeyFile, null, privateKeyFilePwd, accountName, userName); return s.issueJwtToken(); } diff --git a/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java b/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java index ad63ea603..77d8ee492 100644 --- a/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java +++ b/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java @@ -13,9 +13,9 @@ import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; -import java.io.FileReader; import java.io.IOException; import java.io.StringReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -80,6 +80,7 @@ class SessionUtilKeyPair { SessionUtilKeyPair( PrivateKey privateKey, String privateKeyFile, + String privateKeyBase64, String privateKeyFilePwd, String accountName, String userName) @@ -100,17 +101,30 @@ class SessionUtilKeyPair { } } - // if there is both a file and a private key, there is a problem + // Ensure that we only received one of: privateKey, privateKeyFile, or privateKeyBase64 if (!Strings.isNullOrEmpty(privateKeyFile) && privateKey != null) { throw new SFException( ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, - "Cannot have both private key value and private key file."); + "Cannot have both private key object and private key file."); + } else if (!Strings.isNullOrEmpty(privateKeyBase64) && privateKey != null) { + throw new SFException( + ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, + "Cannot have both private key object and private key string value."); + } else if (!Strings.isNullOrEmpty(privateKeyBase64) && !Strings.isNullOrEmpty(privateKeyFile)) { + throw new SFException( + ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, + "Cannot have both private key file and private key string value."); } else { - // if privateKeyFile has a value and privateKey is null - this.privateKey = - Strings.isNullOrEmpty(privateKeyFile) - ? privateKey - : extractPrivateKeyFromFile(privateKeyFile, privateKeyFilePwd); + if (!Strings.isNullOrEmpty(privateKeyBase64)) { + // privateKeyBase64 has a value and other options for passing private key are null + this.privateKey = extractPrivateKeyFromBase64(privateKeyBase64, privateKeyFilePwd); + } else { + // either extract from privateKeyFile or use the passed object + this.privateKey = + Strings.isNullOrEmpty(privateKeyFile) + ? privateKey + : extractPrivateKeyFromFile(privateKeyFile, privateKeyFilePwd); + } } // construct public key from raw bytes if (this.privateKey instanceof RSAPrivateCrtKey) { @@ -148,16 +162,30 @@ private SecretKeyFactory getSecretKeyFactory(String algorithm) throws NoSuchAlgo private PrivateKey extractPrivateKeyFromFile(String privateKeyFile, String privateKeyFilePwd) throws SFException { + + try { + Path privKeyPath = Paths.get(privateKeyFile); + FileUtil.logFileUsage(privKeyPath, "Extract private key from file", true); + byte[] bytes = Files.readAllBytes(privKeyPath); + return extractPrivateKeyFromBytes(bytes, privateKeyFilePwd); + } catch (IOException ie) { + logger.error("Could not read private key from file", ie); + throw new SFException(ie, ErrorCode.INVALID_PARAMETER_VALUE, ie.getCause()); + } + } + + private PrivateKey extractPrivateKeyFromBytes(byte[] privateKeyBytes, String privateKeyFilePwd) + throws SFException { if (isBouncyCastleProviderEnabled) { try { - return extractPrivateKeyWithBouncyCastle(privateKeyFile, privateKeyFilePwd); + return extractPrivateKeyWithBouncyCastle(privateKeyBytes, privateKeyFilePwd); } catch (IOException | PKCSException | OperatorCreationException e) { logger.error("Could not extract private key using Bouncy Castle provider", e); throw new SFException(e, ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, e.getCause()); } } else { try { - return extractPrivateKeyWithJdk(privateKeyFile, privateKeyFilePwd); + return extractPrivateKeyWithJdk(privateKeyBytes, privateKeyFilePwd); } catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException @@ -165,16 +193,21 @@ private PrivateKey extractPrivateKeyFromFile(String privateKeyFile, String priva | NullPointerException | InvalidKeyException e) { logger.error( - "Could not extract private key. Try setting the JVM argument: " + "-D{}" + "=TRUE", + "Could not extract private key using standard JDK. Try setting the JVM argument: " + + "-D{}" + + "=TRUE", SecurityUtil.ENABLE_BOUNCYCASTLE_PROVIDER_JVM); - throw new SFException( - e, - ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, - privateKeyFile + ": " + e.getMessage()); + throw new SFException(e, ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, e.getMessage()); } } } + private PrivateKey extractPrivateKeyFromBase64(String privateKeyBase64, String privateKeyFilePwd) + throws SFException { + byte[] decodedKey = Base64.decodeBase64(privateKeyBase64); + return extractPrivateKeyFromBytes(decodedKey, privateKeyFilePwd); + } + public String issueJwtToken() throws SFException { JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder(); String sub = String.format(SUBJECT_FMT, this.accountName, this.userName); @@ -232,13 +265,12 @@ public static int getTimeout() { } private PrivateKey extractPrivateKeyWithBouncyCastle( - String privateKeyFile, String privateKeyFilePwd) + byte[] privateKeyBytes, String privateKeyFilePwd) throws IOException, PKCSException, OperatorCreationException { - Path privKeyPath = Paths.get(privateKeyFile); - FileUtil.logFileUsage( - privKeyPath, "Extract private key from file using Bouncy Castle provider", true); + PrivateKeyInfo privateKeyInfo = null; - PEMParser pemParser = new PEMParser(new FileReader(privKeyPath.toFile())); + PEMParser pemParser = + new PEMParser(new StringReader(new String(privateKeyBytes, StandardCharsets.UTF_8))); Object pemObject = pemParser.readObject(); if (pemObject instanceof PKCS8EncryptedPrivateKeyInfo) { // Handle the case where the private key is encrypted. @@ -264,11 +296,9 @@ private PrivateKey extractPrivateKeyWithBouncyCastle( return converter.getPrivateKey(privateKeyInfo); } - private PrivateKey extractPrivateKeyWithJdk(String privateKeyFile, String privateKeyFilePwd) + private PrivateKey extractPrivateKeyWithJdk(byte[] privateKeyFileBytes, String privateKeyFilePwd) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { - Path privKeyPath = Paths.get(privateKeyFile); - FileUtil.logFileUsage(privKeyPath, "Extract private key from file using Jdk", true); - String privateKeyContent = new String(Files.readAllBytes(privKeyPath)); + String privateKeyContent = new String(privateKeyFileBytes, StandardCharsets.UTF_8); if (Strings.isNullOrEmpty(privateKeyFilePwd)) { // unencrypted private key file return generatePrivateKey(false, privateKeyContent, privateKeyFilePwd); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java index a88e48c61..e29ba644e 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java @@ -220,14 +220,22 @@ public String getUrl() { public void setPrivateKey(PrivateKey privateKey) { this.setAuthenticator(AUTHENTICATOR_SNOWFLAKE_JWT); - this.properties.put(SFSessionProperty.PRIVATE_KEY.getPropertyKey(), privateKey); + this.properties.put(SFSessionProperty.PRIVATE_KEY, privateKey); } public void setPrivateKeyFile(String location, String password) { this.setAuthenticator(AUTHENTICATOR_SNOWFLAKE_JWT); - this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE.getPropertyKey(), location); + this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE, location); if (!Strings.isNullOrEmpty(password)) { - this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey(), password); + this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE_PWD, password); + } + } + + public void setPrivateKeyBase64(String privateKeyBase64, String password) { + this.setAuthenticator(AUTHENTICATOR_SNOWFLAKE_JWT); + this.properties.put(SFSessionProperty.PRIVATE_KEY_BASE64, privateKeyBase64); + if (!Strings.isNullOrEmpty(password)) { + this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE_PWD, password); } } diff --git a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java index f936ee616..01dd26620 100644 --- a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java +++ b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java @@ -16,6 +16,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -81,7 +84,7 @@ public void testJwtAuthTimeoutRetry() throws SFException, SnowflakeSQLException * * @return a mock object for SFLoginInput */ - private SFLoginInput initMockLoginInput() { + private SFLoginInput initMockLoginInput() throws SFException { // mock SFLoginInput SFLoginInput loginInput = mock(SFLoginInput.class); when(loginInput.getServerUrl()).thenReturn(systemGetEnv("SNOWFLAKE_TEST_HOST")); @@ -89,6 +92,17 @@ private SFLoginInput initMockLoginInput() { .thenReturn(ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT.name()); when(loginInput.getPrivateKeyFile()) .thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE")); + try { + when(loginInput.getPrivateKeyBase64()) + .thenReturn( + Base64.getEncoder() + .encodeToString( + Files.readAllBytes( + Paths.get(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE"))))); + } catch (IOException e) { + throw new SFException( + e, ErrorCode.INVALID_PARAMETER_VALUE, systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE")); + } when(loginInput.getPrivateKeyFilePwd()) .thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE_PWD")); when(loginInput.getUserName()).thenReturn(systemGetEnv("SNOWFLAKE_TEST_USER")); diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java index 0e7083e7e..b4578dc2c 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java @@ -41,6 +41,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.Collections; import java.util.Enumeration; import java.util.List; @@ -774,6 +775,70 @@ public void testKeyPairFileDataSourceSerialization() throws Exception { } } + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testKeyPairBase64DataSourceSerialization() throws Exception { + // test with key/pair authentication where key is passed as a Base64 string value + // set up DataSource object and ensure connection works + Map params = getConnectionParameters(); + SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); + ds.setServerName(params.get("host")); + ds.setSsl("on".equals(params.get("ssl"))); + ds.setAccount(params.get("account")); + ds.setPortNumber(Integer.parseInt(params.get("port"))); + ds.setUser(params.get("user")); + String privateKeyBase64 = + Base64.getEncoder() + .encodeToString( + Files.readAllBytes(Paths.get(getFullPathFileInResource("encrypted_rsa_key.p8")))); + ds.setPrivateKeyBase64(privateKeyBase64, "test"); + + // set up public key + try (Connection con = getConnection(); + Statement statement = con.createStatement()) { + statement.execute("use role accountadmin"); + String pathfile = getFullPathFileInResource("encrypted_rsa_key.pub"); + String pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); + pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); + pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); + statement.execute( + String.format("alter user %s set rsa_public_key='%s'", params.get("user"), pubKey)); + } + + try (Connection con = ds.getConnection(); + Statement statement = con.createStatement(); + ResultSet resultSet = statement.executeQuery("select 1")) { + assertTrue(resultSet.next()); + assertThat("select 1", resultSet.getInt(1), equalTo(1)); + } + File serializedFile = tmpFolder.newFile("serializedStuff.ser"); + // serialize datasource object into a file + try (FileOutputStream outputFile = new FileOutputStream(serializedFile); + ObjectOutputStream out = new ObjectOutputStream(outputFile)) { + out.writeObject(ds); + } + // deserialize into datasource object again + try (FileInputStream inputFile = new FileInputStream(serializedFile); + ObjectInputStream in = new ObjectInputStream(inputFile)) { + SnowflakeBasicDataSource ds2 = (SnowflakeBasicDataSource) in.readObject(); + // test connection a second time + try (Connection con = ds2.getConnection(); + Statement statement = con.createStatement()) { + ResultSet resultSet = statement.executeQuery("select 1"); + assertTrue(resultSet.next()); + assertThat("select 1", resultSet.getInt(1), equalTo(1)); + } + + } finally { + // clean up + try (Connection connection = getConnection()) { + Statement statement = connection.createStatement(); + statement.execute("use role accountadmin"); + statement.execute(String.format("alter user %s unset rsa_public_key", params.get("user"))); + } + } + } + @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testPrivateKeyInConnectionString() throws SQLException, IOException { @@ -892,6 +957,140 @@ public void testPrivateKeyInConnectionStringWithBouncyCastle() throws SQLExcepti testPrivateKeyInConnectionString(); } + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testPrivateKeyBase64InConnectionString() throws SQLException, IOException { + Map parameters = getConnectionParameters(); + String testUser = parameters.get("user"); + String pathfile = null; + String pubKey = null; + // Test with non-password-protected private key file (.pem) + try (Connection connection = getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("use role accountadmin"); + pathfile = getFullPathFileInResource("rsa_key.pub"); + pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); + pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); + pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); + statement.execute(String.format("alter user %s set rsa_public_key='%s'", testUser, pubKey)); + } + + // PKCS #8 + String privateKeyBase64 = + Base64.getEncoder() + .encodeToString(Files.readAllBytes(Paths.get(getFullPathFileInResource("rsa_key.p8")))); + String uri = parameters.get("uri") + "/?private_key_base64=" + privateKeyBase64; + Properties properties = new Properties(); + properties.put("account", parameters.get("account")); + properties.put("user", testUser); + properties.put("ssl", parameters.get("ssl")); + properties.put("port", parameters.get("port")); + try (Connection connection = DriverManager.getConnection(uri, properties)) {} + + // PKCS #1 + privateKeyBase64 = + Base64.getEncoder() + .encodeToString( + Files.readAllBytes(Paths.get(getFullPathFileInResource("rsa_key.pem")))); + uri = parameters.get("uri") + "/?private_key_base64=" + privateKeyBase64; + properties = new Properties(); + properties.put("account", parameters.get("account")); + properties.put("user", testUser); + properties.put("ssl", parameters.get("ssl")); + properties.put("port", parameters.get("port")); + properties.put("authenticator", ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT.toString()); + try (Connection connection = DriverManager.getConnection(uri, properties)) {} + + // test with password-protected private key file (.p8) + try (Connection connection = getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("use role accountadmin"); + pathfile = getFullPathFileInResource("encrypted_rsa_key.pub"); + pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); + pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); + pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); + statement.execute(String.format("alter user %s set rsa_public_key='%s'", testUser, pubKey)); + } + + privateKeyBase64 = + Base64.getEncoder() + .encodeToString( + Files.readAllBytes(Paths.get(getFullPathFileInResource("encrypted_rsa_key.p8")))); + uri = + parameters.get("uri") + + "/?private_key_file_pwd=test&private_key_base64=" + + privateKeyBase64; + + try (Connection connection = DriverManager.getConnection(uri, properties)) {} + // test with incorrect password for private key + uri = + parameters.get("uri") + + "/?private_key_file_pwd=wrong_password&private_key_base64=" + + privateKeyBase64; + + try (Connection connection = DriverManager.getConnection(uri, properties)) { + fail(); + } catch (SQLException e) { + assertEquals( + (int) ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY.getMessageCode(), e.getErrorCode()); + } + + // test with invalid public/private key combo (using 1st public key with 2nd private key) + try (Connection connection = getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("use role accountadmin"); + pathfile = getFullPathFileInResource("rsa_key.pub"); + pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); + pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); + pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); + statement.execute(String.format("alter user %s set rsa_public_key='%s'", testUser, pubKey)); + } + + privateKeyBase64 = + Base64.getEncoder() + .encodeToString( + Files.readAllBytes(Paths.get(getFullPathFileInResource("encrypted_rsa_key.p8")))); + uri = + parameters.get("uri") + + "/?private_key_file_pwd=test&private_key_base64=" + + privateKeyBase64; + try (Connection connection = DriverManager.getConnection(uri, properties)) { + fail(); + } catch (SQLException e) { + assertEquals(390144, e.getErrorCode()); + } + + // test with invalid private key + privateKeyBase64 = + Base64.getEncoder() + .encodeToString( + Files.readAllBytes( + Paths.get(getFullPathFileInResource("invalid_private_key.pem")))); + uri = parameters.get("uri") + "/?private_key_base64=" + privateKeyBase64; + try (Connection connection = DriverManager.getConnection(uri, properties)) { + fail(); + } catch (SQLException e) { + assertEquals( + (int) ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY.getMessageCode(), e.getErrorCode()); + } + + // clean up + try (Connection connection = getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("use role accountadmin"); + statement.execute(String.format("alter user %s unset rsa_public_key", testUser)); + } + } + + // This will only work with JDBC driver versions higher than 3.15.1 + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testPrivateKeyBase64InConnectionStringWithBouncyCastle() + throws SQLException, IOException { + System.setProperty(SecurityUtil.ENABLE_BOUNCYCASTLE_PROVIDER_JVM, "true"); + testPrivateKeyBase64InConnectionString(); + } + @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testBasicDataSourceSerialization() throws Exception { diff --git a/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryIT.java b/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryIT.java index 3efa9d168..e100534e7 100644 --- a/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryIT.java +++ b/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryIT.java @@ -213,7 +213,12 @@ private TelemetryClient createSessionlessTelemetry() Map parameters = getConnectionParameters(); String jwtToken = SessionUtil.generateJWTToken( - null, privateKeyLocation, null, parameters.get("account"), parameters.get("user")); + null, + privateKeyLocation, + null, + null, + parameters.get("account"), + parameters.get("user")); CloseableHttpClient httpClient = HttpUtil.buildHttpClient(null, null, false); TelemetryClient telemetry = @@ -232,7 +237,12 @@ private TelemetryClient createJWTSessionlessTelemetry() Map parameters = getConnectionParameters(); String jwtToken = SessionUtil.generateJWTToken( - null, privateKeyLocation, null, parameters.get("account"), parameters.get("user")); + null, + privateKeyLocation, + null, + null, + parameters.get("account"), + parameters.get("user")); CloseableHttpClient httpClient = HttpUtil.buildHttpClient(null, null, false); TelemetryClient telemetry = From 9edc7335fa60ed98e4e73cbe6ddb9a131e0739b1 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz Date: Fri, 26 Jul 2024 13:41:45 +0200 Subject: [PATCH 2/7] SNOW-618478: Fix code review issues --- .../snowflake/client/core/SFLoginInput.java | 8 +- .../net/snowflake/client/core/SFSession.java | 4 +- .../snowflake/client/core/SessionUtil.java | 11 +- .../client/core/SessionUtilKeyPair.java | 68 +- .../client/jdbc/SnowflakeBasicDataSource.java | 10 +- .../client/core/SessionUtilLatestIT.java | 16 +- .../client/jdbc/ConnectionLatestIT.java | 626 +++++++----------- .../jdbc/SnowflakeBasicDataSourceTest.java | 93 +++ 8 files changed, 381 insertions(+), 455 deletions(-) diff --git a/src/main/java/net/snowflake/client/core/SFLoginInput.java b/src/main/java/net/snowflake/client/core/SFLoginInput.java index d096c24ec..d42ed6d85 100644 --- a/src/main/java/net/snowflake/client/core/SFLoginInput.java +++ b/src/main/java/net/snowflake/client/core/SFLoginInput.java @@ -315,6 +315,10 @@ SFLoginInput setPrivateKey(PrivateKey privateKey) { return this; } + String getPrivateKeyBase64() { + return privateKeyBase64; + } + SFLoginInput setPrivateKeyBase64(String privateKeyBase64) { this.privateKeyBase64 = privateKeyBase64; return this; @@ -334,10 +338,6 @@ String getPrivateKeyFile() { return privateKeyFile; } - String getPrivateKeyBase64() { - return privateKeyBase64; - } - String getPrivateKeyFilePwd() { return privateKeyFilePwd; } diff --git a/src/main/java/net/snowflake/client/core/SFSession.java b/src/main/java/net/snowflake/client/core/SFSession.java index 8c93dad2c..5b51e352e 100644 --- a/src/main/java/net/snowflake/client/core/SFSession.java +++ b/src/main/java/net/snowflake/client/core/SFSession.java @@ -547,7 +547,7 @@ public synchronized void open() throws SFException, SnowflakeSQLException { + " warehouse: {}, validate default parameters: {}, authenticator: {}, ocsp mode: {}," + " passcode in password: {}, passcode is {}, private key is {}, disable socks proxy: {}," + " application: {}, app id: {}, app version: {}, login timeout: {}, retry timeout: {}, network timeout: {}," - + " query timeout: {}, tracing: {}, private key file: {}, private key file pwd is {}," + + " query timeout: {}, tracing: {}, private key file: {}, private key base64 is {}, private key file pwd is {}," + " enable_diagnostics: {}, diagnostics_allowlist_path: {}," + " session parameters: client store temporary credential: {}, gzip disabled: {}", connectionPropertiesMap.get(SFSessionProperty.SERVER_URL), @@ -576,6 +576,8 @@ public synchronized void open() throws SFException, SnowflakeSQLException { connectionPropertiesMap.get(SFSessionProperty.QUERY_TIMEOUT), connectionPropertiesMap.get(SFSessionProperty.TRACING), connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE), + SFLoggerUtil.isVariableProvided( + (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_BASE64)), SFLoggerUtil.isVariableProvided( (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD)), connectionPropertiesMap.get(SFSessionProperty.ENABLE_DIAGNOSTICS), diff --git a/src/main/java/net/snowflake/client/core/SessionUtil.java b/src/main/java/net/snowflake/client/core/SessionUtil.java index 430c05b75..303c231dc 100644 --- a/src/main/java/net/snowflake/client/core/SessionUtil.java +++ b/src/main/java/net/snowflake/client/core/SessionUtil.java @@ -1747,7 +1747,8 @@ public static String generateJWTToken( } /** - * Helper function to generate a JWT token + * Helper function to generate a JWT token. Use {@link #generateJWTToken(PrivateKey, String, + * String, String, String, String)} * * @param privateKey private key * @param privateKeyFile path to private key file @@ -1757,7 +1758,7 @@ public static String generateJWTToken( * @return JWT token * @throws SFException if Snowflake error occurs */ - @Deprecated() + @Deprecated public static String generateJWTToken( PrivateKey privateKey, String privateKeyFile, @@ -1765,10 +1766,8 @@ public static String generateJWTToken( String accountName, String userName) throws SFException { - SessionUtilKeyPair s = - new SessionUtilKeyPair( - privateKey, privateKeyFile, null, privateKeyFilePwd, accountName, userName); - return s.issueJwtToken(); + return generateJWTToken( + privateKey, privateKeyFile, null, privateKeyFilePwd, accountName, userName); } /** diff --git a/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java b/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java index 77d8ee492..ae9e1c5b7 100644 --- a/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java +++ b/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java @@ -101,31 +101,10 @@ class SessionUtilKeyPair { } } - // Ensure that we only received one of: privateKey, privateKeyFile, or privateKeyBase64 - if (!Strings.isNullOrEmpty(privateKeyFile) && privateKey != null) { - throw new SFException( - ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, - "Cannot have both private key object and private key file."); - } else if (!Strings.isNullOrEmpty(privateKeyBase64) && privateKey != null) { - throw new SFException( - ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, - "Cannot have both private key object and private key string value."); - } else if (!Strings.isNullOrEmpty(privateKeyBase64) && !Strings.isNullOrEmpty(privateKeyFile)) { - throw new SFException( - ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, - "Cannot have both private key file and private key string value."); - } else { - if (!Strings.isNullOrEmpty(privateKeyBase64)) { - // privateKeyBase64 has a value and other options for passing private key are null - this.privateKey = extractPrivateKeyFromBase64(privateKeyBase64, privateKeyFilePwd); - } else { - // either extract from privateKeyFile or use the passed object - this.privateKey = - Strings.isNullOrEmpty(privateKeyFile) - ? privateKey - : extractPrivateKeyFromFile(privateKeyFile, privateKeyFilePwd); - } - } + ensurePrivateKeyProvidedInOnlyOneProperty(privateKey, privateKeyFile, privateKeyBase64); + this.privateKey = + buildPrivateKey(privateKey, privateKeyFile, privateKeyBase64, privateKeyFilePwd); + // construct public key from raw bytes if (this.privateKey instanceof RSAPrivateCrtKey) { RSAPrivateCrtKey rsaPrivateCrtKey = (RSAPrivateCrtKey) this.privateKey; @@ -144,6 +123,42 @@ class SessionUtilKeyPair { } } + private static void ensurePrivateKeyProvidedInOnlyOneProperty( + PrivateKey privateKey, String privateKeyFile, String privateKeyBase64) throws SFException { + if (!Strings.isNullOrEmpty(privateKeyFile) && privateKey != null) { + throw new SFException( + ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, + "Cannot have both private key object and private key file."); + } + if (!Strings.isNullOrEmpty(privateKeyBase64) && !Strings.isNullOrEmpty(privateKeyFile)) { + throw new SFException( + ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, + "Cannot have both private key file and private key base64 string value."); + } + if (!Strings.isNullOrEmpty(privateKeyBase64) && privateKey != null) { + throw new SFException( + ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, + "Cannot have both private key object and private key base64 string value."); + } + } + + private PrivateKey buildPrivateKey( + PrivateKey privateKey, + String privateKeyFile, + String privateKeyBase64, + String privateKeyFilePwd) + throws SFException { + if (!Strings.isNullOrEmpty(privateKeyBase64)) { + logger.trace("Reading private key from base64 string"); + return extractPrivateKeyFromBase64(privateKeyBase64, privateKeyFilePwd); + } + if (!Strings.isNullOrEmpty(privateKeyFile)) { + logger.trace("Reading private key from file"); + return extractPrivateKeyFromFile(privateKeyFile, privateKeyFilePwd); + } + return privateKey; + } + private KeyFactory getKeyFactoryInstance() throws NoSuchAlgorithmException { if (isFipsMode) { return KeyFactory.getInstance("RSA", this.SecurityProvider); @@ -267,7 +282,7 @@ public static int getTimeout() { private PrivateKey extractPrivateKeyWithBouncyCastle( byte[] privateKeyBytes, String privateKeyFilePwd) throws IOException, PKCSException, OperatorCreationException { - + logger.trace("Extracting private key using Bouncy Castle provider"); PrivateKeyInfo privateKeyInfo = null; PEMParser pemParser = new PEMParser(new StringReader(new String(privateKeyBytes, StandardCharsets.UTF_8))); @@ -298,6 +313,7 @@ private PrivateKey extractPrivateKeyWithBouncyCastle( private PrivateKey extractPrivateKeyWithJdk(byte[] privateKeyFileBytes, String privateKeyFilePwd) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { + logger.trace("Extracting private key using JDK"); String privateKeyContent = new String(privateKeyFileBytes, StandardCharsets.UTF_8); if (Strings.isNullOrEmpty(privateKeyFilePwd)) { // unencrypted private key file diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java index e29ba644e..7ff720902 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java @@ -220,22 +220,22 @@ public String getUrl() { public void setPrivateKey(PrivateKey privateKey) { this.setAuthenticator(AUTHENTICATOR_SNOWFLAKE_JWT); - this.properties.put(SFSessionProperty.PRIVATE_KEY, privateKey); + this.properties.put(SFSessionProperty.PRIVATE_KEY.getPropertyKey(), privateKey); } public void setPrivateKeyFile(String location, String password) { this.setAuthenticator(AUTHENTICATOR_SNOWFLAKE_JWT); - this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE, location); + this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE.getPropertyKey(), location); if (!Strings.isNullOrEmpty(password)) { - this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE_PWD, password); + this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey(), password); } } public void setPrivateKeyBase64(String privateKeyBase64, String password) { this.setAuthenticator(AUTHENTICATOR_SNOWFLAKE_JWT); - this.properties.put(SFSessionProperty.PRIVATE_KEY_BASE64, privateKeyBase64); + this.properties.put(SFSessionProperty.PRIVATE_KEY_BASE64.getPropertyKey(), privateKeyBase64); if (!Strings.isNullOrEmpty(password)) { - this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE_PWD, password); + this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey(), password); } } diff --git a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java index 01dd26620..f936ee616 100644 --- a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java +++ b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java @@ -16,9 +16,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -84,7 +81,7 @@ public void testJwtAuthTimeoutRetry() throws SFException, SnowflakeSQLException * * @return a mock object for SFLoginInput */ - private SFLoginInput initMockLoginInput() throws SFException { + private SFLoginInput initMockLoginInput() { // mock SFLoginInput SFLoginInput loginInput = mock(SFLoginInput.class); when(loginInput.getServerUrl()).thenReturn(systemGetEnv("SNOWFLAKE_TEST_HOST")); @@ -92,17 +89,6 @@ private SFLoginInput initMockLoginInput() throws SFException { .thenReturn(ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT.name()); when(loginInput.getPrivateKeyFile()) .thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE")); - try { - when(loginInput.getPrivateKeyBase64()) - .thenReturn( - Base64.getEncoder() - .encodeToString( - Files.readAllBytes( - Paths.get(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE"))))); - } catch (IOException e) { - throw new SFException( - e, ErrorCode.INVALID_PARAMETER_VALUE, systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE")); - } when(loginInput.getPrivateKeyFilePwd()) .thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE_PWD")); when(loginInput.getUserName()).thenReturn(systemGetEnv("SNOWFLAKE_TEST_USER")); diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java index b4578dc2c..4e696a871 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java @@ -721,230 +721,186 @@ public void testKeyPairFileDataSourceSerialization() throws Exception { // test with key/pair authentication where key is in file // set up DataSource object and ensure connection works Map params = getConnectionParameters(); - SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); - ds.setServerName(params.get("host")); - ds.setSsl("on".equals(params.get("ssl"))); - ds.setAccount(params.get("account")); - ds.setPortNumber(Integer.parseInt(params.get("port"))); - ds.setUser(params.get("user")); - String privateKeyLocation = getFullPathFileInResource("encrypted_rsa_key.p8"); - ds.setPrivateKeyFile(privateKeyLocation, "test"); + try { + SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); + ds.setServerName(params.get("host")); + ds.setSsl("on".equals(params.get("ssl"))); + ds.setAccount(params.get("account")); + ds.setPortNumber(Integer.parseInt(params.get("port"))); + ds.setUser(params.get("user")); + String privateKeyLocation = getFullPathFileInResource("encrypted_rsa_key.p8"); + ds.setPrivateKeyFile(privateKeyLocation, "test"); - // set up public key - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { - statement.execute("use role accountadmin"); - String pathfile = getFullPathFileInResource("encrypted_rsa_key.pub"); - String pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); - pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); - pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); - statement.execute( - String.format("alter user %s set rsa_public_key='%s'", params.get("user"), pubKey)); - } + // set up public key + setUpPublicKey("encrypted_rsa_key.pub", params.get("user")); - try (Connection con = ds.getConnection(); - Statement statement = con.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1")) { - resultSet.next(); - assertThat("select 1", resultSet.getInt(1), equalTo(1)); - } - File serializedFile = tmpFolder.newFile("serializedStuff.ser"); - // serialize datasource object into a file - try (FileOutputStream outputFile = new FileOutputStream(serializedFile); - ObjectOutputStream out = new ObjectOutputStream(outputFile)) { - out.writeObject(ds); - } - // deserialize into datasource object again - try (FileInputStream inputFile = new FileInputStream(serializedFile); - ObjectInputStream in = new ObjectInputStream(inputFile)) { - SnowflakeBasicDataSource ds2 = (SnowflakeBasicDataSource) in.readObject(); - // test connection a second time - try (Connection con = ds2.getConnection(); - Statement statement = con.createStatement()) { - ResultSet resultSet = statement.executeQuery("select 1"); - resultSet.next(); - assertThat("select 1", resultSet.getInt(1), equalTo(1)); - } + connectAndExecuteSelect1(ds); - // clean up - try (Connection connection = getConnection()) { - Statement statement = connection.createStatement(); - statement.execute("use role accountadmin"); - statement.execute(String.format("alter user %s unset rsa_public_key", params.get("user"))); + File serializedFile = tmpFolder.newFile("serializedStuff.ser"); + // serialize datasource object into a file + try (FileOutputStream outputFile = new FileOutputStream(serializedFile); + ObjectOutputStream out = new ObjectOutputStream(outputFile)) { + out.writeObject(ds); } + // deserialize into datasource object again + try (FileInputStream inputFile = new FileInputStream(serializedFile); + ObjectInputStream in = new ObjectInputStream(inputFile)) { + SnowflakeBasicDataSource ds2 = (SnowflakeBasicDataSource) in.readObject(); + // test connection a second time + connectAndExecuteSelect1(ds2); + } + } finally { + unsetPublicKey(params.get("user")); } } + private static String readPrivateKeyFileToBase64Content(String fileName) throws IOException { + return Base64.getEncoder() + .encodeToString(Files.readAllBytes(Paths.get(getFullPathFileInResource(fileName)))); + } + + /** Works in > 3.18.0 */ @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testKeyPairBase64DataSourceSerialization() throws Exception { // test with key/pair authentication where key is passed as a Base64 string value // set up DataSource object and ensure connection works Map params = getConnectionParameters(); - SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); - ds.setServerName(params.get("host")); - ds.setSsl("on".equals(params.get("ssl"))); - ds.setAccount(params.get("account")); - ds.setPortNumber(Integer.parseInt(params.get("port"))); - ds.setUser(params.get("user")); - String privateKeyBase64 = - Base64.getEncoder() - .encodeToString( - Files.readAllBytes(Paths.get(getFullPathFileInResource("encrypted_rsa_key.p8")))); - ds.setPrivateKeyBase64(privateKeyBase64, "test"); + try { + SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); + ds.setServerName(params.get("host")); + ds.setSsl("on".equals(params.get("ssl"))); + ds.setAccount(params.get("account")); + ds.setPortNumber(Integer.parseInt(params.get("port"))); + ds.setUser(params.get("user")); + String privateKeyBase64 = readPrivateKeyFileToBase64Content("encrypted_rsa_key.p8"); + ds.setPrivateKeyBase64(privateKeyBase64, "test"); - // set up public key - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { - statement.execute("use role accountadmin"); - String pathfile = getFullPathFileInResource("encrypted_rsa_key.pub"); - String pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); - pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); - pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); - statement.execute( - String.format("alter user %s set rsa_public_key='%s'", params.get("user"), pubKey)); - } + // set up public key + setUpPublicKey("encrypted_rsa_key.pub", params.get("user")); - try (Connection con = ds.getConnection(); - Statement statement = con.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1")) { - assertTrue(resultSet.next()); - assertThat("select 1", resultSet.getInt(1), equalTo(1)); - } - File serializedFile = tmpFolder.newFile("serializedStuff.ser"); - // serialize datasource object into a file - try (FileOutputStream outputFile = new FileOutputStream(serializedFile); - ObjectOutputStream out = new ObjectOutputStream(outputFile)) { - out.writeObject(ds); - } - // deserialize into datasource object again - try (FileInputStream inputFile = new FileInputStream(serializedFile); - ObjectInputStream in = new ObjectInputStream(inputFile)) { - SnowflakeBasicDataSource ds2 = (SnowflakeBasicDataSource) in.readObject(); - // test connection a second time - try (Connection con = ds2.getConnection(); - Statement statement = con.createStatement()) { - ResultSet resultSet = statement.executeQuery("select 1"); - assertTrue(resultSet.next()); - assertThat("select 1", resultSet.getInt(1), equalTo(1)); - } + connectAndExecuteSelect1(ds); - } finally { - // clean up - try (Connection connection = getConnection()) { - Statement statement = connection.createStatement(); - statement.execute("use role accountadmin"); - statement.execute(String.format("alter user %s unset rsa_public_key", params.get("user"))); + File serializedFile = tmpFolder.newFile("serializedStuff.ser"); + // serialize datasource object into a file + try (FileOutputStream outputFile = new FileOutputStream(serializedFile); + ObjectOutputStream out = new ObjectOutputStream(outputFile)) { + out.writeObject(ds); + } + // deserialize into datasource object again + try (FileInputStream inputFile = new FileInputStream(serializedFile); + ObjectInputStream in = new ObjectInputStream(inputFile)) { + SnowflakeBasicDataSource ds2 = (SnowflakeBasicDataSource) in.readObject(); + // test connection a second time + connectAndExecuteSelect1(ds2); } + } finally { + unsetPublicKey(params.get("user")); } } + /** + * This test may be split but let's keep it with multiple keys checks to not slow down test * + * executions + */ @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testPrivateKeyInConnectionString() throws SQLException, IOException { Map parameters = getConnectionParameters(); String testUser = parameters.get("user"); - String pathfile = null; - String pubKey = null; - // Test with non-password-protected private key file (.pem) - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - statement.execute("use role accountadmin"); - pathfile = getFullPathFileInResource("rsa_key.pub"); - pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); - pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); - pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); - statement.execute(String.format("alter user %s set rsa_public_key='%s'", testUser, pubKey)); - } + String baseUri = parameters.get("uri"); + try { + // Test with non-password-protected private key file (.pem) + setUpPublicKey("rsa_key.pub", testUser); - // PKCS #8 - String privateKeyLocation = getFullPathFileInResource("rsa_key.p8"); - String uri = parameters.get("uri") + "/?private_key_file=" + privateKeyLocation; - Properties properties = new Properties(); - properties.put("account", parameters.get("account")); - properties.put("user", testUser); - properties.put("ssl", parameters.get("ssl")); - properties.put("port", parameters.get("port")); - try (Connection connection = DriverManager.getConnection(uri, properties)) {} + Properties properties = preparePropertiesForPrivateKeyTests(parameters); - // PKCS #1 - privateKeyLocation = getFullPathFileInResource("rsa_key.pem"); - uri = parameters.get("uri") + "/?private_key_file=" + privateKeyLocation; - properties = new Properties(); - properties.put("account", parameters.get("account")); - properties.put("user", testUser); - properties.put("ssl", parameters.get("ssl")); - properties.put("port", parameters.get("port")); - properties.put("authenticator", ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT.toString()); - try (Connection connection = DriverManager.getConnection(uri, properties)) {} + // PKCS #8 + String privateKeyLocation = getFullPathFileInResource("rsa_key.p8"); + String uri = uriWithPrivateKeyFile(baseUri, privateKeyLocation); + connectSuccessfully(uri, properties); - // test with password-protected private key file (.p8) - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - statement.execute("use role accountadmin"); - pathfile = getFullPathFileInResource("encrypted_rsa_key.pub"); - pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); - pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); - pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); - statement.execute(String.format("alter user %s set rsa_public_key='%s'", testUser, pubKey)); + // PKCS #1 + privateKeyLocation = getFullPathFileInResource("rsa_key.pem"); + uri = uriWithPrivateKeyFile(baseUri, privateKeyLocation); + connectSuccessfully(uri, properties); + + // test with password-protected private key file (.p8) + setUpPublicKey("encrypted_rsa_key.pub", testUser); + + privateKeyLocation = getFullPathFileInResource("encrypted_rsa_key.p8"); + uri = uriWithPrivateKeyFileAndPassword(baseUri, privateKeyLocation, "test"); + + connectSuccessfully(uri, properties); + // test with incorrect password for private key + uri = uriWithPrivateKeyFileAndPassword(baseUri, privateKeyLocation, "wrong_password"); + connectExpectingInvalidOrUnsupportedPrivateKey(uri, properties); + + // test with invalid public/private key combo (using 1st public key with 2nd private key) + setUpPublicKey("rsa_key.pub", testUser); + + privateKeyLocation = getFullPathFileInResource("encrypted_rsa_key.p8"); + uri = uriWithPrivateKeyFileAndPassword(baseUri, privateKeyLocation, "test"); + connectExpectingError390144(uri, properties); + + // test with invalid private key + privateKeyLocation = getFullPathFileInResource("invalid_private_key.pem"); + uri = uriWithPrivateKeyFile(baseUri, privateKeyLocation); + connectExpectingInvalidOrUnsupportedPrivateKey(uri, properties); + + } finally { + unsetPublicKey(testUser); } + } - privateKeyLocation = getFullPathFileInResource("encrypted_rsa_key.p8"); - uri = - parameters.get("uri") - + "/?private_key_file_pwd=test&private_key_file=" - + privateKeyLocation; + private static String uriWithPrivateKeyFile(String baseUri, String privateKeyLocation) { + return baseUri + + "/?" + + SFSessionProperty.PRIVATE_KEY_FILE.getPropertyKey() + + "=" + + privateKeyLocation; + } - try (Connection connection = DriverManager.getConnection(uri, properties)) {} - // test with incorrect password for private key - uri = - parameters.get("uri") - + "/?private_key_file_pwd=wrong_password&private_key_file=" - + privateKeyLocation; + private static String uriWithPrivateKeyFileAndPassword( + String baseUri, String privateKeyLocation, String password) { + return uriWithPrivateKeyFile(baseUri, privateKeyLocation) + + "&" + + SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey() + + "=" + + password; + } - try (Connection connection = DriverManager.getConnection(uri, properties)) { + private static void connectExpectingError390144(String fullUri, Properties properties) { + try (Connection connection = DriverManager.getConnection(fullUri, properties)) { fail(); } catch (SQLException e) { - assertEquals( - (int) ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY.getMessageCode(), e.getErrorCode()); + assertEquals(390144, e.getErrorCode()); } + } - // test with invalid public/private key combo (using 1st public key with 2nd private key) - try (Connection connection = getConnection(); + private static void connectSuccessfully(String uri, Properties properties) throws SQLException { + try (Connection connection = DriverManager.getConnection(uri, properties)) {} + } + + private static void setUpPublicKey(String fileName, String testUser) + throws SQLException, IOException { + Properties properties = new Properties(); + properties.put("role", "accountadmin"); + try (Connection connection = getConnection(properties); Statement statement = connection.createStatement()) { - statement.execute("use role accountadmin"); - pathfile = getFullPathFileInResource("rsa_key.pub"); - pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); + String pathfile = getFullPathFileInResource(fileName); + String pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); statement.execute(String.format("alter user %s set rsa_public_key='%s'", testUser, pubKey)); } + } - privateKeyLocation = getFullPathFileInResource("encrypted_rsa_key.p8"); - uri = - parameters.get("uri") - + "/?private_key_file_pwd=test&private_key_file=" - + privateKeyLocation; - try (Connection connection = DriverManager.getConnection(uri, properties)) { - fail(); - } catch (SQLException e) { - assertEquals(390144, e.getErrorCode()); - } - - // test with invalid private key - privateKeyLocation = getFullPathFileInResource("invalid_private_key.pem"); - uri = parameters.get("uri") + "/?private_key_file=" + privateKeyLocation; - try (Connection connection = DriverManager.getConnection(uri, properties)) { - fail(); - } catch (SQLException e) { - assertEquals( - (int) ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY.getMessageCode(), e.getErrorCode()); - } - - // clean up - try (Connection connection = getConnection(); + private static void unsetPublicKey(String testUser) throws SQLException { + Properties props = new Properties(); + props.put("role", "accountadmin"); + try (Connection connection = getConnection(props); Statement statement = connection.createStatement()) { - statement.execute("use role accountadmin"); statement.execute(String.format("alter user %s unset rsa_public_key", testUser)); } } @@ -957,132 +913,100 @@ public void testPrivateKeyInConnectionStringWithBouncyCastle() throws SQLExcepti testPrivateKeyInConnectionString(); } + /** + * Works in > 3.18.0 + * + *

This test may be split but let's keep it with multiple keys checks to not slow down test + * executions + */ @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testPrivateKeyBase64InConnectionString() throws SQLException, IOException { Map parameters = getConnectionParameters(); String testUser = parameters.get("user"); - String pathfile = null; - String pubKey = null; - // Test with non-password-protected private key file (.pem) - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - statement.execute("use role accountadmin"); - pathfile = getFullPathFileInResource("rsa_key.pub"); - pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); - pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); - pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); - statement.execute(String.format("alter user %s set rsa_public_key='%s'", testUser, pubKey)); - } + String baseUri = parameters.get("uri"); + try { + // Test with non-password-protected private key file (.pem) + setUpPublicKey("rsa_key.pub", testUser); - // PKCS #8 - String privateKeyBase64 = - Base64.getEncoder() - .encodeToString(Files.readAllBytes(Paths.get(getFullPathFileInResource("rsa_key.p8")))); - String uri = parameters.get("uri") + "/?private_key_base64=" + privateKeyBase64; - Properties properties = new Properties(); - properties.put("account", parameters.get("account")); - properties.put("user", testUser); - properties.put("ssl", parameters.get("ssl")); - properties.put("port", parameters.get("port")); - try (Connection connection = DriverManager.getConnection(uri, properties)) {} + Properties properties = preparePropertiesForPrivateKeyTests(parameters); - // PKCS #1 - privateKeyBase64 = - Base64.getEncoder() - .encodeToString( - Files.readAllBytes(Paths.get(getFullPathFileInResource("rsa_key.pem")))); - uri = parameters.get("uri") + "/?private_key_base64=" + privateKeyBase64; - properties = new Properties(); - properties.put("account", parameters.get("account")); - properties.put("user", testUser); - properties.put("ssl", parameters.get("ssl")); - properties.put("port", parameters.get("port")); - properties.put("authenticator", ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT.toString()); - try (Connection connection = DriverManager.getConnection(uri, properties)) {} + // PKCS #8 + String privateKeyBase64 = readPrivateKeyFileToBase64Content("rsa_key.p8"); + String uri = uriWithPrivateKeyBase64(baseUri, privateKeyBase64); + connectSuccessfully(uri, properties); - // test with password-protected private key file (.p8) - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - statement.execute("use role accountadmin"); - pathfile = getFullPathFileInResource("encrypted_rsa_key.pub"); - pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); - pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); - pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); - statement.execute(String.format("alter user %s set rsa_public_key='%s'", testUser, pubKey)); - } + // PKCS #1 + privateKeyBase64 = readPrivateKeyFileToBase64Content("rsa_key.pem"); + uri = uriWithPrivateKeyBase64(baseUri, privateKeyBase64); + connectSuccessfully(uri, properties); - privateKeyBase64 = - Base64.getEncoder() - .encodeToString( - Files.readAllBytes(Paths.get(getFullPathFileInResource("encrypted_rsa_key.p8")))); - uri = - parameters.get("uri") - + "/?private_key_file_pwd=test&private_key_base64=" - + privateKeyBase64; + // test with password-protected private key file (.p8) + setUpPublicKey("encrypted_rsa_key.pub", testUser); - try (Connection connection = DriverManager.getConnection(uri, properties)) {} - // test with incorrect password for private key - uri = - parameters.get("uri") - + "/?private_key_file_pwd=wrong_password&private_key_base64=" - + privateKeyBase64; + privateKeyBase64 = readPrivateKeyFileToBase64Content("encrypted_rsa_key.p8"); + uri = uriWithPrivateKeyBase64AndPassword(baseUri, privateKeyBase64, "test"); + connectSuccessfully(uri, properties); - try (Connection connection = DriverManager.getConnection(uri, properties)) { - fail(); - } catch (SQLException e) { - assertEquals( - (int) ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY.getMessageCode(), e.getErrorCode()); - } + // test with incorrect password for private key + uri = uriWithPrivateKeyBase64AndPassword(baseUri, privateKeyBase64, "wrong_password"); + connectExpectingInvalidOrUnsupportedPrivateKey(uri, properties); - // test with invalid public/private key combo (using 1st public key with 2nd private key) - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - statement.execute("use role accountadmin"); - pathfile = getFullPathFileInResource("rsa_key.pub"); - pubKey = new String(Files.readAllBytes(Paths.get(pathfile))); - pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", ""); - pubKey = pubKey.replace("-----END PUBLIC KEY-----", ""); - statement.execute(String.format("alter user %s set rsa_public_key='%s'", testUser, pubKey)); - } + // test with invalid public/private key combo (using 1st public key with 2nd private key) + setUpPublicKey("rsa_key.pub", testUser); - privateKeyBase64 = - Base64.getEncoder() - .encodeToString( - Files.readAllBytes(Paths.get(getFullPathFileInResource("encrypted_rsa_key.p8")))); - uri = - parameters.get("uri") - + "/?private_key_file_pwd=test&private_key_base64=" - + privateKeyBase64; - try (Connection connection = DriverManager.getConnection(uri, properties)) { - fail(); - } catch (SQLException e) { - assertEquals(390144, e.getErrorCode()); + privateKeyBase64 = readPrivateKeyFileToBase64Content("encrypted_rsa_key.p8"); + uri = uriWithPrivateKeyBase64AndPassword(baseUri, privateKeyBase64, "test"); + connectExpectingError390144(uri, properties); + + // test with invalid private key + privateKeyBase64 = readPrivateKeyFileToBase64Content("invalid_private_key.pem"); + uri = uriWithPrivateKeyBase64(baseUri, privateKeyBase64); + + connectExpectingInvalidOrUnsupportedPrivateKey(uri, properties); + } finally { + // clean up + unsetPublicKey(testUser); } + } - // test with invalid private key - privateKeyBase64 = - Base64.getEncoder() - .encodeToString( - Files.readAllBytes( - Paths.get(getFullPathFileInResource("invalid_private_key.pem")))); - uri = parameters.get("uri") + "/?private_key_base64=" + privateKeyBase64; + private static String uriWithPrivateKeyBase64(String baseUri, String privateKeyBase64) { + return baseUri + + "/?" + + SFSessionProperty.PRIVATE_KEY_BASE64.getPropertyKey() + + "=" + + privateKeyBase64; + } + + private static String uriWithPrivateKeyBase64AndPassword( + String baseUri, String privateKeyBase64, String password) { + return uriWithPrivateKeyBase64(baseUri, privateKeyBase64) + + "&" + + SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey() + + "=" + + password; + } + + private static Properties preparePropertiesForPrivateKeyTests(Map parameters) { + Properties properties = new Properties(); + properties.put("account", parameters.get("account")); + properties.put("user", parameters.get("user")); + properties.put("ssl", parameters.get("ssl")); + properties.put("port", parameters.get("port")); + return properties; + } + + private static void connectExpectingInvalidOrUnsupportedPrivateKey( + String uri, Properties properties) { try (Connection connection = DriverManager.getConnection(uri, properties)) { fail(); } catch (SQLException e) { assertEquals( (int) ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY.getMessageCode(), e.getErrorCode()); } - - // clean up - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - statement.execute("use role accountadmin"); - statement.execute(String.format("alter user %s unset rsa_public_key", testUser)); - } } - // This will only work with JDBC driver versions higher than 3.15.1 + /** Works in > 3.18.0 */ @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testPrivateKeyBase64InConnectionStringWithBouncyCastle() @@ -1098,38 +1022,35 @@ public void testBasicDataSourceSerialization() throws Exception { // set up DataSource object and ensure connection works Map params = getConnectionParameters(); SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); - SnowflakeBasicDataSource ds2 = null; ds.setServerName(params.get("host")); ds.setSsl("on".equals(params.get("ssl"))); ds.setAccount(params.get("account")); ds.setPortNumber(Integer.parseInt(params.get("port"))); ds.setUser(params.get("user")); ds.setPassword(params.get("password")); - try (Connection con = ds.getConnection(); - Statement statement = con.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1")) { - resultSet.next(); - assertThat("select 1", resultSet.getInt(1), equalTo(1)); - con.close(); - File serializedFile = tmpFolder.newFile("serializedStuff.ser"); - // serialize datasource object into a file - try (FileOutputStream outputFile = new FileOutputStream(serializedFile); - ObjectOutputStream out = new ObjectOutputStream(outputFile)) { - out.writeObject(ds); - } - // deserialize into datasource object again - try (FileInputStream inputFile = new FileInputStream(serializedFile); - ObjectInputStream in = new ObjectInputStream(inputFile)) { - ds2 = (SnowflakeBasicDataSource) in.readObject(); - } + + connectAndExecuteSelect1(ds); + + File serializedFile = tmpFolder.newFile("serializedStuff.ser"); + // serialize datasource object into a file + try (FileOutputStream outputFile = new FileOutputStream(serializedFile); + ObjectOutputStream out = new ObjectOutputStream(outputFile)) { + out.writeObject(ds); } + // deserialize into datasource object again + try (FileInputStream inputFile = new FileInputStream(serializedFile); + ObjectInputStream in = new ObjectInputStream(inputFile)) { + SnowflakeBasicDataSource ds2 = (SnowflakeBasicDataSource) in.readObject(); + connectAndExecuteSelect1(ds2); + } + } - // test connection a second time - try (Connection con = ds2.getConnection(); + private static void connectAndExecuteSelect1(SnowflakeBasicDataSource ds) throws SQLException { + try (Connection con = ds.getConnection(); Statement statement = con.createStatement(); ResultSet resultSet = statement.executeQuery("select 1")) { - resultSet.next(); - assertThat("select 1", resultSet.getInt(1), equalTo(1)); + assertTrue(resultSet.next()); + assertEquals(1, resultSet.getInt(1)); } } @@ -1484,12 +1405,7 @@ private Boolean isPbes2KeySupported() throws SQLException, IOException, Security return false; } - // clean up - connection = getConnection(); - statement = connection.createStatement(); - statement.execute("use role accountadmin"); - statement.execute(String.format("alter user %s unset rsa_public_key", testUser)); - connection.close(); + unsetPublicKey(testUser); return true; } @@ -1579,92 +1495,6 @@ public void testDataSourceOktaGenerates429StatusCode() throws Exception { } } - /** Test added in JDBC driver version > 3.16.1 */ - @Test - public void testDataSourceSetters() { - Map params = getConnectionParameters(); - SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); - - ds.setTracing("all"); - ds.setApplication("application_name"); - ds.setAccount(params.get("account")); - ds.setAuthenticator("snowflake"); - ds.setArrowTreatDecimalAsInt(true); - ds.setAllowUnderscoresInHost(true); - ds.setClientConfigFile("/some/path/file.json"); - ds.setDisableGcsDefaultCredentials(false); - ds.setDisableSamlURLCheck(false); - ds.setDisableSocksProxy(false); - ds.setEnablePatternSearch(true); - ds.setDatabaseName("DB_NAME"); - ds.setEnablePutGet(false); - ds.setMaxHttpRetries(5); - ds.setNetworkTimeout(10); - ds.setOcspFailOpen(false); - ds.setProxyHost("proxyHost.com"); - ds.setProxyPort(8080); - ds.setProxyProtocol("http"); - ds.setProxyUser("proxyUser"); - ds.setProxyPassword("proxyPassword"); - ds.setPutGetMaxRetries(3); - ds.setStringsQuotedForColumnDef(true); - ds.setEnableDiagnostics(true); - ds.setDiagnosticsAllowlistFile("/some/path/allowlist.json"); - - Properties props = ds.getProperties(); - assertEquals(params.get("account"), props.get("account")); - assertEquals("snowflake", props.get("authenticator")); - assertEquals("all", props.get("tracing")); - assertEquals("application_name", props.get(SFSessionProperty.APPLICATION.getPropertyKey())); - assertEquals("snowflake", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); - assertEquals( - "true", props.get(SFSessionProperty.JDBC_ARROW_TREAT_DECIMAL_AS_INT.getPropertyKey())); - assertEquals("true", props.get(SFSessionProperty.ALLOW_UNDERSCORES_IN_HOST.getPropertyKey())); - assertEquals( - "/some/path/file.json", props.get(SFSessionProperty.CLIENT_CONFIG_FILE.getPropertyKey())); - assertEquals( - "false", props.get(SFSessionProperty.DISABLE_GCS_DEFAULT_CREDENTIALS.getPropertyKey())); - assertEquals("false", props.get(SFSessionProperty.DISABLE_SAML_URL_CHECK.getPropertyKey())); - assertEquals("false", props.get(SFSessionProperty.DISABLE_SOCKS_PROXY.getPropertyKey())); - assertEquals("true", props.get(SFSessionProperty.ENABLE_PATTERN_SEARCH.getPropertyKey())); - assertEquals("DB_NAME", props.get(SFSessionProperty.DATABASE.getPropertyKey())); - assertEquals("false", props.get(SFSessionProperty.ENABLE_PUT_GET.getPropertyKey())); - assertEquals("5", props.get(SFSessionProperty.MAX_HTTP_RETRIES.getPropertyKey())); - assertEquals("10", props.get(SFSessionProperty.NETWORK_TIMEOUT.getPropertyKey())); - assertEquals("false", props.get(SFSessionProperty.OCSP_FAIL_OPEN.getPropertyKey())); - assertEquals("proxyHost.com", props.get(SFSessionProperty.PROXY_HOST.getPropertyKey())); - assertEquals("8080", props.get(SFSessionProperty.PROXY_PORT.getPropertyKey())); - assertEquals("http", props.get(SFSessionProperty.PROXY_PROTOCOL.getPropertyKey())); - assertEquals("proxyUser", props.get(SFSessionProperty.PROXY_USER.getPropertyKey())); - assertEquals("proxyPassword", props.get(SFSessionProperty.PROXY_PASSWORD.getPropertyKey())); - assertEquals("3", props.get(SFSessionProperty.PUT_GET_MAX_RETRIES.getPropertyKey())); - assertEquals("true", props.get(SFSessionProperty.STRINGS_QUOTED.getPropertyKey())); - assertEquals("true", props.get(SFSessionProperty.ENABLE_DIAGNOSTICS.getPropertyKey())); - assertEquals( - "/some/path/allowlist.json", - props.get(SFSessionProperty.DIAGNOSTICS_ALLOWLIST_FILE.getPropertyKey())); - - ds.setOauthToken("a_token"); - assertEquals("OAUTH", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); - assertEquals("a_token", props.get(SFSessionProperty.TOKEN.getPropertyKey())); - - ds.setPasscodeInPassword(true); - assertEquals("true", props.get(SFSessionProperty.PASSCODE_IN_PASSWORD.getPropertyKey())); - assertEquals( - "USERNAME_PASSWORD_MFA", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); - - ds.setPrivateKeyFile("key.p8", "pwd"); - assertEquals("key.p8", props.get(SFSessionProperty.PRIVATE_KEY_FILE.getPropertyKey())); - assertEquals("pwd", props.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey())); - assertEquals("SNOWFLAKE_JWT", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); - - ds.setPasscodeInPassword(false); - ds.setPasscode("a_passcode"); - assertEquals("false", props.get(SFSessionProperty.PASSCODE_IN_PASSWORD.getPropertyKey())); - assertEquals( - "USERNAME_PASSWORD_MFA", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); - assertEquals("a_passcode", props.get(SFSessionProperty.PASSCODE.getPropertyKey())); - } /** * SNOW-1465374: For TIMESTAMP_LTZ we were returning timestamps without timezone when scale was * set e.g. to 6 in Arrow format The problem wasn't visible when calling getString, but was diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java index 1a0c44f84..930fe92f1 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java @@ -5,8 +5,11 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; import java.sql.SQLException; +import java.util.Properties; +import net.snowflake.client.core.SFSessionProperty; import org.junit.Test; /** Data source unit test */ @@ -19,4 +22,94 @@ public void testSetLoginTimeout() throws SQLException { ds.setLoginTimeout(10); assertThat(ds.getLoginTimeout(), is(10)); } + + @Test + public void testDataSourceSetters() { + SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); + + ds.setTracing("all"); + ds.setApplication("application_name"); + ds.setAccount("testaccount"); + ds.setAuthenticator("snowflake"); + ds.setArrowTreatDecimalAsInt(true); + ds.setAllowUnderscoresInHost(true); + ds.setClientConfigFile("/some/path/file.json"); + ds.setDisableGcsDefaultCredentials(false); + ds.setDisableSamlURLCheck(false); + ds.setDisableSocksProxy(false); + ds.setEnablePatternSearch(true); + ds.setDatabaseName("DB_NAME"); + ds.setEnablePutGet(false); + ds.setMaxHttpRetries(5); + ds.setNetworkTimeout(10); + ds.setOcspFailOpen(false); + ds.setProxyHost("proxyHost.com"); + ds.setProxyPort(8080); + ds.setProxyProtocol("http"); + ds.setProxyUser("proxyUser"); + ds.setProxyPassword("proxyPassword"); + ds.setPutGetMaxRetries(3); + ds.setStringsQuotedForColumnDef(true); + ds.setEnableDiagnostics(true); + ds.setDiagnosticsAllowlistFile("/some/path/allowlist.json"); + + Properties props = ds.getProperties(); + assertEquals("testaccount", props.get("account")); + assertEquals("snowflake", props.get("authenticator")); + assertEquals("all", props.get("tracing")); + assertEquals("application_name", props.get(SFSessionProperty.APPLICATION.getPropertyKey())); + assertEquals("snowflake", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); + assertEquals( + "true", props.get(SFSessionProperty.JDBC_ARROW_TREAT_DECIMAL_AS_INT.getPropertyKey())); + assertEquals("true", props.get(SFSessionProperty.ALLOW_UNDERSCORES_IN_HOST.getPropertyKey())); + assertEquals( + "/some/path/file.json", props.get(SFSessionProperty.CLIENT_CONFIG_FILE.getPropertyKey())); + assertEquals( + "false", props.get(SFSessionProperty.DISABLE_GCS_DEFAULT_CREDENTIALS.getPropertyKey())); + assertEquals("false", props.get(SFSessionProperty.DISABLE_SAML_URL_CHECK.getPropertyKey())); + assertEquals("false", props.get(SFSessionProperty.DISABLE_SOCKS_PROXY.getPropertyKey())); + assertEquals("true", props.get(SFSessionProperty.ENABLE_PATTERN_SEARCH.getPropertyKey())); + assertEquals("DB_NAME", props.get(SFSessionProperty.DATABASE.getPropertyKey())); + assertEquals("false", props.get(SFSessionProperty.ENABLE_PUT_GET.getPropertyKey())); + assertEquals("5", props.get(SFSessionProperty.MAX_HTTP_RETRIES.getPropertyKey())); + assertEquals("10", props.get(SFSessionProperty.NETWORK_TIMEOUT.getPropertyKey())); + assertEquals("false", props.get(SFSessionProperty.OCSP_FAIL_OPEN.getPropertyKey())); + assertEquals("proxyHost.com", props.get(SFSessionProperty.PROXY_HOST.getPropertyKey())); + assertEquals("8080", props.get(SFSessionProperty.PROXY_PORT.getPropertyKey())); + assertEquals("http", props.get(SFSessionProperty.PROXY_PROTOCOL.getPropertyKey())); + assertEquals("proxyUser", props.get(SFSessionProperty.PROXY_USER.getPropertyKey())); + assertEquals("proxyPassword", props.get(SFSessionProperty.PROXY_PASSWORD.getPropertyKey())); + assertEquals("3", props.get(SFSessionProperty.PUT_GET_MAX_RETRIES.getPropertyKey())); + assertEquals("true", props.get(SFSessionProperty.STRINGS_QUOTED.getPropertyKey())); + assertEquals("true", props.get(SFSessionProperty.ENABLE_DIAGNOSTICS.getPropertyKey())); + assertEquals( + "/some/path/allowlist.json", + props.get(SFSessionProperty.DIAGNOSTICS_ALLOWLIST_FILE.getPropertyKey())); + + ds.setOauthToken("a_token"); + assertEquals("OAUTH", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); + assertEquals("a_token", props.get(SFSessionProperty.TOKEN.getPropertyKey())); + + ds.setPasscodeInPassword(true); + assertEquals("true", props.get(SFSessionProperty.PASSCODE_IN_PASSWORD.getPropertyKey())); + assertEquals( + "USERNAME_PASSWORD_MFA", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); + + ds.setPrivateKeyFile("key.p8", "pwd"); + assertEquals("key.p8", props.get(SFSessionProperty.PRIVATE_KEY_FILE.getPropertyKey())); + assertEquals("pwd", props.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey())); + assertEquals("SNOWFLAKE_JWT", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); + + ds.setPasscodeInPassword(false); + ds.setPasscode("a_passcode"); + assertEquals("false", props.get(SFSessionProperty.PASSCODE_IN_PASSWORD.getPropertyKey())); + assertEquals( + "USERNAME_PASSWORD_MFA", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); + assertEquals("a_passcode", props.get(SFSessionProperty.PASSCODE.getPropertyKey())); + + ds.setPrivateKeyBase64("fake_key", "pwd"); + assertEquals("fake_key", props.get(SFSessionProperty.PRIVATE_KEY_BASE64.getPropertyKey())); + assertEquals("pwd", props.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey())); + assertEquals("SNOWFLAKE_JWT", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); + } } From f01dcc5f8513fd084c91e3abe26e962cbfa2c503 Mon Sep 17 00:00:00 2001 From: Dawid Heyman Date: Fri, 9 Aug 2024 18:40:17 +0200 Subject: [PATCH 3/7] SNOW-1618131: Remove error_on_generic_pruner integration test (#1866) --- .../client/jdbc/SnowflakeDriverIT.java | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverIT.java index a66dd4c4a..a540adcec 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverIT.java @@ -1293,40 +1293,6 @@ private void addBindBatch(PreparedStatement preparedStatement, java.sql.Date sql preparedStatement.addBatch(); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void test31448() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - - statement.execute( - "alter session set enable_fix_31448_2=2, " + "error_on_generic_pruner=true;"); - - statement.execute("alter session set timestamp_type_mapping=timestamp_ntz"); - - statement.execute("create or replace table " + "bug56658(iv number, tsv timestamp_ntz)"); - statement.execute( - "insert into bug56658 select seq8(), " - + "timestampadd(day, seq8(), '1970-01-13 00:00:00'::timestamp_ntz)\n" - + "from table(generator(rowcount=>20))"); - - connection - .unwrap(SnowflakeConnectionV1.class) - .getSfSession() - .setTimestampMappedType(SnowflakeType.TIMESTAMP_NTZ); - Timestamp ts = buildTimestamp(1970, 0, 15, 10, 14, 30, 0); - try (PreparedStatement preparedStatement = - connection.prepareStatement( - "select iv, tsv from bug56658 where tsv" + " >= ? and tsv <= ? order by iv;")) { - statement.execute("alter session set timestamp_type_mapping=timestamp_ntz"); - Timestamp ts2 = buildTimestamp(1970, 0, 18, 10, 14, 30, 0); - preparedStatement.setTimestamp(1, ts); - preparedStatement.setTimestamp(2, ts2); - preparedStatement.executeQuery(); - } - } - } - @Test public void testBind() throws Throwable { ResultSetMetaData resultSetMetaData = null; From 4060fe1a286ba3319be456fa04d6dbd36106fdba Mon Sep 17 00:00:00 2001 From: Waleed Fateem <72769898+sfc-gh-wfateem@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:50:19 -0500 Subject: [PATCH 4/7] SNOW-1614292: java.lang.ClassNotFoundException: org.slf4j.LoggerFactor (#1862) Fixes issue where the absence of the slf4j-api library from the classpath results in a NoClassDefFoundError/ClassNotFoundException because the class org.slf4j.LoggerFactory can't be found --- .../net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java index 751b47d19..bd5a3945e 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java @@ -75,8 +75,6 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.WildcardFileFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Class for uploading/downloading files @@ -100,7 +98,6 @@ public class SnowflakeFileTransferAgent extends SFBaseFileTransferAgent { private static final String localFSFileSep = systemGetProperty("file.separator"); private static final int DEFAULT_PARALLEL = 10; - private static final Logger log = LoggerFactory.getLogger(SnowflakeFileTransferAgent.class); private final String command; From f22fce492812c3609900bbd77b8b7e28bc9b6e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kubik?= Date: Wed, 14 Aug 2024 11:33:43 +0200 Subject: [PATCH 5/7] SNOW-618478: Introduce unified property for private key file and base64 bytes Adds private_key_pwd session property that may be used interchangeably with existing private_key_file_pwd. Marks private_key_file_pwd property as deprecated as in the future it'll be completely replaced with private_key_pwd. Rename usages of privateKeyFilePwd to privateKeyPwd to clarify that from now it may refer not only to the file but also private_key_base64. --- .../snowflake/client/core/SFLoginInput.java | 10 ++--- .../net/snowflake/client/core/SFSession.java | 9 +++-- .../client/core/SFSessionProperty.java | 6 +++ .../snowflake/client/core/SessionUtil.java | 10 ++--- .../client/core/SessionUtilKeyPair.java | 40 +++++++++---------- .../client/jdbc/SnowflakeBasicDataSource.java | 4 +- .../client/core/SessionUtilLatestIT.java | 4 +- .../client/jdbc/ConnectionLatestIT.java | 2 +- .../jdbc/SnowflakeBasicDataSourceTest.java | 4 +- .../client/jdbc/SnowflakeDriverTest.java | 2 +- .../client/util/SecretDetectorTest.java | 2 +- 11 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/main/java/net/snowflake/client/core/SFLoginInput.java b/src/main/java/net/snowflake/client/core/SFLoginInput.java index 874bb3265..4cfbcba08 100644 --- a/src/main/java/net/snowflake/client/core/SFLoginInput.java +++ b/src/main/java/net/snowflake/client/core/SFLoginInput.java @@ -46,7 +46,7 @@ public class SFLoginInput { private HttpClientSettingsKey httpClientKey; private String privateKeyFile; private String privateKeyBase64; - private String privateKeyFilePwd; + private String privateKeyPwd; private String inFlightCtx; // Opaque string sent for Snowsight account activation private boolean disableConsoleLogin = true; @@ -340,8 +340,8 @@ SFLoginInput setPrivateKeyFile(String privateKeyFile) { return this; } - SFLoginInput setPrivateKeyFilePwd(String privateKeyFilePwd) { - this.privateKeyFilePwd = privateKeyFilePwd; + SFLoginInput setPrivateKeyPwd(String privateKeyPwd) { + this.privateKeyPwd = privateKeyPwd; return this; } @@ -349,8 +349,8 @@ String getPrivateKeyFile() { return privateKeyFile; } - String getPrivateKeyFilePwd() { - return privateKeyFilePwd; + String getPrivateKeyPwd() { + return privateKeyPwd; } boolean isPrivateKeyProvided() { diff --git a/src/main/java/net/snowflake/client/core/SFSession.java b/src/main/java/net/snowflake/client/core/SFSession.java index c96f50381..f7ed48717 100644 --- a/src/main/java/net/snowflake/client/core/SFSession.java +++ b/src/main/java/net/snowflake/client/core/SFSession.java @@ -460,6 +460,7 @@ public void addSFSessionProperty(String propertyName, Object propertyValue) thro break; case PRIVATE_KEY_FILE_PWD: + case PRIVATE_KEY_PWD: if (propertyValue != null) { privateKeyPassword = (String) propertyValue; } @@ -592,7 +593,8 @@ public synchronized void open() throws SFException, SnowflakeSQLException { SFLoggerUtil.isVariableProvided( (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_BASE64)), SFLoggerUtil.isVariableProvided( - (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD)), + (String) connectionPropertiesMap.getOrDefault(SFSessionProperty.PRIVATE_KEY_PWD, + connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD))), connectionPropertiesMap.get(SFSessionProperty.ENABLE_DIAGNOSTICS), connectionPropertiesMap.get(SFSessionProperty.DIAGNOSTICS_ALLOWLIST_FILE), sessionParametersMap.get(CLIENT_STORE_TEMPORARY_CREDENTIAL), @@ -642,8 +644,9 @@ public synchronized void open() throws SFException, SnowflakeSQLException { .setPrivateKeyFile((String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE)) .setPrivateKeyBase64( (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_BASE64)) - .setPrivateKeyFilePwd( - (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD)) + .setPrivateKeyPwd( + (String) connectionPropertiesMap.getOrDefault(SFSessionProperty.PRIVATE_KEY_PWD, + connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD))) .setApplication((String) connectionPropertiesMap.get(SFSessionProperty.APPLICATION)) .setServiceName(getServiceName()) .setOCSPMode(getOCSPMode()) diff --git a/src/main/java/net/snowflake/client/core/SFSessionProperty.java b/src/main/java/net/snowflake/client/core/SFSessionProperty.java index 08bbc63af..5e1c90847 100644 --- a/src/main/java/net/snowflake/client/core/SFSessionProperty.java +++ b/src/main/java/net/snowflake/client/core/SFSessionProperty.java @@ -55,7 +55,13 @@ public enum SFSessionProperty { INJECT_WAIT_IN_PUT("inject_wait_in_put", false, Integer.class), PRIVATE_KEY_FILE("private_key_file", false, String.class), PRIVATE_KEY_BASE64("private_key_base64", false, String.class), + /** + * @deprecated Use {@link #PRIVATE_KEY_PWD} for clarity. The given password will be used to decrypt + * the private key value independent of whether that value is supplied as a file or base64 string + */ + @Deprecated PRIVATE_KEY_FILE_PWD("private_key_file_pwd", false, String.class), + PRIVATE_KEY_PWD("private_key_pwd", false, String.class), CLIENT_INFO("snowflakeClientInfo", false, String.class), ALLOW_UNDERSCORES_IN_HOST("allowUnderscoresInHost", false, Boolean.class), diff --git a/src/main/java/net/snowflake/client/core/SessionUtil.java b/src/main/java/net/snowflake/client/core/SessionUtil.java index 303c231dc..3e8ac29a9 100644 --- a/src/main/java/net/snowflake/client/core/SessionUtil.java +++ b/src/main/java/net/snowflake/client/core/SessionUtil.java @@ -422,7 +422,7 @@ private static SFLoginOutput newSession( loginInput.getPrivateKey(), loginInput.getPrivateKeyFile(), loginInput.getPrivateKeyBase64(), - loginInput.getPrivateKeyFilePwd(), + loginInput.getPrivateKeyPwd(), loginInput.getAccountName(), loginInput.getUserName()); @@ -678,7 +678,7 @@ private static SFLoginOutput newSession( loginInput.getPrivateKey(), loginInput.getPrivateKeyFile(), loginInput.getPrivateKeyBase64(), - loginInput.getPrivateKeyFilePwd(), + loginInput.getPrivateKeyPwd(), loginInput.getAccountName(), loginInput.getUserName()); @@ -1726,7 +1726,7 @@ public static void resetOCSPUrlIfNecessary(String serverUrl) throws IOException * @param privateKey private key * @param privateKeyFile path to private key file * @param privateKeyBase64 base64 encoded content of the private key file - * @param privateKeyFilePwd password for private key file + * @param privateKeyPwd password for private key file or base64 encoded private key * @param accountName account name * @param userName user name * @return JWT token @@ -1736,13 +1736,13 @@ public static String generateJWTToken( PrivateKey privateKey, String privateKeyFile, String privateKeyBase64, - String privateKeyFilePwd, + String privateKeyPwd, String accountName, String userName) throws SFException { SessionUtilKeyPair s = new SessionUtilKeyPair( - privateKey, privateKeyFile, privateKeyBase64, privateKeyFilePwd, accountName, userName); + privateKey, privateKeyFile, privateKeyBase64, privateKeyPwd, accountName, userName); return s.issueJwtToken(); } diff --git a/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java b/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java index ae9e1c5b7..502e4b9d3 100644 --- a/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java +++ b/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java @@ -81,7 +81,7 @@ class SessionUtilKeyPair { PrivateKey privateKey, String privateKeyFile, String privateKeyBase64, - String privateKeyFilePwd, + String privateKeyPwd, String accountName, String userName) throws SFException { @@ -103,7 +103,7 @@ class SessionUtilKeyPair { ensurePrivateKeyProvidedInOnlyOneProperty(privateKey, privateKeyFile, privateKeyBase64); this.privateKey = - buildPrivateKey(privateKey, privateKeyFile, privateKeyBase64, privateKeyFilePwd); + buildPrivateKey(privateKey, privateKeyFile, privateKeyBase64, privateKeyPwd); // construct public key from raw bytes if (this.privateKey instanceof RSAPrivateCrtKey) { @@ -146,15 +146,15 @@ private PrivateKey buildPrivateKey( PrivateKey privateKey, String privateKeyFile, String privateKeyBase64, - String privateKeyFilePwd) + String privateKeyPwd) throws SFException { if (!Strings.isNullOrEmpty(privateKeyBase64)) { logger.trace("Reading private key from base64 string"); - return extractPrivateKeyFromBase64(privateKeyBase64, privateKeyFilePwd); + return extractPrivateKeyFromBase64(privateKeyBase64, privateKeyPwd); } if (!Strings.isNullOrEmpty(privateKeyFile)) { logger.trace("Reading private key from file"); - return extractPrivateKeyFromFile(privateKeyFile, privateKeyFilePwd); + return extractPrivateKeyFromFile(privateKeyFile, privateKeyPwd); } return privateKey; } @@ -175,32 +175,32 @@ private SecretKeyFactory getSecretKeyFactory(String algorithm) throws NoSuchAlgo } } - private PrivateKey extractPrivateKeyFromFile(String privateKeyFile, String privateKeyFilePwd) + private PrivateKey extractPrivateKeyFromFile(String privateKeyFile, String privateKeyPwd) throws SFException { try { Path privKeyPath = Paths.get(privateKeyFile); FileUtil.logFileUsage(privKeyPath, "Extract private key from file", true); byte[] bytes = Files.readAllBytes(privKeyPath); - return extractPrivateKeyFromBytes(bytes, privateKeyFilePwd); + return extractPrivateKeyFromBytes(bytes, privateKeyPwd); } catch (IOException ie) { logger.error("Could not read private key from file", ie); throw new SFException(ie, ErrorCode.INVALID_PARAMETER_VALUE, ie.getCause()); } } - private PrivateKey extractPrivateKeyFromBytes(byte[] privateKeyBytes, String privateKeyFilePwd) + private PrivateKey extractPrivateKeyFromBytes(byte[] privateKeyBytes, String privateKeyPwd) throws SFException { if (isBouncyCastleProviderEnabled) { try { - return extractPrivateKeyWithBouncyCastle(privateKeyBytes, privateKeyFilePwd); + return extractPrivateKeyWithBouncyCastle(privateKeyBytes, privateKeyPwd); } catch (IOException | PKCSException | OperatorCreationException e) { logger.error("Could not extract private key using Bouncy Castle provider", e); throw new SFException(e, ErrorCode.INVALID_OR_UNSUPPORTED_PRIVATE_KEY, e.getCause()); } } else { try { - return extractPrivateKeyWithJdk(privateKeyBytes, privateKeyFilePwd); + return extractPrivateKeyWithJdk(privateKeyBytes, privateKeyPwd); } catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException @@ -217,10 +217,10 @@ private PrivateKey extractPrivateKeyFromBytes(byte[] privateKeyBytes, String pri } } - private PrivateKey extractPrivateKeyFromBase64(String privateKeyBase64, String privateKeyFilePwd) + private PrivateKey extractPrivateKeyFromBase64(String privateKeyBase64, String privateKeyPwd) throws SFException { byte[] decodedKey = Base64.decodeBase64(privateKeyBase64); - return extractPrivateKeyFromBytes(decodedKey, privateKeyFilePwd); + return extractPrivateKeyFromBytes(decodedKey, privateKeyPwd); } public String issueJwtToken() throws SFException { @@ -280,7 +280,7 @@ public static int getTimeout() { } private PrivateKey extractPrivateKeyWithBouncyCastle( - byte[] privateKeyBytes, String privateKeyFilePwd) + byte[] privateKeyBytes, String privateKeyPwd) throws IOException, PKCSException, OperatorCreationException { logger.trace("Extracting private key using Bouncy Castle provider"); PrivateKeyInfo privateKeyInfo = null; @@ -292,7 +292,7 @@ private PrivateKey extractPrivateKeyWithBouncyCastle( PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) pemObject; InputDecryptorProvider pkcs8Prov = - new JceOpenSSLPKCS8DecryptorProviderBuilder().build(privateKeyFilePwd.toCharArray()); + new JceOpenSSLPKCS8DecryptorProviderBuilder().build(privateKeyPwd.toCharArray()); privateKeyInfo = encryptedPrivateKeyInfo.decryptPrivateKeyInfo(pkcs8Prov); } else if (pemObject instanceof PEMKeyPair) { // PKCS#1 private key @@ -311,28 +311,28 @@ private PrivateKey extractPrivateKeyWithBouncyCastle( return converter.getPrivateKey(privateKeyInfo); } - private PrivateKey extractPrivateKeyWithJdk(byte[] privateKeyFileBytes, String privateKeyFilePwd) + private PrivateKey extractPrivateKeyWithJdk(byte[] privateKeyFileBytes, String privateKeyPwd) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { logger.trace("Extracting private key using JDK"); String privateKeyContent = new String(privateKeyFileBytes, StandardCharsets.UTF_8); - if (Strings.isNullOrEmpty(privateKeyFilePwd)) { + if (Strings.isNullOrEmpty(privateKeyPwd)) { // unencrypted private key file - return generatePrivateKey(false, privateKeyContent, privateKeyFilePwd); + return generatePrivateKey(false, privateKeyContent, privateKeyPwd); } else { // encrypted private key file - return generatePrivateKey(true, privateKeyContent, privateKeyFilePwd); + return generatePrivateKey(true, privateKeyContent, privateKeyPwd); } } private PrivateKey generatePrivateKey( - boolean isEncrypted, String privateKeyContent, String privateKeyFilePwd) + boolean isEncrypted, String privateKeyContent, String privateKeyPwd) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { if (isEncrypted) { try (PemReader pr = new PemReader(new StringReader(privateKeyContent))) { byte[] decoded = pr.readPemObject().getContent(); pr.close(); EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo(decoded); - PBEKeySpec keySpec = new PBEKeySpec(privateKeyFilePwd.toCharArray()); + PBEKeySpec keySpec = new PBEKeySpec(privateKeyPwd.toCharArray()); SecretKeyFactory pbeKeyFactory = this.getSecretKeyFactory(pkInfo.getAlgName()); PKCS8EncodedKeySpec encodedKeySpec = pkInfo.getKeySpec(pbeKeyFactory.generateSecret(keySpec)); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java index 1e90b27f4..0f91aa09c 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeBasicDataSource.java @@ -232,7 +232,7 @@ public void setPrivateKeyFile(String location, String password) { this.setAuthenticator(AUTHENTICATOR_SNOWFLAKE_JWT); this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE.getPropertyKey(), location); if (!Strings.isNullOrEmpty(password)) { - this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey(), password); + this.properties.put(SFSessionProperty.PRIVATE_KEY_PWD.getPropertyKey(), password); } } @@ -240,7 +240,7 @@ public void setPrivateKeyBase64(String privateKeyBase64, String password) { this.setAuthenticator(AUTHENTICATOR_SNOWFLAKE_JWT); this.properties.put(SFSessionProperty.PRIVATE_KEY_BASE64.getPropertyKey(), privateKeyBase64); if (!Strings.isNullOrEmpty(password)) { - this.properties.put(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey(), password); + this.properties.put(SFSessionProperty.PRIVATE_KEY_PWD.getPropertyKey(), password); } } diff --git a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java index f936ee616..07f817efc 100644 --- a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java +++ b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java @@ -89,8 +89,8 @@ private SFLoginInput initMockLoginInput() { .thenReturn(ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT.name()); when(loginInput.getPrivateKeyFile()) .thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE")); - when(loginInput.getPrivateKeyFilePwd()) - .thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE_PWD")); + when(loginInput.getPrivateKeyPwd()) + .thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_PWD")); when(loginInput.getUserName()).thenReturn(systemGetEnv("SNOWFLAKE_TEST_USER")); when(loginInput.getAccountName()).thenReturn("testaccount"); when(loginInput.getAppId()).thenReturn("testid"); diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java index 4e696a871..76336080d 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java @@ -1396,7 +1396,7 @@ private Boolean isPbes2KeySupported() throws SQLException, IOException, Security String privateKeyLocation = getFullPathFileInResource(privateKeyFile); String uri = parameters.get("uri") - + "/?private_key_file_pwd=" + + "/?private_key_pwd=" + passphrase + "&private_key_file=" + privateKeyLocation; diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java index 930fe92f1..8df351889 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java @@ -97,7 +97,7 @@ public void testDataSourceSetters() { ds.setPrivateKeyFile("key.p8", "pwd"); assertEquals("key.p8", props.get(SFSessionProperty.PRIVATE_KEY_FILE.getPropertyKey())); - assertEquals("pwd", props.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey())); + assertEquals("pwd", props.get(SFSessionProperty.PRIVATE_KEY_PWD.getPropertyKey())); assertEquals("SNOWFLAKE_JWT", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); ds.setPasscodeInPassword(false); @@ -109,7 +109,7 @@ public void testDataSourceSetters() { ds.setPrivateKeyBase64("fake_key", "pwd"); assertEquals("fake_key", props.get(SFSessionProperty.PRIVATE_KEY_BASE64.getPropertyKey())); - assertEquals("pwd", props.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD.getPropertyKey())); + assertEquals("pwd", props.get(SFSessionProperty.PRIVATE_KEY_PWD.getPropertyKey())); assertEquals("SNOWFLAKE_JWT", props.get(SFSessionProperty.AUTHENTICATOR.getPropertyKey())); } } diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverTest.java index b67f99a96..8b42be5c4 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverTest.java @@ -427,7 +427,7 @@ public void testParseConnectStringException() { SnowflakeDriver snowflakeDriver = SnowflakeDriver.INSTANCE; Properties info = new Properties(); String jdbcConnectString = - "jdbc:snowflake://abc-test.us-east-1.snowflakecomputing.com/?private_key_file=C:\\temp\\rsa_key.p8&private_key_file_pwd=test_password&user=test_user"; + "jdbc:snowflake://abc-test.us-east-1.snowflakecomputing.com/?private_key_file=C:\\temp\\rsa_key.p8&private_key_pwd=test_password&user=test_user"; try { snowflakeDriver.connect(jdbcConnectString, info); fail(); diff --git a/src/test/java/net/snowflake/client/util/SecretDetectorTest.java b/src/test/java/net/snowflake/client/util/SecretDetectorTest.java index ee53f1886..15db74fca 100644 --- a/src/test/java/net/snowflake/client/util/SecretDetectorTest.java +++ b/src/test/java/net/snowflake/client/util/SecretDetectorTest.java @@ -202,7 +202,7 @@ public void testMaskParameterValue() { testParametersMasked.put("passcodeInPassword", "test"); testParametersMasked.put("passcode", "test"); testParametersMasked.put("id_token", "test"); - testParametersMasked.put("private_key_file_pwd", "test"); + testParametersMasked.put("private_key_pwd", "test"); testParametersMasked.put("proxyPassword", "test"); testParametersMasked.put("proxyUser", "test"); From ea2c44ad467e767f2bfa2f24239fd2bd26dee94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kubik?= Date: Wed, 14 Aug 2024 11:33:43 +0200 Subject: [PATCH 6/7] SNOW-618478: Introduce unified property for private key file and base64 bytes Adds private_key_pwd session property that may be used interchangeably with existing private_key_file_pwd. Marks private_key_file_pwd property as deprecated as in the future it'll be completely replaced with private_key_pwd. Rename usages of privateKeyFilePwd to privateKeyPwd to clarify that from now it may refer not only to the file but also private_key_base64. --- .../java/net/snowflake/client/core/SFSession.java | 8 ++++++-- .../net/snowflake/client/core/SFSessionProperty.java | 5 +++-- .../net/snowflake/client/core/SessionUtilKeyPair.java | 11 +++-------- .../snowflake/client/core/SessionUtilLatestIT.java | 3 +-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/snowflake/client/core/SFSession.java b/src/main/java/net/snowflake/client/core/SFSession.java index f7ed48717..3b0890501 100644 --- a/src/main/java/net/snowflake/client/core/SFSession.java +++ b/src/main/java/net/snowflake/client/core/SFSession.java @@ -593,7 +593,9 @@ public synchronized void open() throws SFException, SnowflakeSQLException { SFLoggerUtil.isVariableProvided( (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_BASE64)), SFLoggerUtil.isVariableProvided( - (String) connectionPropertiesMap.getOrDefault(SFSessionProperty.PRIVATE_KEY_PWD, + (String) + connectionPropertiesMap.getOrDefault( + SFSessionProperty.PRIVATE_KEY_PWD, connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD))), connectionPropertiesMap.get(SFSessionProperty.ENABLE_DIAGNOSTICS), connectionPropertiesMap.get(SFSessionProperty.DIAGNOSTICS_ALLOWLIST_FILE), @@ -645,7 +647,9 @@ public synchronized void open() throws SFException, SnowflakeSQLException { .setPrivateKeyBase64( (String) connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_BASE64)) .setPrivateKeyPwd( - (String) connectionPropertiesMap.getOrDefault(SFSessionProperty.PRIVATE_KEY_PWD, + (String) + connectionPropertiesMap.getOrDefault( + SFSessionProperty.PRIVATE_KEY_PWD, connectionPropertiesMap.get(SFSessionProperty.PRIVATE_KEY_FILE_PWD))) .setApplication((String) connectionPropertiesMap.get(SFSessionProperty.APPLICATION)) .setServiceName(getServiceName()) diff --git a/src/main/java/net/snowflake/client/core/SFSessionProperty.java b/src/main/java/net/snowflake/client/core/SFSessionProperty.java index 5e1c90847..e99466d17 100644 --- a/src/main/java/net/snowflake/client/core/SFSessionProperty.java +++ b/src/main/java/net/snowflake/client/core/SFSessionProperty.java @@ -56,8 +56,9 @@ public enum SFSessionProperty { PRIVATE_KEY_FILE("private_key_file", false, String.class), PRIVATE_KEY_BASE64("private_key_base64", false, String.class), /** - * @deprecated Use {@link #PRIVATE_KEY_PWD} for clarity. The given password will be used to decrypt - * the private key value independent of whether that value is supplied as a file or base64 string + * @deprecated Use {@link #PRIVATE_KEY_PWD} for clarity. The given password will be used to + * decrypt the private key value independent of whether that value is supplied as a file or + * base64 string */ @Deprecated PRIVATE_KEY_FILE_PWD("private_key_file_pwd", false, String.class), diff --git a/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java b/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java index 502e4b9d3..50e351cd1 100644 --- a/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java +++ b/src/main/java/net/snowflake/client/core/SessionUtilKeyPair.java @@ -102,8 +102,7 @@ class SessionUtilKeyPair { } ensurePrivateKeyProvidedInOnlyOneProperty(privateKey, privateKeyFile, privateKeyBase64); - this.privateKey = - buildPrivateKey(privateKey, privateKeyFile, privateKeyBase64, privateKeyPwd); + this.privateKey = buildPrivateKey(privateKey, privateKeyFile, privateKeyBase64, privateKeyPwd); // construct public key from raw bytes if (this.privateKey instanceof RSAPrivateCrtKey) { @@ -143,10 +142,7 @@ private static void ensurePrivateKeyProvidedInOnlyOneProperty( } private PrivateKey buildPrivateKey( - PrivateKey privateKey, - String privateKeyFile, - String privateKeyBase64, - String privateKeyPwd) + PrivateKey privateKey, String privateKeyFile, String privateKeyBase64, String privateKeyPwd) throws SFException { if (!Strings.isNullOrEmpty(privateKeyBase64)) { logger.trace("Reading private key from base64 string"); @@ -279,8 +275,7 @@ public static int getTimeout() { return jwtAuthTimeout; } - private PrivateKey extractPrivateKeyWithBouncyCastle( - byte[] privateKeyBytes, String privateKeyPwd) + private PrivateKey extractPrivateKeyWithBouncyCastle(byte[] privateKeyBytes, String privateKeyPwd) throws IOException, PKCSException, OperatorCreationException { logger.trace("Extracting private key using Bouncy Castle provider"); PrivateKeyInfo privateKeyInfo = null; diff --git a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java index 07f817efc..be6c03b01 100644 --- a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java +++ b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java @@ -89,8 +89,7 @@ private SFLoginInput initMockLoginInput() { .thenReturn(ClientAuthnDTO.AuthenticatorType.SNOWFLAKE_JWT.name()); when(loginInput.getPrivateKeyFile()) .thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_FILE")); - when(loginInput.getPrivateKeyPwd()) - .thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_PWD")); + when(loginInput.getPrivateKeyPwd()).thenReturn(systemGetEnv("SNOWFLAKE_TEST_PRIVATE_KEY_PWD")); when(loginInput.getUserName()).thenReturn(systemGetEnv("SNOWFLAKE_TEST_USER")); when(loginInput.getAccountName()).thenReturn("testaccount"); when(loginInput.getAppId()).thenReturn("testid"); From 464b199bfc19e6c5e484d711eb9128106b4fb819 Mon Sep 17 00:00:00 2001 From: Piotr Bulawa Date: Wed, 28 Aug 2024 16:03:47 +0200 Subject: [PATCH 7/7] SNOW-144173: Add more cases (#1764) --- .../snowflake/client/jdbc/SnowflakeConnectString.java | 7 +++---- .../java/net/snowflake/client/util/SecretDetector.java | 4 +++- .../net/snowflake/client/util/SecretDetectorTest.java | 9 +++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeConnectString.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeConnectString.java index ea456be6c..e918edf50 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeConnectString.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeConnectString.java @@ -18,6 +18,7 @@ import net.snowflake.client.core.SFSessionProperty; import net.snowflake.client.log.SFLogger; import net.snowflake.client.log.SFLoggerFactory; +import net.snowflake.client.util.SecretDetector; public class SnowflakeConnectString implements Serializable { private static final long serialVersionUID = 1L; @@ -209,10 +210,8 @@ public String toString(boolean maskSensitiveValue) { String k = URLEncoder.encode(entry.getKey(), "UTF-8"); String v = URLEncoder.encode(entry.getValue().toString(), "UTF-8"); urlStr.append(k).append('='); - if (maskSensitiveValue && "password".equalsIgnoreCase(k) - || "passcode".equalsIgnoreCase(k) - || "proxyPassword".equalsIgnoreCase(k)) { - urlStr.append("******"); + if (maskSensitiveValue) { + urlStr.append(SecretDetector.maskParameterValue(k, v)); } else { urlStr.append(v); } diff --git a/src/main/java/net/snowflake/client/util/SecretDetector.java b/src/main/java/net/snowflake/client/util/SecretDetector.java index 454d7b7be..3ae48defa 100644 --- a/src/main/java/net/snowflake/client/util/SecretDetector.java +++ b/src/main/java/net/snowflake/client/util/SecretDetector.java @@ -111,7 +111,9 @@ public static boolean isSensitive(String name) { */ private static boolean isSensitiveParameter(String name) { Pattern PASSWORD_IN_NAME = - Pattern.compile(".*?(password|pwd|token|proxyuser).*?", Pattern.CASE_INSENSITIVE); + Pattern.compile( + ".*?(password|pwd|token|proxyuser|privatekey|passcode|proxypassword|private_key_base).*?", + Pattern.CASE_INSENSITIVE); Matcher matcher = PASSWORD_IN_NAME.matcher(name); return isSensitive(name) || matcher.matches(); } diff --git a/src/test/java/net/snowflake/client/util/SecretDetectorTest.java b/src/test/java/net/snowflake/client/util/SecretDetectorTest.java index 15db74fca..aa3339309 100644 --- a/src/test/java/net/snowflake/client/util/SecretDetectorTest.java +++ b/src/test/java/net/snowflake/client/util/SecretDetectorTest.java @@ -205,6 +205,15 @@ public void testMaskParameterValue() { testParametersMasked.put("private_key_pwd", "test"); testParametersMasked.put("proxyPassword", "test"); testParametersMasked.put("proxyUser", "test"); + testParametersMasked.put("privatekey", "test"); + testParametersMasked.put("private_key_base64", "test"); + testParametersMasked.put("privateKeyBase64", "test"); + testParametersMasked.put("id_token_password", "test"); + testParametersMasked.put("masterToken", "test"); + testParametersMasked.put("mfaToken", "test"); + testParametersMasked.put("password", "test"); + testParametersMasked.put("sessionToken", "test"); + testParametersMasked.put("token", "test"); Map testParametersUnmasked = new HashMap<>(); testParametersUnmasked.put("oktausername", "test");