Skip to content

Commit

Permalink
Merge branch 'master' into SNOW-1524152-implement-setQueryTimeout-for…
Browse files Browse the repository at this point in the history
…-async-queries
  • Loading branch information
sfc-gh-ext-simba-jf authored Dec 19, 2024
2 parents 448afaa + b3a2763 commit 41aedbf
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 79 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/jira_issue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
summary: '${{ github.event.issue.title }}'
description: |
${{ github.event.issue.body }} \\ \\ _Created from GitHub Action_ for ${{ github.event.issue.html_url }}
fields: '{ "customfield_11401": {"id": "14723"}, "assignee": {"id": "712020:3c0352b5-63f7-4e26-9afe-38f6f9f0f4c5"}, "components":[{"id":"19281"}] }'
fields: '{ "customfield_11401": {"id": "14723"}, "assignee": {"id": "712020:e1f41916-da57-4fe8-b317-116d5229aa51"}, "components":[{"id":"19281"}], "labels": ["oss"], "priority": {"id": "10001"} }'

- name: Update GitHub Issue
uses: ./jira/gajira-issue-update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,20 @@ static Properties getStoreIDTokenConnectionParameters() {
properties.put("CLIENT_STORE_TEMPORARY_CREDENTIAL", true);
return properties;
}

static Properties getOktaConnectionParameters() {
Properties properties = getBaseConnectionParameters();
properties.put("user", SSO_USER);
properties.put("password", SSO_PASSWORD);
properties.put("authenticator", systemGetEnv("SNOWFLAKE_AUTH_TEST_OAUTH_URL"));
return properties;
}

static Properties getOauthConnectionParameters(String token) {
Properties properties = getBaseConnectionParameters();
properties.put("user", SSO_USER);
properties.put("authenticator", "OAUTH");
properties.put("token", token);
return properties;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
import net.snowflake.client.jdbc.SnowflakeConnectionV1;
import net.snowflake.client.jdbc.SnowflakeSQLException;

public class AuthTest {
public class AuthTestHelper {

private Exception exception;
private String idToken;
private final boolean runAuthTestsManually;

public AuthTest() {
public AuthTestHelper() {
this.runAuthTestsManually = Boolean.parseBoolean(System.getenv("RUN_AUTH_TESTS_MANUALLY"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,45 @@
import org.junit.jupiter.api.Test;

@Tag(TestTags.AUTHENTICATION)
class ExternalBrowserIT {
class ExternalBrowserLatestIT {

String login = AuthConnectionParameters.SSO_USER;
String password = AuthConnectionParameters.SSO_PASSWORD;
AuthTest authTest = new AuthTest();
AuthTestHelper authTestHelper = new AuthTestHelper();

@BeforeEach
public void setUp() throws IOException {
AuthTest.deleteIdToken();
AuthTestHelper.deleteIdToken();
}

@AfterEach
public void tearDown() {
authTest.cleanBrowserProcesses();
AuthTest.deleteIdToken();
authTestHelper.cleanBrowserProcesses();
AuthTestHelper.deleteIdToken();
}

@Test
void shouldAuthenticateUsingExternalBrowser() throws InterruptedException {
Thread provideCredentialsThread =
new Thread(() -> authTest.provideCredentials("success", login, password));
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
Thread connectThread =
authTest.getConnectAndExecuteSimpleQueryThread(getExternalBrowserConnectionParameters());
authTestHelper.getConnectAndExecuteSimpleQueryThread(
getExternalBrowserConnectionParameters());

authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTest.verifyExceptionIsNotThrown();
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTestHelper.verifyExceptionIsNotThrown();
}

@Test
void shouldThrowErrorForMismatchedUsername() throws InterruptedException {
Properties properties = getExternalBrowserConnectionParameters();
properties.put("user", "differentUsername");
Thread provideCredentialsThread =
new Thread(() -> authTest.provideCredentials("success", login, password));
Thread connectThread = authTest.getConnectAndExecuteSimpleQueryThread(properties);
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
Thread connectThread = authTestHelper.getConnectAndExecuteSimpleQueryThread(properties);

authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTest.verifyExceptionIsThrown(
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTestHelper.verifyExceptionIsThrown(
"The user you were trying to authenticate as differs from the user currently logged in at the IDP.");
}

Expand All @@ -57,26 +58,26 @@ void shouldThrowErrorForWrongCredentials() throws InterruptedException {
String login = "itsnotanaccount.com";
String password = "fakepassword";
Thread provideCredentialsThread =
new Thread(() -> authTest.provideCredentials("fail", login, password));
new Thread(() -> authTestHelper.provideCredentials("fail", login, password));
Thread connectThread =
authTest.getConnectAndExecuteSimpleQueryThread(
authTestHelper.getConnectAndExecuteSimpleQueryThread(
getExternalBrowserConnectionParameters(), "BROWSER_RESPONSE_TIMEOUT=10");

authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTest.verifyExceptionIsThrown(
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTestHelper.verifyExceptionIsThrown(
"JDBC driver encountered communication error. Message: External browser authentication failed within timeout of 10000 milliseconds.");
}

@Test
void shouldThrowErrorForBrowserTimeout() throws InterruptedException {
Thread provideCredentialsThread =
new Thread(() -> authTest.provideCredentials("timeout", login, password));
new Thread(() -> authTestHelper.provideCredentials("timeout", login, password));
Thread connectThread =
authTest.getConnectAndExecuteSimpleQueryThread(
authTestHelper.getConnectAndExecuteSimpleQueryThread(
getExternalBrowserConnectionParameters(), "BROWSER_RESPONSE_TIMEOUT=1");

authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTest.verifyExceptionIsThrown(
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTestHelper.verifyExceptionIsThrown(
"JDBC driver encountered communication error. Message: External browser authentication failed within timeout of 1000 milliseconds.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,58 +17,58 @@

@Tag(TestTags.AUTHENTICATION)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class IdTokenIT {
class IdTokenLatestIT {

String login = AuthConnectionParameters.SSO_USER;
String password = AuthConnectionParameters.SSO_PASSWORD;
AuthTest authTest = new AuthTest();
AuthTestHelper authTestHelper = new AuthTestHelper();
private static String firstToken;

@BeforeAll
public static void globalSetUp() {
AuthTest.deleteIdToken();
AuthTestHelper.deleteIdToken();
}

@AfterEach
public void tearDown() {
authTest.cleanBrowserProcesses();
authTestHelper.cleanBrowserProcesses();
}

@Test
@Order(1)
void shouldAuthenticateUsingExternalBrowserAndSaveToken() throws InterruptedException {
Thread provideCredentialsThread =
new Thread(() -> authTest.provideCredentials("success", login, password));
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
Thread connectThread =
authTest.getConnectAndExecuteSimpleQueryThread(getStoreIDTokenConnectionParameters());
authTestHelper.getConnectAndExecuteSimpleQueryThread(getStoreIDTokenConnectionParameters());

authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTest.verifyExceptionIsNotThrown();
firstToken = authTest.getIdToken();
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTestHelper.verifyExceptionIsNotThrown();
firstToken = authTestHelper.getIdToken();
assertThat("Id token was not saved", firstToken, notNullValue());
}

@Test
@Order(2)
void shouldAuthenticateUsingTokenWithoutBrowser() {
verifyFirstTokenWasSaved();
authTest.connectAndExecuteSimpleQuery(getStoreIDTokenConnectionParameters(), null);
authTest.verifyExceptionIsNotThrown();
authTestHelper.connectAndExecuteSimpleQuery(getStoreIDTokenConnectionParameters(), null);
authTestHelper.verifyExceptionIsNotThrown();
}

@Test
@Order(3)
void shouldOpenBrowserAgainWhenTokenIsDeleted() throws InterruptedException {
verifyFirstTokenWasSaved();
AuthTest.deleteIdToken();
AuthTestHelper.deleteIdToken();
Thread provideCredentialsThread =
new Thread(() -> authTest.provideCredentials("success", login, password));
new Thread(() -> authTestHelper.provideCredentials("success", login, password));
Thread connectThread =
authTest.getConnectAndExecuteSimpleQueryThread(getStoreIDTokenConnectionParameters());
authTestHelper.getConnectAndExecuteSimpleQueryThread(getStoreIDTokenConnectionParameters());

authTest.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTest.verifyExceptionIsNotThrown();
String secondToken = authTest.getIdToken();
authTestHelper.connectAndProvideCredentials(provideCredentialsThread, connectThread);
authTestHelper.verifyExceptionIsNotThrown();
String secondToken = authTestHelper.getIdToken();
assertThat("Id token was not saved", secondToken, notNullValue());
assertThat("Id token was not updated", secondToken, not(firstToken));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package net.snowflake.client.authentication;

import static net.snowflake.client.authentication.AuthConnectionParameters.getOauthConnectionParameters;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.snowflake.client.category.TestTags;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag(TestTags.AUTHENTICATION)
public class OauthLatestIT {

AuthTestHelper authTestHelper;

@BeforeEach
public void setUp() throws IOException {
authTestHelper = new AuthTestHelper();
}

@Test
void shouldAuthenticateUsingOauth() throws IOException {
authTestHelper.connectAndExecuteSimpleQuery(getOauthConnectionParameters(getToken()), null);
authTestHelper.verifyExceptionIsNotThrown();
}

@Test
void shouldThrowErrorForInvalidToken() {
authTestHelper.connectAndExecuteSimpleQuery(getOauthConnectionParameters("invalidToken"), null);
authTestHelper.verifyExceptionIsThrown("Invalid OAuth access token. ");
}

@Test
void shouldThrowErrorForMismatchedOauthUsername() throws IOException {
Properties properties = getOauthConnectionParameters(getToken());
properties.put("user", "differentUsername");
authTestHelper.connectAndExecuteSimpleQuery(properties, null);
authTestHelper.verifyExceptionIsThrown(
"The user you were trying to authenticate as differs from the user tied to the access token.");
}

private String getToken() throws IOException {
List<String> data =
Stream.of(
"username=" + System.getenv("SNOWFLAKE_AUTH_TEST_OKTA_USER"),
"password=" + System.getenv("SNOWFLAKE_AUTH_TEST_OKTA_PASS"),
"grant_type=password",
"scope=session:role:" + System.getenv("SNOWFLAKE_AUTH_TEST_ROLE").toLowerCase())
.collect(Collectors.toList());

String auth =
System.getenv("SNOWFLAKE_AUTH_TEST_OAUTH_CLIENT_ID")
+ ":"
+ System.getenv("SNOWFLAKE_AUTH_TEST_OAUTH_CLIENT_SECRET");
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));

URL url = new URL(System.getenv("SNOWFLAKE_AUTH_TEST_OAUTH_URL"));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty(
"Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
connection.setRequestProperty("Authorization", "Basic " + encodedAuth);
connection.setDoOutput(true);

try (DataOutputStream out = new DataOutputStream(connection.getOutputStream())) {
out.writeBytes(String.join("&", data));
out.flush();
}

int responseCode = connection.getResponseCode();
assertThat("Failed to get access token, response code: " + responseCode, responseCode, is(200));

ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode;
try (InputStream inputStream = connection.getInputStream()) {
jsonNode = mapper.readTree(inputStream);
}
return jsonNode.get("access_token").asText();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package net.snowflake.client.authentication;

import static net.snowflake.client.authentication.AuthConnectionParameters.SSO_USER;
import static net.snowflake.client.authentication.AuthConnectionParameters.getOktaConnectionParameters;

import java.io.IOException;
import java.util.Properties;
import net.snowflake.client.category.TestTags;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag(TestTags.AUTHENTICATION)
class OktaAuthLatestIT {

AuthTestHelper authTestHelper;

@BeforeEach
public void setUp() throws IOException {
authTestHelper = new AuthTestHelper();
}

@Test
void shouldAuthenticateUsingOkta() {
authTestHelper.connectAndExecuteSimpleQuery(getOktaConnectionParameters(), null);
authTestHelper.verifyExceptionIsNotThrown();
}

@Test
void shouldAuthenticateUsingOktaWithOktaUsernameParam() {
Properties properties = getOktaConnectionParameters();
properties.replace("user", "differentUsername");
authTestHelper.connectAndExecuteSimpleQuery(properties, "oktausername=" + SSO_USER);
authTestHelper.verifyExceptionIsNotThrown();
}

@Test
void shouldThrowErrorForWrongOktaCredentials() {
Properties properties = getOktaConnectionParameters();
properties.put("user", "invalidUsername");
properties.put("password", "fakepassword");
authTestHelper.connectAndExecuteSimpleQuery(properties, null);
authTestHelper.verifyExceptionIsThrown(
"JDBC driver encountered communication error. Message: HTTP status=401.");
}

@Test
void shouldThrowErrorForWrongOktaCredentialsInOktaUsernameParam() {
Properties properties = getOktaConnectionParameters();
properties.replace("user", "differentUsername");
authTestHelper.connectAndExecuteSimpleQuery(properties, "oktausername=invalidUser");
authTestHelper.verifyExceptionIsThrown(
"JDBC driver encountered communication error. Message: HTTP status=401.");
}

@Test
void shouldThrowErrorForWrongOktaUrl() {
Properties properties = getOktaConnectionParameters();
properties.put("authenticator", "https://invalid.okta.com/");
authTestHelper.connectAndExecuteSimpleQuery(properties, null);
authTestHelper.verifyExceptionIsThrown(
"The specified authenticator is not accepted by your Snowflake account configuration. Please contact your local system administrator to get the correct URL to use.");
}

@Test
@Disabled // todo SNOW-1852279 implement error handling for invalid URL
void shouldThrowErrorForWrongUrlWithoutOktaPath() {
Properties properties = getOktaConnectionParameters();
properties.put("authenticator", "https://invalid.abc.com/");
authTestHelper.connectAndExecuteSimpleQuery(properties, null);
authTestHelper.verifyExceptionIsThrown("todo");
}
}
Loading

0 comments on commit 41aedbf

Please sign in to comment.