-
Notifications
You must be signed in to change notification settings - Fork 141
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SNOW-1881731] Add externalBrowser, oauth, okta, keypair automated au…
…thentication tests (#1082)
- Loading branch information
1 parent
444a2c1
commit 75532c9
Showing
13 changed files
with
696 additions
and
39 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
157 changes: 157 additions & 0 deletions
157
Snowflake.Data.Tests/AuthenticationTests/AuthConnectionString.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
140
Snowflake.Data.Tests/AuthenticationTests/AuthTestHelper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.