Skip to content

Commit b3a2763

Browse files
SNOW-1739611 add oauth and okta automated tests (#1994)
1 parent 871df20 commit b3a2763

8 files changed

+231
-78
lines changed

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

+16
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,20 @@ static Properties getStoreIDTokenConnectionParameters() {
3434
properties.put("CLIENT_STORE_TEMPORARY_CREDENTIAL", true);
3535
return properties;
3636
}
37+
38+
static Properties getOktaConnectionParameters() {
39+
Properties properties = getBaseConnectionParameters();
40+
properties.put("user", SSO_USER);
41+
properties.put("password", SSO_PASSWORD);
42+
properties.put("authenticator", systemGetEnv("SNOWFLAKE_AUTH_TEST_OAUTH_URL"));
43+
return properties;
44+
}
45+
46+
static Properties getOauthConnectionParameters(String token) {
47+
Properties properties = getBaseConnectionParameters();
48+
properties.put("user", SSO_USER);
49+
properties.put("authenticator", "OAUTH");
50+
properties.put("token", token);
51+
return properties;
52+
}
3753
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
import net.snowflake.client.jdbc.SnowflakeConnectionV1;
1919
import net.snowflake.client.jdbc.SnowflakeSQLException;
2020

21-
public class AuthTest {
21+
public class AuthTestHelper {
2222

2323
private Exception exception;
2424
private String idToken;
2525
private final boolean runAuthTestsManually;
2626

27-
public AuthTest() {
27+
public AuthTestHelper() {
2828
this.runAuthTestsManually = Boolean.parseBoolean(System.getenv("RUN_AUTH_TESTS_MANUALLY"));
2929
}
3030

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

+22-21
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,45 @@
1111
import org.junit.jupiter.api.Test;
1212

1313
@Tag(TestTags.AUTHENTICATION)
14-
class ExternalBrowserIT {
14+
class ExternalBrowserLatestIT {
1515

1616
String login = AuthConnectionParameters.SSO_USER;
1717
String password = AuthConnectionParameters.SSO_PASSWORD;
18-
AuthTest authTest = new AuthTest();
18+
AuthTestHelper authTestHelper = new AuthTestHelper();
1919

2020
@BeforeEach
2121
public void setUp() throws IOException {
22-
AuthTest.deleteIdToken();
22+
AuthTestHelper.deleteIdToken();
2323
}
2424

2525
@AfterEach
2626
public void tearDown() {
27-
authTest.cleanBrowserProcesses();
28-
AuthTest.deleteIdToken();
27+
authTestHelper.cleanBrowserProcesses();
28+
AuthTestHelper.deleteIdToken();
2929
}
3030

3131
@Test
3232
void shouldAuthenticateUsingExternalBrowser() throws InterruptedException {
3333
Thread provideCredentialsThread =
34-
new Thread(() -> authTest.provideCredentials("success", login, password));
34+
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
3535
Thread connectThread =
36-
authTest.getConnectAndExecuteSimpleQueryThread(getExternalBrowserConnectionParameters());
36+
authTestHelper.getConnectAndExecuteSimpleQueryThread(
37+
getExternalBrowserConnectionParameters());
3738

38-
authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
39-
authTest.verifyExceptionIsNotThrown();
39+
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
40+
authTestHelper.verifyExceptionIsNotThrown();
4041
}
4142

4243
@Test
4344
void shouldThrowErrorForMismatchedUsername() throws InterruptedException {
4445
Properties properties = getExternalBrowserConnectionParameters();
4546
properties.put("user", "differentUsername");
4647
Thread provideCredentialsThread =
47-
new Thread(() -> authTest.provideCredentials("success", login, password));
48-
Thread connectThread = authTest.getConnectAndExecuteSimpleQueryThread(properties);
48+
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
49+
Thread connectThread = authTestHelper.getConnectAndExecuteSimpleQueryThread(properties);
4950

50-
authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
51-
authTest.verifyExceptionIsThrown(
51+
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
52+
authTestHelper.verifyExceptionIsThrown(
5253
"The user you were trying to authenticate as differs from the user currently logged in at the IDP.");
5354
}
5455

@@ -57,26 +58,26 @@ void shouldThrowErrorForWrongCredentials() throws InterruptedException {
5758
String login = "itsnotanaccount.com";
5859
String password = "fakepassword";
5960
Thread provideCredentialsThread =
60-
new Thread(() -> authTest.provideCredentials("fail", login, password));
61+
new Thread(() -> authTestHelper.provideCredentials("fail", login, password));
6162
Thread connectThread =
62-
authTest.getConnectAndExecuteSimpleQueryThread(
63+
authTestHelper.getConnectAndExecuteSimpleQueryThread(
6364
getExternalBrowserConnectionParameters(), "BROWSER_RESPONSE_TIMEOUT=10");
6465

65-
authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
66-
authTest.verifyExceptionIsThrown(
66+
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
67+
authTestHelper.verifyExceptionIsThrown(
6768
"JDBC driver encountered communication error. Message: External browser authentication failed within timeout of 10000 milliseconds.");
6869
}
6970

7071
@Test
7172
void shouldThrowErrorForBrowserTimeout() throws InterruptedException {
7273
Thread provideCredentialsThread =
73-
new Thread(() -> authTest.provideCredentials("timeout", login, password));
74+
new Thread(() -> authTestHelper.provideCredentials("timeout", login, password));
7475
Thread connectThread =
75-
authTest.getConnectAndExecuteSimpleQueryThread(
76+
authTestHelper.getConnectAndExecuteSimpleQueryThread(
7677
getExternalBrowserConnectionParameters(), "BROWSER_RESPONSE_TIMEOUT=1");
7778

78-
authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
79-
authTest.verifyExceptionIsThrown(
79+
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
80+
authTestHelper.verifyExceptionIsThrown(
8081
"JDBC driver encountered communication error. Message: External browser authentication failed within timeout of 1000 milliseconds.");
8182
}
8283
}

src/test/java/net/snowflake/client/authentication/IdTokenIT.java src/test/java/net/snowflake/client/authentication/IdTokenLatestIT.java

+17-17
Original file line numberDiff line numberDiff line change
@@ -17,58 +17,58 @@
1717

1818
@Tag(TestTags.AUTHENTICATION)
1919
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
20-
class IdTokenIT {
20+
class IdTokenLatestIT {
2121

2222
String login = AuthConnectionParameters.SSO_USER;
2323
String password = AuthConnectionParameters.SSO_PASSWORD;
24-
AuthTest authTest = new AuthTest();
24+
AuthTestHelper authTestHelper = new AuthTestHelper();
2525
private static String firstToken;
2626

2727
@BeforeAll
2828
public static void globalSetUp() {
29-
AuthTest.deleteIdToken();
29+
AuthTestHelper.deleteIdToken();
3030
}
3131

3232
@AfterEach
3333
public void tearDown() {
34-
authTest.cleanBrowserProcesses();
34+
authTestHelper.cleanBrowserProcesses();
3535
}
3636

3737
@Test
3838
@Order(1)
3939
void shouldAuthenticateUsingExternalBrowserAndSaveToken() throws InterruptedException {
4040
Thread provideCredentialsThread =
41-
new Thread(() -> authTest.provideCredentials("success", login, password));
41+
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
4242
Thread connectThread =
43-
authTest.getConnectAndExecuteSimpleQueryThread(getStoreIDTokenConnectionParameters());
43+
authTestHelper.getConnectAndExecuteSimpleQueryThread(getStoreIDTokenConnectionParameters());
4444

45-
authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
46-
authTest.verifyExceptionIsNotThrown();
47-
firstToken = authTest.getIdToken();
45+
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
46+
authTestHelper.verifyExceptionIsNotThrown();
47+
firstToken = authTestHelper.getIdToken();
4848
assertThat("Id token was not saved", firstToken, notNullValue());
4949
}
5050

5151
@Test
5252
@Order(2)
5353
void shouldAuthenticateUsingTokenWithoutBrowser() {
5454
verifyFirstTokenWasSaved();
55-
authTest.connectAndExecuteSimpleQuery(getStoreIDTokenConnectionParameters(), null);
56-
authTest.verifyExceptionIsNotThrown();
55+
authTestHelper.connectAndExecuteSimpleQuery(getStoreIDTokenConnectionParameters(), null);
56+
authTestHelper.verifyExceptionIsNotThrown();
5757
}
5858

5959
@Test
6060
@Order(3)
6161
void shouldOpenBrowserAgainWhenTokenIsDeleted() throws InterruptedException {
6262
verifyFirstTokenWasSaved();
63-
AuthTest.deleteIdToken();
63+
AuthTestHelper.deleteIdToken();
6464
Thread provideCredentialsThread =
65-
new Thread(() -> authTest.provideCredentials("success", login, password));
65+
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
6666
Thread connectThread =
67-
authTest.getConnectAndExecuteSimpleQueryThread(getStoreIDTokenConnectionParameters());
67+
authTestHelper.getConnectAndExecuteSimpleQueryThread(getStoreIDTokenConnectionParameters());
6868

69-
authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
70-
authTest.verifyExceptionIsNotThrown();
71-
String secondToken = authTest.getIdToken();
69+
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
70+
authTestHelper.verifyExceptionIsNotThrown();
71+
String secondToken = authTestHelper.getIdToken();
7272
assertThat("Id token was not saved", secondToken, notNullValue());
7373
assertThat("Id token was not updated", secondToken, not(firstToken));
7474
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package net.snowflake.client.authentication;
2+
3+
import static net.snowflake.client.authentication.AuthConnectionParameters.getOauthConnectionParameters;
4+
import static org.hamcrest.CoreMatchers.is;
5+
import static org.hamcrest.MatcherAssert.assertThat;
6+
7+
import com.fasterxml.jackson.databind.JsonNode;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import java.io.DataOutputStream;
10+
import java.io.IOException;
11+
import java.io.InputStream;
12+
import java.net.HttpURLConnection;
13+
import java.net.URL;
14+
import java.nio.charset.StandardCharsets;
15+
import java.util.Base64;
16+
import java.util.List;
17+
import java.util.Properties;
18+
import java.util.stream.Collectors;
19+
import java.util.stream.Stream;
20+
import net.snowflake.client.category.TestTags;
21+
import org.junit.jupiter.api.BeforeEach;
22+
import org.junit.jupiter.api.Tag;
23+
import org.junit.jupiter.api.Test;
24+
25+
@Tag(TestTags.AUTHENTICATION)
26+
public class OauthLatestIT {
27+
28+
AuthTestHelper authTestHelper;
29+
30+
@BeforeEach
31+
public void setUp() throws IOException {
32+
authTestHelper = new AuthTestHelper();
33+
}
34+
35+
@Test
36+
void shouldAuthenticateUsingOauth() throws IOException {
37+
authTestHelper.connectAndExecuteSimpleQuery(getOauthConnectionParameters(getToken()), null);
38+
authTestHelper.verifyExceptionIsNotThrown();
39+
}
40+
41+
@Test
42+
void shouldThrowErrorForInvalidToken() {
43+
authTestHelper.connectAndExecuteSimpleQuery(getOauthConnectionParameters("invalidToken"), null);
44+
authTestHelper.verifyExceptionIsThrown("Invalid OAuth access token. ");
45+
}
46+
47+
@Test
48+
void shouldThrowErrorForMismatchedOauthUsername() throws IOException {
49+
Properties properties = getOauthConnectionParameters(getToken());
50+
properties.put("user", "differentUsername");
51+
authTestHelper.connectAndExecuteSimpleQuery(properties, null);
52+
authTestHelper.verifyExceptionIsThrown(
53+
"The user you were trying to authenticate as differs from the user tied to the access token.");
54+
}
55+
56+
private String getToken() throws IOException {
57+
List<String> data =
58+
Stream.of(
59+
"username=" + System.getenv("SNOWFLAKE_AUTH_TEST_OKTA_USER"),
60+
"password=" + System.getenv("SNOWFLAKE_AUTH_TEST_OKTA_PASS"),
61+
"grant_type=password",
62+
"scope=session:role:" + System.getenv("SNOWFLAKE_AUTH_TEST_ROLE").toLowerCase())
63+
.collect(Collectors.toList());
64+
65+
String auth =
66+
System.getenv("SNOWFLAKE_AUTH_TEST_OAUTH_CLIENT_ID")
67+
+ ":"
68+
+ System.getenv("SNOWFLAKE_AUTH_TEST_OAUTH_CLIENT_SECRET");
69+
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
70+
71+
URL url = new URL(System.getenv("SNOWFLAKE_AUTH_TEST_OAUTH_URL"));
72+
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
73+
connection.setRequestMethod("POST");
74+
connection.setRequestProperty(
75+
"Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
76+
connection.setRequestProperty("Authorization", "Basic " + encodedAuth);
77+
connection.setDoOutput(true);
78+
79+
try (DataOutputStream out = new DataOutputStream(connection.getOutputStream())) {
80+
out.writeBytes(String.join("&", data));
81+
out.flush();
82+
}
83+
84+
int responseCode = connection.getResponseCode();
85+
assertThat("Failed to get access token, response code: " + responseCode, responseCode, is(200));
86+
87+
ObjectMapper mapper = new ObjectMapper();
88+
JsonNode jsonNode;
89+
try (InputStream inputStream = connection.getInputStream()) {
90+
jsonNode = mapper.readTree(inputStream);
91+
}
92+
return jsonNode.get("access_token").asText();
93+
}
94+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package net.snowflake.client.authentication;
2+
3+
import static net.snowflake.client.authentication.AuthConnectionParameters.SSO_USER;
4+
import static net.snowflake.client.authentication.AuthConnectionParameters.getOktaConnectionParameters;
5+
6+
import java.io.IOException;
7+
import java.util.Properties;
8+
import net.snowflake.client.category.TestTags;
9+
import org.junit.jupiter.api.BeforeEach;
10+
import org.junit.jupiter.api.Disabled;
11+
import org.junit.jupiter.api.Tag;
12+
import org.junit.jupiter.api.Test;
13+
14+
@Tag(TestTags.AUTHENTICATION)
15+
class OktaAuthLatestIT {
16+
17+
AuthTestHelper authTestHelper;
18+
19+
@BeforeEach
20+
public void setUp() throws IOException {
21+
authTestHelper = new AuthTestHelper();
22+
}
23+
24+
@Test
25+
void shouldAuthenticateUsingOkta() {
26+
authTestHelper.connectAndExecuteSimpleQuery(getOktaConnectionParameters(), null);
27+
authTestHelper.verifyExceptionIsNotThrown();
28+
}
29+
30+
@Test
31+
void shouldAuthenticateUsingOktaWithOktaUsernameParam() {
32+
Properties properties = getOktaConnectionParameters();
33+
properties.replace("user", "differentUsername");
34+
authTestHelper.connectAndExecuteSimpleQuery(properties, "oktausername=" + SSO_USER);
35+
authTestHelper.verifyExceptionIsNotThrown();
36+
}
37+
38+
@Test
39+
void shouldThrowErrorForWrongOktaCredentials() {
40+
Properties properties = getOktaConnectionParameters();
41+
properties.put("user", "invalidUsername");
42+
properties.put("password", "fakepassword");
43+
authTestHelper.connectAndExecuteSimpleQuery(properties, null);
44+
authTestHelper.verifyExceptionIsThrown(
45+
"JDBC driver encountered communication error. Message: HTTP status=401.");
46+
}
47+
48+
@Test
49+
void shouldThrowErrorForWrongOktaCredentialsInOktaUsernameParam() {
50+
Properties properties = getOktaConnectionParameters();
51+
properties.replace("user", "differentUsername");
52+
authTestHelper.connectAndExecuteSimpleQuery(properties, "oktausername=invalidUser");
53+
authTestHelper.verifyExceptionIsThrown(
54+
"JDBC driver encountered communication error. Message: HTTP status=401.");
55+
}
56+
57+
@Test
58+
void shouldThrowErrorForWrongOktaUrl() {
59+
Properties properties = getOktaConnectionParameters();
60+
properties.put("authenticator", "https://invalid.okta.com/");
61+
authTestHelper.connectAndExecuteSimpleQuery(properties, null);
62+
authTestHelper.verifyExceptionIsThrown(
63+
"The specified authenticator is not accepted by your Snowflake account configuration. Please contact your local system administrator to get the correct URL to use.");
64+
}
65+
66+
@Test
67+
@Disabled // todo SNOW-1852279 implement error handling for invalid URL
68+
void shouldThrowErrorForWrongUrlWithoutOktaPath() {
69+
Properties properties = getOktaConnectionParameters();
70+
properties.put("authenticator", "https://invalid.abc.com/");
71+
authTestHelper.connectAndExecuteSimpleQuery(properties, null);
72+
authTestHelper.verifyExceptionIsThrown("todo");
73+
}
74+
}

src/test/java/net/snowflake/client/core/HttpUtilLatestIT.java

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.apache.http.impl.client.CloseableHttpClient;
1515
import org.hamcrest.CoreMatchers;
1616
import org.hamcrest.MatcherAssert;
17+
import org.junit.jupiter.api.BeforeEach;
1718
import org.junit.jupiter.api.Tag;
1819
import org.junit.jupiter.api.Test;
1920
import org.junit.jupiter.api.Timeout;
@@ -23,6 +24,11 @@ public class HttpUtilLatestIT {
2324

2425
private static final String HANG_WEBSERVER_ADDRESS = "http://localhost:12345/hang";
2526

27+
@BeforeEach
28+
public void resetHttpClientsCache() {
29+
HttpUtil.httpClient.clear();
30+
}
31+
2632
/** Added in > 3.14.5 */
2733
@Test
2834
public void shouldGetDefaultConnectionAndSocketTimeouts() {

0 commit comments

Comments
 (0)