Skip to content

Commit 403c651

Browse files
sfc-gh-pcyreksfc-gh-dheymansfc-gh-akolodziejczyksfc-gh-pmotacki
authored
SNOW-1874066: Adding Soteria tests for JDBC (#2026)
Co-authored-by: Dawid Heyman <[email protected]> Co-authored-by: Adam Kolodziejczyk <[email protected]> Co-authored-by: Przemyslaw Motacki <[email protected]>
1 parent 485e690 commit 403c651

18 files changed

+579
-29
lines changed
Binary file not shown.

TestOnly/pom.xml

+2
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@
414414
<exclude>**/ResultSetAsyncIT.java</exclude>
415415
<!-- ResultSet serialization is new -->
416416
<exclude>**/SnowflakeResultSetSerializable*IT.java</exclude>
417+
<!-- AuthTestHelper is a new class for authentication tests -->
418+
<exclude>**/AuthTestHelper.java</exclude>
417419
</testExcludes>
418420
<useIncrementalCompilation>false</useIncrementalCompilation>
419421
</configuration>

ci/container/test_authentication.sh

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ eval $(jq -r '.authtestparams | to_entries | map("export \(.key)=\(.value|tostri
1212
export SF_ENABLE_EXPERIMENTAL_AUTHENTICATION=true
1313

1414
$MVNW_EXE -DjenkinsIT \
15+
-Dnet.snowflake.jdbc.temporaryCredentialCacheDir=/mnt/workspace/abc \
16+
-Dnet.snowflake.jdbc.ocspResponseCacheDir=/mnt/workspace/abc \
1517
-Djava.io.tmpdir=$WORKSPACE \
1618
-Djacoco.skip.instrument=true \
1719
-Dskip.unitTests=true \

ci/test_authentication.sh

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
11
#!/bin/bash -e
22

33
set -o pipefail
4-
THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4+
export THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
55
export WORKSPACE=${WORKSPACE:-/tmp}
66
export INTERNAL_REPO=nexus.int.snowflakecomputing.com:8086
77

8-
source $THIS_DIR/scripts/login_internal_docker.sh
8+
9+
CI_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
10+
if [[ -n "$JENKINS_HOME" ]]; then
11+
ROOT_DIR="$(cd "${CI_DIR}/.." && pwd)"
12+
export WORKSPACE=${WORKSPACE:-/tmp}
13+
14+
source $CI_DIR/_init.sh
15+
source $CI_DIR/scripts/login_internal_docker.sh
16+
17+
echo "Use /sbin/ip"
18+
IP_ADDR=$(/sbin/ip -4 addr show scope global dev eth0 | grep inet | awk '{print $2}' | cut -d / -f 1)
19+
20+
fi
21+
922
gpg --quiet --batch --yes --decrypt --passphrase="$PARAMETERS_SECRET" --output $THIS_DIR/../.github/workflows/parameters_aws_auth_tests.json "$THIS_DIR/../.github/workflows/parameters_aws_auth_tests.json.gpg"
1023

1124
docker run \
1225
-v $(cd $THIS_DIR/.. && pwd):/mnt/host \
1326
-v $WORKSPACE:/mnt/workspace \
1427
--rm \
15-
nexus.int.snowflakecomputing.com:8086/docker/snowdrivers-test-external-browser-jdbc:1 \
28+
nexus.int.snowflakecomputing.com:8086/docker/snowdrivers-test-external-browser-jdbc:2 \
1629
"/mnt/host/ci/container/test_authentication.sh"

src/main/java/net/snowflake/client/core/CredentialManager.java

+15-11
Original file line numberDiff line numberDiff line change
@@ -280,28 +280,32 @@ static void deleteMfaTokenCache(String host, String user) {
280280
getInstance().deleteTemporaryCredential(host, user, CachedCredentialType.MFA_TOKEN);
281281
}
282282

283+
/** Delete the Oauth access token cache */
284+
static void deleteOAuthAccessTokenCache(String host, String user) {
285+
logger.debug(
286+
"Removing cached mfa token from a secure storage for user: {}, host: {}", user, host);
287+
getInstance().deleteTemporaryCredential(host, user, CachedCredentialType.OAUTH_ACCESS_TOKEN);
288+
}
289+
283290
/** Delete the OAuth access token cache */
284291
static void deleteOAuthAccessTokenCache(SFLoginInput loginInput) throws SFException {
285292
String host = getHostForOAuthCacheKey(loginInput);
286-
logger.debug(
287-
"Removing cached OAuth access token from a secure storage for user: {}, host: {}",
288-
loginInput.getUserName(),
289-
host);
290-
getInstance()
291-
.deleteTemporaryCredential(
292-
host, loginInput.getUserName(), CachedCredentialType.OAUTH_ACCESS_TOKEN);
293+
deleteOAuthAccessTokenCache(host, loginInput.getUserName());
293294
}
294295

295296
/** Delete the OAuth refresh token cache */
296297
static void deleteOAuthRefreshTokenCache(SFLoginInput loginInput) throws SFException {
297298
String host = getHostForOAuthCacheKey(loginInput);
299+
deleteOAuthRefreshTokenCache(host, loginInput.getUserName());
300+
}
301+
302+
/** Delete the Oauth refresh token cache */
303+
static void deleteOAuthRefreshTokenCache(String host, String user) {
298304
logger.debug(
299305
"Removing cached OAuth refresh token from a secure storage for user: {}, host: {}",
300-
loginInput.getUserName(),
306+
user,
301307
host);
302-
getInstance()
303-
.deleteTemporaryCredential(
304-
host, loginInput.getUserName(), CachedCredentialType.OAUTH_REFRESH_TOKEN);
308+
getInstance().deleteTemporaryCredential(host, user, CachedCredentialType.OAUTH_REFRESH_TOKEN);
305309
}
306310

307311
/**

src/main/java/net/snowflake/client/core/SFLoginInput.java

+34-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ public class SFLoginInput {
5959
private boolean enableClientStoreTemporaryCredential;
6060
private boolean enableClientRequestMfaToken;
6161

62+
// OAuth
63+
private int redirectUriPort = -1;
64+
private String clientId;
65+
private String clientSecret;
66+
6267
private Duration browserResponseTimeout;
6368

6469
// Additional headers to add for Snowsight.
@@ -426,13 +431,13 @@ SFLoginInput setHttpClientSettingsKey(HttpClientSettingsKey key) {
426431
this.httpClientKey = key;
427432
return this;
428433
}
429-
// Opaque string sent for Snowsight account activation
430434

435+
// Opaque string sent for Snowsight account activation
431436
String getInFlightCtx() {
432437
return inFlightCtx;
433438
}
434-
// Opaque string sent for Snowsight account activation
435439

440+
// Opaque string sent for Snowsight account activation
436441
SFLoginInput setInFlightCtx(String inFlightCtx) {
437442
this.inFlightCtx = inFlightCtx;
438443
return this;
@@ -447,6 +452,33 @@ SFLoginInput setDisableSamlURLCheck(boolean disableSamlURLCheck) {
447452
return this;
448453
}
449454

455+
public int getRedirectUriPort() {
456+
return redirectUriPort;
457+
}
458+
459+
public SFLoginInput setRedirectUriPort(int redirectUriPort) {
460+
this.redirectUriPort = redirectUriPort;
461+
return this;
462+
}
463+
464+
public String getClientId() {
465+
return clientId;
466+
}
467+
468+
public SFLoginInput setClientId(String clientId) {
469+
this.clientId = clientId;
470+
return this;
471+
}
472+
473+
public String getClientSecret() {
474+
return clientSecret;
475+
}
476+
477+
public SFLoginInput setClientSecret(String clientSecret) {
478+
this.clientSecret = clientSecret;
479+
return this;
480+
}
481+
450482
Map<String, String> getAdditionalHttpHeadersForSnowsight() {
451483
return additionalHttpHeadersForSnowsight;
452484
}

src/main/java/net/snowflake/client/core/SFSession.java

+5
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,11 @@ public String getIdToken() {
12591259
return idToken;
12601260
}
12611261

1262+
@SnowflakeJdbcInternalApi
1263+
public String getAccessToken() {
1264+
return oauthAccessToken;
1265+
}
1266+
12621267
public String getMfaToken() {
12631268
return mfaToken;
12641269
}

src/main/java/net/snowflake/client/core/SessionUtil.java

+22
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,28 @@ public static void deleteIdTokenCache(String host, String user) {
10901090
CredentialManager.deleteIdTokenCache(host, user);
10911091
}
10921092

1093+
/**
1094+
* Delete the Oauth access token cache
1095+
*
1096+
* @param host The host string
1097+
* @param user The user
1098+
*/
1099+
@SnowflakeJdbcInternalApi
1100+
public static void deleteOAuthAccessTokenCache(String host, String user) {
1101+
CredentialManager.deleteOAuthAccessTokenCache(host, user);
1102+
}
1103+
1104+
/**
1105+
* Delete the Oauth refresh token cache
1106+
*
1107+
* @param host The host string
1108+
* @param user The user
1109+
*/
1110+
@SnowflakeJdbcInternalApi
1111+
public static void deleteOAuthRefreshTokenCache(String host, String user) {
1112+
CredentialManager.deleteOAuthRefreshTokenCache(host, user);
1113+
}
1114+
10931115
/**
10941116
* Delete the mfa token cache
10951117
*

src/test/java/net/snowflake/client/authentication/AuthConnectionParameters.java

+65
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ public class AuthConnectionParameters {
99
static final String SSO_USER = systemGetEnv("SNOWFLAKE_AUTH_TEST_BROWSER_USER");
1010
static final String HOST = systemGetEnv("SNOWFLAKE_AUTH_TEST_HOST");
1111
static final String SSO_PASSWORD = systemGetEnv("SNOWFLAKE_AUTH_TEST_OKTA_PASS");
12+
static final String OKTA = systemGetEnv("SNOWFLAKE_AUTH_TEST_OKTA_NAME");
13+
static final String OAUTH_PASSWORD =
14+
systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_USER_PASSWORD");
1215

1316
static Properties getBaseConnectionParameters() {
1417
Properties properties = new Properties();
@@ -19,6 +22,7 @@ static Properties getBaseConnectionParameters() {
1922
properties.put("db", systemGetEnv("SNOWFLAKE_AUTH_TEST_DATABASE"));
2023
properties.put("schema", systemGetEnv("SNOWFLAKE_AUTH_TEST_SCHEMA"));
2124
properties.put("warehouse", systemGetEnv("SNOWFLAKE_AUTH_TEST_WAREHOUSE"));
25+
properties.put("CLIENT_STORE_TEMPORARY_CREDENTIAL", false);
2226
return properties;
2327
}
2428

@@ -50,4 +54,65 @@ static Properties getOauthConnectionParameters(String token) {
5054
properties.put("token", token);
5155
return properties;
5256
}
57+
58+
static Properties getOAuthExternalAuthorizationCodeConnectionParameters() {
59+
Properties properties = getBaseConnectionParameters();
60+
properties.put("authenticator", "OAUTH_AUTHORIZATION_CODE");
61+
properties.put(
62+
"oauthClientId", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
63+
properties.put(
64+
"oauthClientSecret", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_SECRET"));
65+
properties.put(
66+
"oauthRedirectURI", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_REDIRECT_URI"));
67+
properties.put(
68+
"oauthAuthorizationUrl", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_AUTH_URL"));
69+
properties.put(
70+
"oauthTokenRequestUrl", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_TOKEN"));
71+
properties.put("user", SSO_USER);
72+
73+
return properties;
74+
}
75+
76+
static Properties getOAuthSnowflakeAuthorizationCodeConnectionParameters() {
77+
Properties properties = getBaseConnectionParameters();
78+
properties.put("authenticator", "OAUTH_AUTHORIZATION_CODE");
79+
properties.put(
80+
"oauthClientId", systemGetEnv("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_CLIENT_ID"));
81+
properties.put(
82+
"oauthClientSecret",
83+
systemGetEnv("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_CLIENT_SECRET"));
84+
properties.put(
85+
"oauthRedirectURI",
86+
systemGetEnv("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_REDIRECT_URI"));
87+
properties.put("role", systemGetEnv("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_ROLE"));
88+
properties.put("user", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
89+
return properties;
90+
}
91+
92+
static Properties getOAuthSnowflakeWildcardsAuthorizationCodeConnectionParameters() {
93+
Properties properties = getBaseConnectionParameters();
94+
properties.put("authenticator", "OAUTH_AUTHORIZATION_CODE");
95+
properties.put(
96+
"oauthClientId",
97+
systemGetEnv("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_WILDCARDS_CLIENT_ID"));
98+
properties.put(
99+
"oauthClientSecret",
100+
systemGetEnv("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_WILDCARDS_CLIENT_SECRET"));
101+
properties.put("role", systemGetEnv("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_ROLE"));
102+
properties.put("user", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
103+
return properties;
104+
}
105+
106+
static Properties getOAuthSnowflakeClientCredentialParameters() {
107+
Properties properties = getBaseConnectionParameters();
108+
properties.put("authenticator", "OAUTH_CLIENT_CREDENTIALS");
109+
properties.put(
110+
"oauthClientId", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
111+
properties.put(
112+
"oauthClientSecret", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_SECRET"));
113+
properties.put(
114+
"oauthTokenRequestUrl", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_TOKEN"));
115+
properties.put("user", systemGetEnv("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
116+
return properties;
117+
}
53118
}

src/test/java/net/snowflake/client/authentication/AuthTestHelper.java

+31-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class AuthTestHelper {
2222

2323
private Exception exception;
2424
private String idToken;
25+
private String accessToken;
2526
private final boolean runAuthTestsManually;
2627

2728
public AuthTestHelper() {
@@ -87,17 +88,36 @@ public static void deleteIdToken() {
8788
AuthConnectionParameters.HOST, AuthConnectionParameters.SSO_USER);
8889
}
8990

91+
public static void deleteIdToken(String host, String user) {
92+
SessionUtil.deleteIdTokenCache(host, user);
93+
}
94+
95+
public static void deleteOauthToken() {
96+
SessionUtil.deleteOAuthAccessTokenCache(
97+
AuthConnectionParameters.OKTA, AuthConnectionParameters.SSO_USER);
98+
}
99+
100+
public static void deleteOauthToken(String host, String user) {
101+
SessionUtil.deleteOAuthAccessTokenCache(host, user);
102+
}
103+
104+
public static void deleteOauthRefreshToken(String host, String user) {
105+
SessionUtil.deleteOAuthRefreshTokenCache(host, user);
106+
}
107+
90108
public void connectAndExecuteSimpleQuery(Properties props, String sessionParameters) {
91-
String url = String.format("jdbc:snowflake://%s:%s", props.get("host"), props.get("port"));
109+
String url = String.format("jdbc:snowflake://%s", props.get("host"));
92110
if (sessionParameters != null) {
93111
url += "?" + sessionParameters;
94112
}
95113
try (Connection con = DriverManager.getConnection(url, props);
96114
Statement stmt = con.createStatement();
97115
ResultSet rs = stmt.executeQuery("select 1")) {
98116
assertTrue(rs.next());
99-
assertEquals(1, rs.getInt(1));
117+
int value = rs.getInt(1);
118+
assertEquals(1, value);
100119
saveToken(con);
120+
saveAccessToken(con);
101121
} catch (SQLException e) {
102122
this.exception = e;
103123
}
@@ -108,7 +128,16 @@ private void saveToken(Connection con) throws SnowflakeSQLException {
108128
this.idToken = sfcon.getSfSession().getIdToken();
109129
}
110130

131+
private void saveAccessToken(Connection con) throws SnowflakeSQLException {
132+
SnowflakeConnectionV1 sfcon = (SnowflakeConnectionV1) con;
133+
this.accessToken = sfcon.getSfSession().getAccessToken();
134+
}
135+
111136
public String getIdToken() {
112137
return idToken;
113138
}
139+
140+
public String getAccessToken() {
141+
return accessToken;
142+
}
114143
}

src/test/java/net/snowflake/client/authentication/ExternalBrowserLatestIT.java

+5-6
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ class ExternalBrowserLatestIT {
1616
String login = AuthConnectionParameters.SSO_USER;
1717
String password = AuthConnectionParameters.SSO_PASSWORD;
1818
AuthTestHelper authTestHelper = new AuthTestHelper();
19+
Properties properties;
1920

2021
@BeforeEach
2122
public void setUp() throws IOException {
2223
AuthTestHelper.deleteIdToken();
24+
properties = getExternalBrowserConnectionParameters();
2325
}
2426

2527
@AfterEach
@@ -32,17 +34,14 @@ public void tearDown() {
3234
void shouldAuthenticateUsingExternalBrowser() throws InterruptedException {
3335
Thread provideCredentialsThread =
3436
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
35-
Thread connectThread =
36-
authTestHelper.getConnectAndExecuteSimpleQueryThread(
37-
getExternalBrowserConnectionParameters());
37+
Thread connectThread = authTestHelper.getConnectAndExecuteSimpleQueryThread(properties);
3838

3939
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
4040
authTestHelper.verifyExceptionIsNotThrown();
4141
}
4242

4343
@Test
4444
void shouldThrowErrorForMismatchedUsername() throws InterruptedException {
45-
Properties properties = getExternalBrowserConnectionParameters();
4645
properties.put("user", "differentUsername");
4746
Thread provideCredentialsThread =
4847
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
@@ -61,7 +60,7 @@ void shouldThrowErrorForWrongCredentials() throws InterruptedException {
6160
new Thread(() -> authTestHelper.provideCredentials("fail", login, password));
6261
Thread connectThread =
6362
authTestHelper.getConnectAndExecuteSimpleQueryThread(
64-
getExternalBrowserConnectionParameters(), "BROWSER_RESPONSE_TIMEOUT=10");
63+
properties, "BROWSER_RESPONSE_TIMEOUT=10");
6564

6665
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
6766
authTestHelper.verifyExceptionIsThrown(
@@ -74,7 +73,7 @@ void shouldThrowErrorForBrowserTimeout() throws InterruptedException {
7473
new Thread(() -> authTestHelper.provideCredentials("timeout", login, password));
7574
Thread connectThread =
7675
authTestHelper.getConnectAndExecuteSimpleQueryThread(
77-
getExternalBrowserConnectionParameters(), "BROWSER_RESPONSE_TIMEOUT=1");
76+
properties, "BROWSER_RESPONSE_TIMEOUT=1");
7877

7978
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
8079
authTestHelper.verifyExceptionIsThrown(

0 commit comments

Comments
 (0)