From 07a3201d990241cf1d1aef64f004421b293a8bb3 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 7 Nov 2024 13:03:07 +0100 Subject: [PATCH] SNOW-1689931 Adding flag to skip token file permission verification --- .../config/SFConnectionConfigParser.java | 83 ++++++++++--------- .../client/core/SFSessionProperty.java | 5 +- .../snowflake/client/jdbc/SnowflakeUtil.java | 16 ++++ .../config/SFConnectionConfigParserTest.java | 17 ++++ 4 files changed, 82 insertions(+), 39 deletions(-) diff --git a/src/main/java/net/snowflake/client/config/SFConnectionConfigParser.java b/src/main/java/net/snowflake/client/config/SFConnectionConfigParser.java index 35698c557..6577a921a 100644 --- a/src/main/java/net/snowflake/client/config/SFConnectionConfigParser.java +++ b/src/main/java/net/snowflake/client/config/SFConnectionConfigParser.java @@ -1,5 +1,6 @@ package net.snowflake.client.config; +import static net.snowflake.client.jdbc.SnowflakeUtil.convertSystemGetEnvToBooleanValue; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetEnv; import com.fasterxml.jackson.dataformat.toml.TomlMapper; @@ -34,6 +35,50 @@ public class SFConnectionConfigParser { "SNOWFLAKE_DEFAULT_CONNECTION_NAME"; public static final String DEFAULT = "default"; public static final String SNOWFLAKE_TOKEN_FILE_PATH = "/snowflake/session/token"; + public static final String SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION = + "SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION"; + + public static ConnectionParameters buildConnectionParameters() throws SnowflakeSQLException { + String defaultConnectionName = + Optional.ofNullable(systemGetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY)).orElse(DEFAULT); + Map fileConnectionConfiguration = + loadDefaultConnectionConfiguration(defaultConnectionName); + + if (fileConnectionConfiguration != null && !fileConnectionConfiguration.isEmpty()) { + Properties conectionProperties = new Properties(); + conectionProperties.putAll(fileConnectionConfiguration); + + String url = createUrl(fileConnectionConfiguration); + logger.debug("Url created using parameters from connection configuration file: {}", url); + + if ("oauth".equals(fileConnectionConfiguration.get("authenticator")) + && fileConnectionConfiguration.get("token") == null) { + Path path = + Paths.get( + Optional.ofNullable(fileConnectionConfiguration.get("token_file_path")) + .orElse(SNOWFLAKE_TOKEN_FILE_PATH)); + logger.debug("Token used in connect is read from file: {}", path); + try { + boolean shouldSkipTokenFilePermissionsVerification = + convertSystemGetEnvToBooleanValue(SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION, false); + if (!shouldSkipTokenFilePermissionsVerification) { + verifyFilePermissionSecure(path); + } + String token = new String(Files.readAllBytes(path), Charset.defaultCharset()); + if (!token.isEmpty()) { + putPropertyIfNotNull(conectionProperties, "token", token.trim()); + } else { + logger.warn("The token has empty value"); + } + } catch (Exception ex) { + throw new SnowflakeSQLException(ex, "There is a problem during reading token from file"); + } + } + return new ConnectionParameters(url, conectionProperties); + } else { + return null; + } + } private static Map loadDefaultConnectionConfiguration( String defaultConnectionName) throws SnowflakeSQLException { @@ -88,44 +133,6 @@ private static void verifyFilePermissionSecure(Path configFilePath) } } - public static ConnectionParameters buildConnectionParameters() throws SnowflakeSQLException { - String defaultConnectionName = - Optional.ofNullable(systemGetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY)).orElse(DEFAULT); - Map fileConnectionConfiguration = - loadDefaultConnectionConfiguration(defaultConnectionName); - - if (fileConnectionConfiguration != null && !fileConnectionConfiguration.isEmpty()) { - Properties conectionProperties = new Properties(); - conectionProperties.putAll(fileConnectionConfiguration); - - String url = createUrl(fileConnectionConfiguration); - logger.debug("Url created using parameters from connection configuration file: {}", url); - - if ("oauth".equals(fileConnectionConfiguration.get("authenticator")) - && fileConnectionConfiguration.get("token") == null) { - Path path = - Paths.get( - Optional.ofNullable(fileConnectionConfiguration.get("token_file_path")) - .orElse(SNOWFLAKE_TOKEN_FILE_PATH)); - logger.debug("Token used in connect is read from file: {}", path); - try { - verifyFilePermissionSecure(path); - String token = new String(Files.readAllBytes(path), Charset.defaultCharset()); - if (!token.isEmpty()) { - putPropertyIfNotNull(conectionProperties, "token", token.trim()); - } else { - logger.warn("The token has empty value"); - } - } catch (Exception ex) { - throw new SnowflakeSQLException(ex, "There is a problem during reading token from file"); - } - } - return new ConnectionParameters(url, conectionProperties); - } else { - return null; - } - } - private static String createUrl(Map fileConnectionConfiguration) throws SnowflakeSQLException { Optional maybeAccount = Optional.ofNullable(fileConnectionConfiguration.get("account")); diff --git a/src/main/java/net/snowflake/client/core/SFSessionProperty.java b/src/main/java/net/snowflake/client/core/SFSessionProperty.java index 97c0adbc2..917ce5629 100644 --- a/src/main/java/net/snowflake/client/core/SFSessionProperty.java +++ b/src/main/java/net/snowflake/client/core/SFSessionProperty.java @@ -112,7 +112,10 @@ public enum SFSessionProperty { HTTP_CLIENT_CONNECTION_TIMEOUT("HTTP_CLIENT_CONNECTION_TIMEOUT", false, Integer.class), - HTTP_CLIENT_SOCKET_TIMEOUT("HTTP_CLIENT_SOCKET_TIMEOUT", false, Integer.class); + HTTP_CLIENT_SOCKET_TIMEOUT("HTTP_CLIENT_SOCKET_TIMEOUT", false, Integer.class), + + SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION( + "SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION", false, Boolean.class); // property key in string private String propertyKey; diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index 635384972..f934b9009 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -837,6 +837,22 @@ public static boolean convertSystemPropertyToBooleanValue( } return defaultValue; } + /** + * Helper function to convert environment variable to boolean + * + * @param Environment property name of the system property + * @param defaultValue default value used + * @return the value of the system property as boolean, else the default value + */ + @SnowflakeJdbcInternalApi + public static boolean convertSystemGetEnvToBooleanValue( + String systemProperty, boolean defaultValue) { + String systemPropertyValue = systemGetEnv(systemProperty); + if (systemPropertyValue != null) { + return Boolean.parseBoolean(systemPropertyValue); + } + return defaultValue; + } @SnowflakeJdbcInternalApi public static T mapSFExceptionToSQLException(ThrowingCallable action) diff --git a/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java b/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java index 01da714e5..07658b9e8 100644 --- a/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java +++ b/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java @@ -1,5 +1,6 @@ package net.snowflake.client.config; +import static net.snowflake.client.config.SFConnectionConfigParser.SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION; import static net.snowflake.client.config.SFConnectionConfigParser.SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY; import static net.snowflake.client.config.SFConnectionConfigParser.SNOWFLAKE_HOME_KEY; import static org.junit.Assert.assertEquals; @@ -44,6 +45,7 @@ public void setUp() throws IOException { public void close() throws IOException { SnowflakeUtil.systemUnsetEnv(SNOWFLAKE_HOME_KEY); SnowflakeUtil.systemUnsetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY); + SnowflakeUtil.systemUnsetEnv(SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION); Files.walk(tempPath).map(Path::toFile).forEach(File::delete); Files.delete(tempPath); } @@ -103,6 +105,21 @@ public void testThrowErrorWhenWrongPermissionsForTokenFile() throws IOException SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters()); } + @Test + public void testNoThrowErrorWhenWrongPermissionsForTokenFileButSkippingFlagIsEnabled() + throws SnowflakeSQLException, IOException { + SnowflakeUtil.systemSetEnv(SNOWFLAKE_HOME_KEY, tempPath.toString()); + SnowflakeUtil.systemSetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY, "default"); + SnowflakeUtil.systemSetEnv(SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION, "true"); + File tokenFile = new File(Paths.get(tempPath.toString(), "token").toUri()); + prepareConnectionConfigurationTomlFile( + Collections.singletonMap("token_file_path", tokenFile.toString()), true, false); + + ConnectionParameters data = SFConnectionConfigParser.buildConnectionParameters(); + assertNotNull(data); + assertEquals(tokenFile.toString(), data.getParams().get("token_file_path")); + } + @Test public void testLoadSFConnectionConfigWithHostConfigured() throws SnowflakeSQLException, IOException {