Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-1689931 Adding flag to skip token file permission verification #1959

Merged
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) {
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved
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());
sfc-gh-pfus marked this conversation as resolved.
Show resolved Hide resolved
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
Loading