Skip to content

Commit

Permalink
[SNOW-1881731] Add externalBrowser, oauth, okta, keypair automated au…
Browse files Browse the repository at this point in the history
…thentication tests (#1082)
  • Loading branch information
sfc-gh-pcyrek authored Jan 31, 2025
1 parent 444a2c1 commit 75532c9
Show file tree
Hide file tree
Showing 13 changed files with 696 additions and 39 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
46 changes: 30 additions & 16 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import groovy.json.JsonOutput


timestamps {
node('regular-memory-node') {
stage('checkout') {
Expand All @@ -13,8 +12,8 @@ timestamps {
stage('Build') {
withCredentials([
usernamePassword(credentialsId: '063fc85b-62a6-4181-9d72-873b43488411', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY'),
string(credentialsId: 'a791118f-a1ea-46cd-b876-56da1b9bc71c',variable: 'NEXUS_PASSWORD')
]) {
string(credentialsId: 'a791118f-a1ea-46cd-b876-56da1b9bc71c', variable: 'NEXUS_PASSWORD')
]) {
sh '''\
|#!/bin/bash -e
|export GIT_BRANCH=${GIT_BRANCH}
Expand All @@ -23,7 +22,8 @@ timestamps {
'''.stripMargin()
}
}
params = [

def params = [
string(name: 'svn_revision', value: 'bptp-built'),
string(name: 'branch', value: 'main'),
string(name: 'client_git_commit', value: scmInfo.GIT_COMMIT),
Expand All @@ -32,23 +32,37 @@ timestamps {
string(name: 'parent_job', value: env.JOB_NAME),
string(name: 'parent_build_number', value: env.BUILD_NUMBER)
]

stage('Test') {
build job: 'RT-LanguageDotnet-PC',parameters: params
parallel(
'Test': {
stage('Test') {
build job: 'RT-LanguageDotnet-PC', parameters: params
}
},
'Test Authentication': {
stage('Test Authentication') {
withCredentials([
string(credentialsId: 'a791118f-a1ea-46cd-b876-56da1b9bc71c', variable: 'NEXUS_PASSWORD'),
string(credentialsId: 'sfctest0-parameters-secret', variable: 'PARAMETERS_SECRET')
]) {
sh '''\
|#!/bin/bash -e
|$WORKSPACE/ci/test_authentication.sh
'''.stripMargin()
}
}
}
)
}
}
}


pipeline {
agent { label 'regular-memory-node' }
options { timestamps() }
environment {
COMMIT_SHA_LONG = sh(returnStdout: true, script: "echo \$(git rev-parse " + "HEAD)").trim()

// environment variables for semgrep_agent (for findings / analytics page)
// remove .git at the end
// remove SCM URL + .git at the end

COMMIT_SHA_LONG = sh(returnStdout: true, script: "echo \$(git rev-parse HEAD)").trim()
BASELINE_BRANCH = "${env.CHANGE_TARGET}"
}
stages {
Expand All @@ -61,7 +75,7 @@ pipeline {
}

def wgetUpdateGithub(String state, String folder, String targetUrl, String seconds) {
def ghURL = "https://api.github.com/repos/snowflakedb/snowflake-connector-net/statuses/$COMMIT_SHA_LONG"
def data = JsonOutput.toJson([state: "${state}", context: "jenkins/${folder}",target_url: "${targetUrl}"])
sh "wget ${ghURL} --spider -q --header='Authorization: token $GIT_PASSWORD' --post-data='${data}'"
}
def ghURL = "https://api.github.com/repos/snowflakedb/snowflake-connector-net/statuses/$COMMIT_SHA_LONG"
def data = JsonOutput.toJson([state: "${state}", context: "jenkins/${folder}", target_url: "${targetUrl}"])
sh "wget ${ghURL} --spider -q --header='Authorization: token $GIT_PASSWORD' --post-data='${data}'"
}
157 changes: 157 additions & 0 deletions Snowflake.Data.Tests/AuthenticationTests/AuthConnectionString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using System.IO;
using System.Net;
using Snowflake.Data.Core;
using System.Net.Http;
using System.Security.Authentication;

namespace Snowflake.Data.AuthenticationTests

{
static class AuthConnectionString
{
public static readonly string SsoUser = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_BROWSER_USER");
public static readonly string Host = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_HOST");
public static readonly string SsoPassword = Environment.GetEnvironmentVariable("SNOWFLAKE_TEST_OKTA_PASS");

private static SFSessionProperties GetBaseConnectionParameters()
{
var properties = new SFSessionProperties()
{
{SFSessionProperty.HOST, Host },
{SFSessionProperty.PORT, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_PORT") },
{SFSessionProperty.ROLE, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_ROLE") },
{SFSessionProperty.ACCOUNT, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_ACCOUNT") },
{SFSessionProperty.DB, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_DATABASE") },
{SFSessionProperty.SCHEMA, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_SCHEMA") },
{SFSessionProperty.WAREHOUSE, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_WAREHOUSE") },
};
return properties;
}

public static SFSessionProperties GetExternalBrowserConnectionString()
{
var properties = GetBaseConnectionParameters();
properties.Add(SFSessionProperty.AUTHENTICATOR, "externalbrowser");
properties.Add(SFSessionProperty.USER, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_BROWSER_USER"));
return properties;
}

public static SFSessionProperties GetOauthConnectionString(string token)
{
var properties = GetBaseConnectionParameters();
properties.Add(SFSessionProperty.AUTHENTICATOR, "OAUTH");
properties.Add(SFSessionProperty.USER, SsoUser);
properties.Add(SFSessionProperty.TOKEN, token);
return properties;
}

public static SFSessionProperties GetOktaConnectionString()
{
var properties = GetBaseConnectionParameters();
properties.Add(SFSessionProperty.AUTHENTICATOR, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OAUTH_URL"));
properties.Add(SFSessionProperty.USER, SsoUser);
properties.Add(SFSessionProperty.PASSWORD, SsoPassword);

return properties;
}

public static SFSessionProperties GetKeyPairFromFileContentParameters(string privateKey)
{

var properties = GetBaseConnectionParameters();
properties.Add(SFSessionProperty.AUTHENTICATOR, "snowflake_jwt");
properties.Add(SFSessionProperty.USER, SsoUser);
properties.Add(SFSessionProperty.PRIVATE_KEY, privateKey);

return properties;
}


public static SFSessionProperties GetKeyPairFromFilePathConnectionString(string privateKeyPath)
{

var properties = GetBaseConnectionParameters();
properties.Add(SFSessionProperty.AUTHENTICATOR, "snowflake_jwt");
properties.Add(SFSessionProperty.USER, AuthConnectionString.SsoUser);
properties.Add(SFSessionProperty.PRIVATE_KEY_FILE, privateKeyPath);
return properties;
}

public static string ConvertToConnectionString(SFSessionProperties properties)
{
StringBuilder connectionStringBuilder = new StringBuilder();

foreach (var property in properties)
{
connectionStringBuilder.Append($"{property.Key.ToString().ToLower()}={property.Value};");
}
return connectionStringBuilder.ToString();
}

public static string GetPrivateKeyContentForKeypairAuth(string fileLocation)
{
string filePath = Environment.GetEnvironmentVariable(fileLocation);
Assert.IsNotNull(filePath);
string pemKey = File.ReadAllText(Path.Combine("..", "..", "..", "..", filePath));
Assert.IsNotNull(pemKey, $"Failed to read file: {filePath}");
return pemKey;

}

public static string GetPrivateKeyPathForKeypairAuth(string relativeFileLocationEnvVariable)
{
string filePath = Environment.GetEnvironmentVariable(relativeFileLocationEnvVariable);
Assert.IsNotNull(filePath);
return Path.Combine("..", "..", "..", "..", filePath);
}

public static string GetOauthToken()
{
try
{
using (var client = new HttpClient(new HttpClientHandler
{
CheckCertificateRevocationList = true,
SslProtocols = SslProtocols.Tls12,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
UseProxy = false,
UseCookies = false
}))
{
var authUrl = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OAUTH_URL");
var clientId = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OAUTH_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OAUTH_CLIENT_SECRET");
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"));

client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentials);

var values = new Dictionary<string, string>
{
{ "username", Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OKTA_USER") },
{ "password", Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_OKTA_PASS") },
{ "grant_type", "password" },
{ "scope", "session:role:" + Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_ROLE") }
};

var content = new FormUrlEncodedContent(values);
var response = client.PostAsync(authUrl, content).Result;
response.EnsureSuccessStatusCode();

var fullResponse = response.Content.ReadAsStringAsync().Result;
var responseObject = JObject.Parse(fullResponse);
Assert.IsNotNull(responseObject["access_token"]);
return responseObject["access_token"].ToString();
}
}
catch (Exception e)
{
throw new Exception($"Failed to get OAuth token: {e.Message}");
}
}
}
}
140 changes: 140 additions & 0 deletions Snowflake.Data.Tests/AuthenticationTests/AuthTestHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using System;
using System.Threading;
using System.Diagnostics;
using System.Data;
using NUnit.Framework;
using Snowflake.Data.Client;
using Snowflake.Data.Log;

namespace Snowflake.Data.AuthenticationTests
{

public class AuthTestHelper
{
private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger<AuthTestHelper>();
private Exception _exception;
private readonly bool _runAuthTestsManually;

public AuthTestHelper()
{
string envVar = Environment.GetEnvironmentVariable("RUN_AUTH_TESTS_MANUALLY");
_runAuthTestsManually = bool.Parse(envVar ?? "true");
}

public void CleanBrowserProcess()
{
if (_runAuthTestsManually)
return;
try
{
StartNodeProcess("/externalbrowser/cleanBrowserProcesses.js", TimeSpan.FromSeconds(20));
}
catch (Exception e)
{
throw new Exception(e.ToString());
}
}

public void ConnectAndExecuteSimpleQuery(string connectionString)
{
try
{
using (IDbConnection conn = new SnowflakeDbConnection())
{
conn.ConnectionString = connectionString;

conn.Open();
Assert.AreEqual(ConnectionState.Open, conn.State);

using (IDbCommand command = conn.CreateCommand())
{
command.CommandText = "SELECT 1";
var result = command.ExecuteScalar();
Assert.AreEqual("1", result.ToString());
s_logger.Info(result.ToString());
}
}
}
catch (SnowflakeDbException e)
{
_exception = e;
}
}

public Thread GetConnectAndExecuteSimpleQueryThread(string parameters)
{
return new Thread(() => ConnectAndExecuteSimpleQuery(parameters));
}

public Thread GetProvideCredentialsThread(string scenario, string login, string password)
{
return new Thread(() => ProvideCredentials(scenario, login, password));
}

public void VerifyExceptionIsNotThrown() {
Assert.That(_exception, Is.Null, "Unexpected exception thrown");
}

public void VerifyExceptionIsThrown(string error) {
Assert.That(_exception, Is.Not.Null, "Expected exception was not thrown");
Assert.That(_exception.Message, Does.Contain(error), "Unexpected exception message.");

}

public void ConnectAndProvideCredentials(Thread provideCredentialsThread, Thread connectThread)
{
if (_runAuthTestsManually)
{
connectThread.Start();
connectThread.Join();
}
else
{
provideCredentialsThread.Start();
connectThread.Start();
provideCredentialsThread.Join();
connectThread.Join();
}
}

private void StartNodeProcess(string path, TimeSpan timeout)
{
var startInfo = new ProcessStartInfo
{
FileName = "node",
Arguments = path,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var process = new Process { StartInfo = startInfo })
{
process.Start();
if (!process.WaitForExit((int) timeout.TotalMilliseconds))
{
process.Kill();
throw new TimeoutException("The process did not complete in the allotted time.");
}
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();

s_logger.Info("Output: " + output);
s_logger.Info("Error: " + error);
}
}

private void ProvideCredentials(string scenario, string login, string password)
{
try
{
StartNodeProcess($"/externalbrowser/provideBrowserCredentials.js {scenario} {login} {password}", TimeSpan.FromSeconds(15));
}
catch (Exception e)
{
_exception = e;
}
}
}
}

Loading

0 comments on commit 75532c9

Please sign in to comment.