Skip to content

Commit 75532c9

Browse files
[SNOW-1881731] Add externalBrowser, oauth, okta, keypair automated authentication tests (#1082)
1 parent 444a2c1 commit 75532c9

File tree

13 files changed

+696
-39
lines changed

13 files changed

+696
-39
lines changed
941 Bytes
Binary file not shown.
1.37 KB
Binary file not shown.
1.38 KB
Binary file not shown.

Jenkinsfile

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import groovy.json.JsonOutput
22

3-
43
timestamps {
54
node('regular-memory-node') {
65
stage('checkout') {
@@ -13,8 +12,8 @@ timestamps {
1312
stage('Build') {
1413
withCredentials([
1514
usernamePassword(credentialsId: '063fc85b-62a6-4181-9d72-873b43488411', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY'),
16-
string(credentialsId: 'a791118f-a1ea-46cd-b876-56da1b9bc71c',variable: 'NEXUS_PASSWORD')
17-
]) {
15+
string(credentialsId: 'a791118f-a1ea-46cd-b876-56da1b9bc71c', variable: 'NEXUS_PASSWORD')
16+
]) {
1817
sh '''\
1918
|#!/bin/bash -e
2019
|export GIT_BRANCH=${GIT_BRANCH}
@@ -23,7 +22,8 @@ timestamps {
2322
'''.stripMargin()
2423
}
2524
}
26-
params = [
25+
26+
def params = [
2727
string(name: 'svn_revision', value: 'bptp-built'),
2828
string(name: 'branch', value: 'main'),
2929
string(name: 'client_git_commit', value: scmInfo.GIT_COMMIT),
@@ -32,23 +32,37 @@ timestamps {
3232
string(name: 'parent_job', value: env.JOB_NAME),
3333
string(name: 'parent_build_number', value: env.BUILD_NUMBER)
3434
]
35+
3536
stage('Test') {
36-
build job: 'RT-LanguageDotnet-PC',parameters: params
37+
parallel(
38+
'Test': {
39+
stage('Test') {
40+
build job: 'RT-LanguageDotnet-PC', parameters: params
41+
}
42+
},
43+
'Test Authentication': {
44+
stage('Test Authentication') {
45+
withCredentials([
46+
string(credentialsId: 'a791118f-a1ea-46cd-b876-56da1b9bc71c', variable: 'NEXUS_PASSWORD'),
47+
string(credentialsId: 'sfctest0-parameters-secret', variable: 'PARAMETERS_SECRET')
48+
]) {
49+
sh '''\
50+
|#!/bin/bash -e
51+
|$WORKSPACE/ci/test_authentication.sh
52+
'''.stripMargin()
53+
}
54+
}
55+
}
56+
)
3757
}
3858
}
3959
}
4060

41-
4261
pipeline {
4362
agent { label 'regular-memory-node' }
4463
options { timestamps() }
4564
environment {
46-
COMMIT_SHA_LONG = sh(returnStdout: true, script: "echo \$(git rev-parse " + "HEAD)").trim()
47-
48-
// environment variables for semgrep_agent (for findings / analytics page)
49-
// remove .git at the end
50-
// remove SCM URL + .git at the end
51-
65+
COMMIT_SHA_LONG = sh(returnStdout: true, script: "echo \$(git rev-parse HEAD)").trim()
5266
BASELINE_BRANCH = "${env.CHANGE_TARGET}"
5367
}
5468
stages {
@@ -61,7 +75,7 @@ pipeline {
6175
}
6276

6377
def wgetUpdateGithub(String state, String folder, String targetUrl, String seconds) {
64-
def ghURL = "https://api.github.com/repos/snowflakedb/snowflake-connector-net/statuses/$COMMIT_SHA_LONG"
65-
def data = JsonOutput.toJson([state: "${state}", context: "jenkins/${folder}",target_url: "${targetUrl}"])
66-
sh "wget ${ghURL} --spider -q --header='Authorization: token $GIT_PASSWORD' --post-data='${data}'"
67-
}
78+
def ghURL = "https://api.github.com/repos/snowflakedb/snowflake-connector-net/statuses/$COMMIT_SHA_LONG"
79+
def data = JsonOutput.toJson([state: "${state}", context: "jenkins/${folder}", target_url: "${targetUrl}"])
80+
sh "wget ${ghURL} --spider -q --header='Authorization: token $GIT_PASSWORD' --post-data='${data}'"
81+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Newtonsoft.Json.Linq;
5+
using NUnit.Framework;
6+
using System.IO;
7+
using System.Net;
8+
using Snowflake.Data.Core;
9+
using System.Net.Http;
10+
using System.Security.Authentication;
11+
12+
namespace Snowflake.Data.AuthenticationTests
13+
14+
{
15+
static class AuthConnectionString
16+
{
17+
public static readonly string SsoUser = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_BROWSER_USER");
18+
public static readonly string Host = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_HOST");
19+
public static readonly string SsoPassword = Environment.GetEnvironmentVariable("SNOWFLAKE_TEST_OKTA_PASS");
20+
21+
private static SFSessionProperties GetBaseConnectionParameters()
22+
{
23+
var properties = new SFSessionProperties()
24+
{
25+
{SFSessionProperty.HOST, Host },
26+
{SFSessionProperty.PORT, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_PORT") },
27+
{SFSessionProperty.ROLE, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_ROLE") },
28+
{SFSessionProperty.ACCOUNT, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_ACCOUNT") },
29+
{SFSessionProperty.DB, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_DATABASE") },
30+
{SFSessionProperty.SCHEMA, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_SCHEMA") },
31+
{SFSessionProperty.WAREHOUSE, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_WAREHOUSE") },
32+
};
33+
return properties;
34+
}
35+
36+
public static SFSessionProperties GetExternalBrowserConnectionString()
37+
{
38+
var properties = GetBaseConnectionParameters();
39+
properties.Add(SFSessionProperty.AUTHENTICATOR, "externalbrowser");
40+
properties.Add(SFSessionProperty.USER, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_BROWSER_USER"));
41+
return properties;
42+
}
43+
44+
public static SFSessionProperties GetOauthConnectionString(string token)
45+
{
46+
var properties = GetBaseConnectionParameters();
47+
properties.Add(SFSessionProperty.AUTHENTICATOR, "OAUTH");
48+
properties.Add(SFSessionProperty.USER, SsoUser);
49+
properties.Add(SFSessionProperty.TOKEN, token);
50+
return properties;
51+
}
52+
53+
public static SFSessionProperties GetOktaConnectionString()
54+
{
55+
var properties = GetBaseConnectionParameters();
56+
properties.Add(SFSessionProperty.AUTHENTICATOR, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OAUTH_URL"));
57+
properties.Add(SFSessionProperty.USER, SsoUser);
58+
properties.Add(SFSessionProperty.PASSWORD, SsoPassword);
59+
60+
return properties;
61+
}
62+
63+
public static SFSessionProperties GetKeyPairFromFileContentParameters(string privateKey)
64+
{
65+
66+
var properties = GetBaseConnectionParameters();
67+
properties.Add(SFSessionProperty.AUTHENTICATOR, "snowflake_jwt");
68+
properties.Add(SFSessionProperty.USER, SsoUser);
69+
properties.Add(SFSessionProperty.PRIVATE_KEY, privateKey);
70+
71+
return properties;
72+
}
73+
74+
75+
public static SFSessionProperties GetKeyPairFromFilePathConnectionString(string privateKeyPath)
76+
{
77+
78+
var properties = GetBaseConnectionParameters();
79+
properties.Add(SFSessionProperty.AUTHENTICATOR, "snowflake_jwt");
80+
properties.Add(SFSessionProperty.USER, AuthConnectionString.SsoUser);
81+
properties.Add(SFSessionProperty.PRIVATE_KEY_FILE, privateKeyPath);
82+
return properties;
83+
}
84+
85+
public static string ConvertToConnectionString(SFSessionProperties properties)
86+
{
87+
StringBuilder connectionStringBuilder = new StringBuilder();
88+
89+
foreach (var property in properties)
90+
{
91+
connectionStringBuilder.Append($"{property.Key.ToString().ToLower()}={property.Value};");
92+
}
93+
return connectionStringBuilder.ToString();
94+
}
95+
96+
public static string GetPrivateKeyContentForKeypairAuth(string fileLocation)
97+
{
98+
string filePath = Environment.GetEnvironmentVariable(fileLocation);
99+
Assert.IsNotNull(filePath);
100+
string pemKey = File.ReadAllText(Path.Combine("..", "..", "..", "..", filePath));
101+
Assert.IsNotNull(pemKey, $"Failed to read file: {filePath}");
102+
return pemKey;
103+
104+
}
105+
106+
public static string GetPrivateKeyPathForKeypairAuth(string relativeFileLocationEnvVariable)
107+
{
108+
string filePath = Environment.GetEnvironmentVariable(relativeFileLocationEnvVariable);
109+
Assert.IsNotNull(filePath);
110+
return Path.Combine("..", "..", "..", "..", filePath);
111+
}
112+
113+
public static string GetOauthToken()
114+
{
115+
try
116+
{
117+
using (var client = new HttpClient(new HttpClientHandler
118+
{
119+
CheckCertificateRevocationList = true,
120+
SslProtocols = SslProtocols.Tls12,
121+
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
122+
UseProxy = false,
123+
UseCookies = false
124+
}))
125+
{
126+
var authUrl = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OAUTH_URL");
127+
var clientId = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OAUTH_CLIENT_ID");
128+
var clientSecret = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OAUTH_CLIENT_SECRET");
129+
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"));
130+
131+
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentials);
132+
133+
var values = new Dictionary<string, string>
134+
{
135+
{ "username", Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OKTA_USER") },
136+
{ "password", Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OKTA_PASS") },
137+
{ "grant_type", "password" },
138+
{ "scope", "session:role:" + Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_ROLE") }
139+
};
140+
141+
var content = new FormUrlEncodedContent(values);
142+
var response = client.PostAsync(authUrl, content).Result;
143+
response.EnsureSuccessStatusCode();
144+
145+
var fullResponse = response.Content.ReadAsStringAsync().Result;
146+
var responseObject = JObject.Parse(fullResponse);
147+
Assert.IsNotNull(responseObject["access_token"]);
148+
return responseObject["access_token"].ToString();
149+
}
150+
}
151+
catch (Exception e)
152+
{
153+
throw new Exception($"Failed to get OAuth token: {e.Message}");
154+
}
155+
}
156+
}
157+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using System;
2+
using System.Threading;
3+
using System.Diagnostics;
4+
using System.Data;
5+
using NUnit.Framework;
6+
using Snowflake.Data.Client;
7+
using Snowflake.Data.Log;
8+
9+
namespace Snowflake.Data.AuthenticationTests
10+
{
11+
12+
public class AuthTestHelper
13+
{
14+
private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger<AuthTestHelper>();
15+
private Exception _exception;
16+
private readonly bool _runAuthTestsManually;
17+
18+
public AuthTestHelper()
19+
{
20+
string envVar = Environment.GetEnvironmentVariable("RUN_AUTH_TESTS_MANUALLY");
21+
_runAuthTestsManually = bool.Parse(envVar ?? "true");
22+
}
23+
24+
public void CleanBrowserProcess()
25+
{
26+
if (_runAuthTestsManually)
27+
return;
28+
try
29+
{
30+
StartNodeProcess("/externalbrowser/cleanBrowserProcesses.js", TimeSpan.FromSeconds(20));
31+
}
32+
catch (Exception e)
33+
{
34+
throw new Exception(e.ToString());
35+
}
36+
}
37+
38+
public void ConnectAndExecuteSimpleQuery(string connectionString)
39+
{
40+
try
41+
{
42+
using (IDbConnection conn = new SnowflakeDbConnection())
43+
{
44+
conn.ConnectionString = connectionString;
45+
46+
conn.Open();
47+
Assert.AreEqual(ConnectionState.Open, conn.State);
48+
49+
using (IDbCommand command = conn.CreateCommand())
50+
{
51+
command.CommandText = "SELECT 1";
52+
var result = command.ExecuteScalar();
53+
Assert.AreEqual("1", result.ToString());
54+
s_logger.Info(result.ToString());
55+
}
56+
}
57+
}
58+
catch (SnowflakeDbException e)
59+
{
60+
_exception = e;
61+
}
62+
}
63+
64+
public Thread GetConnectAndExecuteSimpleQueryThread(string parameters)
65+
{
66+
return new Thread(() => ConnectAndExecuteSimpleQuery(parameters));
67+
}
68+
69+
public Thread GetProvideCredentialsThread(string scenario, string login, string password)
70+
{
71+
return new Thread(() => ProvideCredentials(scenario, login, password));
72+
}
73+
74+
public void VerifyExceptionIsNotThrown() {
75+
Assert.That(_exception, Is.Null, "Unexpected exception thrown");
76+
}
77+
78+
public void VerifyExceptionIsThrown(string error) {
79+
Assert.That(_exception, Is.Not.Null, "Expected exception was not thrown");
80+
Assert.That(_exception.Message, Does.Contain(error), "Unexpected exception message.");
81+
82+
}
83+
84+
public void ConnectAndProvideCredentials(Thread provideCredentialsThread, Thread connectThread)
85+
{
86+
if (_runAuthTestsManually)
87+
{
88+
connectThread.Start();
89+
connectThread.Join();
90+
}
91+
else
92+
{
93+
provideCredentialsThread.Start();
94+
connectThread.Start();
95+
provideCredentialsThread.Join();
96+
connectThread.Join();
97+
}
98+
}
99+
100+
private void StartNodeProcess(string path, TimeSpan timeout)
101+
{
102+
var startInfo = new ProcessStartInfo
103+
{
104+
FileName = "node",
105+
Arguments = path,
106+
RedirectStandardOutput = true,
107+
RedirectStandardError = true,
108+
UseShellExecute = false,
109+
CreateNoWindow = true
110+
};
111+
using (var process = new Process { StartInfo = startInfo })
112+
{
113+
process.Start();
114+
if (!process.WaitForExit((int) timeout.TotalMilliseconds))
115+
{
116+
process.Kill();
117+
throw new TimeoutException("The process did not complete in the allotted time.");
118+
}
119+
string output = process.StandardOutput.ReadToEnd();
120+
string error = process.StandardError.ReadToEnd();
121+
122+
s_logger.Info("Output: " + output);
123+
s_logger.Info("Error: " + error);
124+
}
125+
}
126+
127+
private void ProvideCredentials(string scenario, string login, string password)
128+
{
129+
try
130+
{
131+
StartNodeProcess($"/externalbrowser/provideBrowserCredentials.js {scenario} {login} {password}", TimeSpan.FromSeconds(15));
132+
}
133+
catch (Exception e)
134+
{
135+
_exception = e;
136+
}
137+
}
138+
}
139+
}
140+

0 commit comments

Comments
 (0)