Skip to content

Commit

Permalink
SNOW-1689931 Adding flag to skip token file permission verification (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-pmotacki authored Nov 26, 2024
1 parent f1038e6 commit 7fd70cd
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -34,6 +35,53 @@ 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<String, String> fileConnectionConfiguration =
loadDefaultConnectionConfiguration(defaultConnectionName);

if (fileConnectionConfiguration != null && !fileConnectionConfiguration.isEmpty()) {
Properties connectionProperties = new Properties();
connectionProperties.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);
} else {
logger.debug("Skip token file permissions verification");
}
String token = new String(Files.readAllBytes(path), Charset.defaultCharset());
if (!token.isEmpty()) {
putPropertyIfNotNull(connectionProperties, "token", token.trim());
} else {
throw new SnowflakeSQLException(
"Non-empty token must be set when the authenticator type is OAUTH");
}
} catch (Exception ex) {
throw new SnowflakeSQLException(ex, "There is a problem during reading token from file");
}
}
return new ConnectionParameters(url, connectionProperties);
} else {
return null;
}
}

private static Map<String, String> loadDefaultConnectionConfiguration(
String defaultConnectionName) throws SnowflakeSQLException {
Expand Down Expand Up @@ -88,44 +136,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<String, String> 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<String, String> fileConnectionConfiguration)
throws SnowflakeSQLException {
Optional<String> maybeAccount = Optional.ofNullable(fileConnectionConfiguration.get("account"));
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,22 @@ public static boolean convertSystemPropertyToBooleanValue(
}
return defaultValue;
}
/**
* Helper function to convert environment variable to boolean
*
* @param envVariableKey property name of the environment variable
* @param defaultValue default value used
* @return the value of the environment variable as boolean, else the default value
*/
@SnowflakeJdbcInternalApi
public static boolean convertSystemGetEnvToBooleanValue(
String envVariableKey, boolean defaultValue) {
String environmentVariableValue = systemGetEnv(envVariableKey);
if (environmentVariableValue != null) {
return Boolean.parseBoolean(environmentVariableValue);
}
return defaultValue;
}

@SnowflakeJdbcInternalApi
public static <T> T mapSFExceptionToSQLException(ThrowingCallable<T, SFException> action)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,8 +18,11 @@
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.snowflake.client.RunningNotOnLinuxMac;
Expand All @@ -32,20 +36,36 @@

public class SFConnectionConfigParserTest {

private static final List<String> ENV_VARIABLES_KEYS =
new ArrayList<>(
Arrays.asList(
SNOWFLAKE_HOME_KEY,
SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY,
SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION));
private Path tempPath = null;
private TomlMapper tomlMapper = new TomlMapper();
private Map<String, String> envVariables = new HashMap();

@Before
public void setUp() throws IOException {
tempPath = Files.createTempDirectory(".snowflake");
ENV_VARIABLES_KEYS.stream()
.forEach(
key -> {
if (SnowflakeUtil.systemGetEnv(key) != null) {
envVariables.put(key, SnowflakeUtil.systemGetEnv(key));
}
});
}

@After
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);
envVariables.forEach((key, value) -> SnowflakeUtil.systemSetEnv(key, value));
}

@Test
Expand Down Expand Up @@ -103,6 +123,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 {
Expand Down Expand Up @@ -133,6 +168,19 @@ public void shouldThrowExceptionIfNoneOfHostAndAccountIsSet() throws IOException
SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters());
}

@Test
public void shouldThrowExceptionIfTokenIsNotSetForOauth() throws 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, "");

Assert.assertThrows(
SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters());
}

private void prepareConnectionConfigurationTomlFile() throws IOException {
prepareConnectionConfigurationTomlFile(null, true, true);
}
Expand All @@ -144,6 +192,16 @@ private void prepareConnectionConfigurationTomlFile(Map moreParameters) throws I
private void prepareConnectionConfigurationTomlFile(
Map moreParameters, boolean onlyUserPermissionConnection, boolean onlyUserPermissionToken)
throws IOException {
prepareConnectionConfigurationTomlFile(
moreParameters, onlyUserPermissionConnection, onlyUserPermissionToken, "token_from_file");
}

private void prepareConnectionConfigurationTomlFile(
Map moreParameters,
boolean onlyUserPermissionConnection,
boolean onlyUserPermissionToken,
String token)
throws IOException {
Path path = Paths.get(tempPath.toString(), "connections.toml");
Path filePath = createFilePathWithPermission(path, onlyUserPermissionConnection);
File file = filePath.toFile();
Expand All @@ -166,7 +224,16 @@ private void prepareConnectionConfigurationTomlFile(
createFilePathWithPermission(
Paths.get(configurationParams.get("token_file_path").toString()),
onlyUserPermissionToken);
Files.write(tokenFilePath, "token_from_file".getBytes());
Files.write(tokenFilePath, token.getBytes());
Path emptyTokenFilePath =
createFilePathWithPermission(
Paths.get(
configurationParams
.get("token_file_path")
.toString()
.replaceAll("token", "emptytoken")),
onlyUserPermissionToken);
Files.write(emptyTokenFilePath, "".getBytes());
}
}

Expand Down

0 comments on commit 7fd70cd

Please sign in to comment.