Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-715524: Add SSO token cache #921

Open
wants to merge 98 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
7de3ea6
SNOW-715524: Add SSO token cache
sfc-gh-ext-simba-lf Apr 18, 2024
4457077
Add tests
sfc-gh-ext-simba-lf Apr 18, 2024
15a58be
Remove unused namespace
sfc-gh-ext-simba-lf Apr 18, 2024
632a6b0
Merge branch 'master' into SNOW-715524-SSO-Token-Cache
sfc-gh-ext-simba-lf Apr 18, 2024
7f28fa8
Fix unit test
sfc-gh-ext-simba-lf Apr 18, 2024
a317158
Refactor constructor and tests
sfc-gh-ext-simba-lf Apr 18, 2024
625e04b
Refactor test
sfc-gh-ext-simba-lf Apr 18, 2024
383fe5e
Refactor test and file impl
sfc-gh-ext-simba-lf Apr 19, 2024
8ce9d10
Refactor file name and fix test
sfc-gh-ext-simba-lf Apr 19, 2024
a460e4b
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Apr 19, 2024
e0b65d0
Revert removed lines
sfc-gh-ext-simba-lf Apr 19, 2024
6606823
Refactor code and remove unnecessary check
sfc-gh-ext-simba-lf Apr 19, 2024
4e6869d
Add session test
sfc-gh-ext-simba-lf Apr 19, 2024
3788474
Fix test
sfc-gh-ext-simba-lf Apr 19, 2024
c597c64
Refactor test and rename file
sfc-gh-ext-simba-lf Apr 19, 2024
39b90b2
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Apr 29, 2024
b4ab4ed
Add more logging
sfc-gh-ext-simba-lf May 1, 2024
13ba839
Change log from error to info
sfc-gh-ext-simba-lf May 1, 2024
b334fb3
Add file path to logs
sfc-gh-ext-simba-lf May 2, 2024
623ce6a
Add Meziantou package for credential manager implementation
sfc-gh-ext-simba-lf May 8, 2024
976bac2
Add native implementation of credential cache
sfc-gh-ext-simba-lf May 17, 2024
feab579
Add impl for ReleaseHandle
sfc-gh-ext-simba-lf May 17, 2024
61b2317
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Jun 5, 2024
cb1c84f
Remove MfaToken from TokenType enum
sfc-gh-ext-simba-lf Jun 5, 2024
7f0f801
Add class name to the log
sfc-gh-ext-simba-lf Jun 5, 2024
780d213
Add class name to the log
sfc-gh-ext-simba-lf Jun 5, 2024
575c0a4
Add class name to the log
sfc-gh-ext-simba-lf Jun 5, 2024
aa00982
Add class name to default log message
sfc-gh-ext-simba-lf Jun 5, 2024
ecdcf37
Merge branch 'SNOW-715524-SSO-Token-Cache' of https://github.com/snow…
sfc-gh-ext-simba-lf Jun 5, 2024
6f31fe6
Rename native class and remove impl with external libs
sfc-gh-ext-simba-lf Jun 5, 2024
cdc9f80
Use HomeDirectoryProvider to retrieve the default location
sfc-gh-ext-simba-lf Jun 5, 2024
9622f5c
Check if json file already exists
sfc-gh-ext-simba-lf Jun 5, 2024
a2f9b50
Change parameter from string to enum
sfc-gh-ext-simba-lf Jun 5, 2024
739c40c
Remove encryption for in-memory credential manager
sfc-gh-ext-simba-lf Jun 6, 2024
c502e80
Change modifier for dictionary
sfc-gh-ext-simba-lf Jun 6, 2024
465da80
Change public modifier for credential manager factory
sfc-gh-ext-simba-lf Jun 6, 2024
94bce01
Move credential manager files to core folder
sfc-gh-ext-simba-lf Jun 6, 2024
83119f3
Move interface and implementations to subpackage
sfc-gh-ext-simba-lf Jun 6, 2024
4e57147
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Jun 12, 2024
61855f2
Remove unused packages
sfc-gh-ext-simba-lf Jun 13, 2024
8b38fed
Include ".snowflake" to the default cache directory
sfc-gh-ext-simba-lf Jun 17, 2024
194eafa
Refactor credential manager
sfc-gh-ext-simba-lf Jun 17, 2024
8666194
Refactor credential manager
sfc-gh-ext-simba-lf Jun 17, 2024
403dbd2
Merge branch 'SNOW-715524-SSO-Token-Cache' of https://github.com/snow…
sfc-gh-ext-simba-lf Jun 17, 2024
33ebec5
Refactor external browser authentication
sfc-gh-ext-simba-lf Jun 18, 2024
512de5b
Remove modifying file permission on Windows
sfc-gh-ext-simba-lf Jun 20, 2024
6ef9b35
Modify test to open the second connection before calling close
sfc-gh-ext-simba-lf Jun 25, 2024
d616dcc
Modify session property test
sfc-gh-ext-simba-lf Jun 25, 2024
045fc04
Uncomment line in session property test
sfc-gh-ext-simba-lf Jun 25, 2024
5c9d8d7
Add check for new map parameter value
sfc-gh-ext-simba-lf Jun 25, 2024
adb3218
Replace user and add test explanation
sfc-gh-ext-simba-lf Jun 26, 2024
da0cffb
Remove unused packages
sfc-gh-ext-simba-lf Jun 28, 2024
44c746b
Add mock for browser and tests for external browser authentication
sfc-gh-ext-simba-lf Jul 4, 2024
590e98b
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Jul 4, 2024
2abcad4
Temporarily ignore test while looking for fix
sfc-gh-ext-simba-lf Jul 4, 2024
dd24c76
Temporarily ignore test while looking for fix
sfc-gh-ext-simba-lf Jul 4, 2024
f8b3230
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Jul 4, 2024
7a384e8
Rename internal property based on convention and fix missing comma
sfc-gh-ext-simba-lf Jul 4, 2024
7672901
Fix exception not being thrown while listening for browser response
sfc-gh-ext-simba-lf Jul 6, 2024
2cdbae4
Mark browser tests nonparallelizable
sfc-gh-ext-simba-lf Jul 6, 2024
524b78f
Catch HttpListenerException
sfc-gh-ext-simba-lf Jul 6, 2024
6fdf045
Catch HttpListenerException
sfc-gh-ext-simba-lf Jul 6, 2024
2d2870d
Add error logs
sfc-gh-ext-simba-lf Jul 6, 2024
f8f7336
Add check if event is still waiting for response before getting the c…
sfc-gh-ext-simba-lf Jul 8, 2024
8af5ba8
Revert event check and add log to check port number
sfc-gh-ext-simba-lf Jul 8, 2024
e56b086
Add log for browser port number
sfc-gh-ext-simba-lf Jul 8, 2024
2ba9aaf
Remove printing port number and add check if result is completed
sfc-gh-ext-simba-lf Jul 8, 2024
7e9c098
Remove printing result.IsCompleted line
sfc-gh-ext-simba-lf Jul 8, 2024
e49ed64
Remove catching HttpListenerException
sfc-gh-ext-simba-lf Jul 8, 2024
c434184
Catch HttpListenerException
sfc-gh-ext-simba-lf Jul 8, 2024
67a8fbf
Add check if result is completed and add wait for result
sfc-gh-ext-simba-lf Jul 8, 2024
f8fcf2b
Add detail for listener exception
sfc-gh-ext-simba-lf Jul 8, 2024
a46f1ad
Specify parameter as AsyncCallback
sfc-gh-ext-simba-lf Jul 8, 2024
52bfd3e
Refactor saml request functions
sfc-gh-ext-simba-lf Jul 9, 2024
9edfd79
Increase browser response timeout for test
sfc-gh-ext-simba-lf Jul 9, 2024
f6d8a0f
Remove catching HttpListenerException
sfc-gh-ext-simba-lf Jul 9, 2024
77a69b3
Remove unnecessary NonParallelizable attribute
sfc-gh-ext-simba-lf Jul 9, 2024
9038ba1
Revert remove catching HttpListenerException
sfc-gh-ext-simba-lf Jul 9, 2024
d800e6f
Refactor if check
sfc-gh-ext-simba-lf Jul 9, 2024
58b74d8
Add temp console lines
sfc-gh-ext-simba-lf Jul 9, 2024
8f919e7
Remove console lines
sfc-gh-ext-simba-lf Jul 9, 2024
fbbac23
Stop http listener before throwing browser exceptions
sfc-gh-ext-simba-lf Jul 9, 2024
d53dd8f
Close HttpListener before throwing exception
sfc-gh-ext-simba-lf Jul 9, 2024
98df45e
Close HttpListener before throwing exception
sfc-gh-ext-simba-lf Jul 9, 2024
359cfa3
Modify browser timeout test
sfc-gh-ext-simba-lf Jul 9, 2024
85d04ed
Abort HttpListener before throwing exception
sfc-gh-ext-simba-lf Jul 9, 2024
2d39171
Check if browser timeout already reached before getting context
sfc-gh-ext-simba-lf Jul 9, 2024
f7be152
Use SecureString for tokens
sfc-gh-ext-simba-lf Jul 9, 2024
2f20edd
Add security checks when reading the credential cache json
sfc-gh-ext-simba-lf Jul 10, 2024
ac1d659
Add error logs when reading the json cache file
sfc-gh-ext-simba-lf Jul 10, 2024
8c65f4d
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Jul 17, 2024
330fca9
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Jul 24, 2024
fc928fc
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Aug 6, 2024
d53955a
Merge branch 'master' into SNOW-715524-SSO-Token-Cache
sfc-gh-ext-simba-lf Aug 21, 2024
d6ff05a
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Aug 27, 2024
a1a6a31
Merge branch 'master' into SNOW-715524-SSO-Token-Cache
sfc-gh-ext-simba-lf Aug 30, 2024
74963c7
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Sep 3, 2024
126e337
Merge branch 'master' into SNOW-715524-SSO-Token-Cache
sfc-gh-ext-simba-lf Sep 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ The following table lists all valid connection properties:
| CLIENT_CONFIG_FILE | No | The location of the client configuration json file. In this file you can configure easy logging feature. |
| ALLOWUNDERSCORESINHOST | No | Specifies whether to allow underscores in account names. This impacts PrivateLink customers whose account names contain underscores. In this situation, you must override the default value by setting allowUnderscoresInHost to true. |
| QUERY_TAG | No | Optional string that can be used to tag queries and other SQL statements executed within a connection. The tags are displayed in the output of the QUERY_HISTORY , QUERY_HISTORY_BY_* functions.<br/> To set QUERY_TAG on the statement level you can use SnowflakeDbCommand.QueryTag. |
| ALLOW_SSO_TOKEN_CACHING | No | Specifies whether to cache tokens and use them for SSO authentication. |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to update the PR with changes from master - this table was moved to another file.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that it's in Connecting.md now so I put this line there


<br />

Expand Down
79 changes: 75 additions & 4 deletions Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
*/

Expand Down Expand Up @@ -1020,6 +1020,52 @@ public void TestSSOConnectionTimeoutAfter10s()
Assert.LessOrEqual(stopwatch.ElapsedMilliseconds, (waitSeconds + 5) * 1000);
}

[Test]
[Ignore("This test requires manual interaction and therefore cannot be run in CI")]
public void TestSSOConnectionWithTokenCaching()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with ignored tests is that they will not be executed so in the future something can break and we will not notice that.
Then if a developer wants to run them manually it takes him a lot of time to create a proper setup for the test.
Could you provide some context (for example in the comments or readme) how to run this test manually for someone who will want to run it for example in one year and would not know any context of this feature?

You provide here a specific user. Also maybe we would need information that we should change it when testing on particular environment. But we should not write about any connection details in this repo since it is publicly visible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, I'll put more details on the comments on how to do the test

As for the user, I'll replace it so the details are not exposed

{
using (IDbConnection conn = new SnowflakeDbConnection())
{
conn.ConnectionString
= ConnectionStringWithoutAuth
+ ";authenticator=externalbrowser;[email protected];allow_sso_token_caching=true;";

// Authenticate to retrieve and store the token if doesn't exist or invalid
conn.Open();
Assert.AreEqual(ConnectionState.Open, conn.State);

conn.Close();
Assert.AreEqual(ConnectionState.Closed, conn.State);

// Authenticate using the token
conn.Open();
Assert.AreEqual(ConnectionState.Open, conn.State);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the connection pool is enabled by default so when you open the connection for the second time you get exactly the same session - so you are not testing getting the token from the credential manager.

You should open the second connection before closing the first one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
}

[Test]
[Ignore("This test requires manual interaction and therefore cannot be run in CI")]
public void TestSSOConnectionWithInvalidCachedToken()
{
using (IDbConnection conn = new SnowflakeDbConnection())
{
conn.ConnectionString
= ConnectionStringWithoutAuth
+ ";authenticator=externalbrowser;[email protected];allow_sso_token_caching=true;";

var key = SnowflakeCredentialManagerFactory.BuildCredentialKey(testConfig.host, testConfig.user, TokenType.IdToken.ToString());
var credentialManager = SnowflakeCredentialManagerInMemoryImpl.Instance;
credentialManager.SaveCredentials(key, "wrongToken");

SnowflakeCredentialManagerFactory.SetCredentialManager(credentialManager);

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

SnowflakeCredentialManagerFactory.UseDefaultCredentialManager();
}
}

[Test]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to write a test which would not be ignored? Maybe with something mocked or something that simulates the browser action?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added tests for external browser authentication using a mock browser

[Ignore("This test requires manual interaction and therefore cannot be run in CI")]
public void TestSSOConnectionWithWrongUser()
Expand Down Expand Up @@ -2231,17 +2277,42 @@ public void TestConnectStringWithQueryTag()
{
string expectedQueryTag = "Test QUERY_TAG 12345";
conn.ConnectionString = ConnectionString + $";query_tag={expectedQueryTag}";

conn.Open();
var command = conn.CreateCommand();
// This query itself will be part of the history and will have the query tag
command.CommandText = "SELECT QUERY_TAG FROM table(information_schema.query_history_by_session())";
var queryTag = command.ExecuteScalar();

Assert.AreEqual(expectedQueryTag, queryTag);
}
}


[Test]
[Ignore("This test requires manual interaction and therefore cannot be run in CI")]
public void TestSSOConnectionWithTokenCachingAsync()
{
using (SnowflakeDbConnection conn = new SnowflakeDbConnection())
{
conn.ConnectionString
= ConnectionStringWithoutAuth
+ ";authenticator=externalbrowser;[email protected];allow_sso_token_caching=true;";

// Authenticate to retrieve and store the token if doesn't exist or invalid
Task connectTask = conn.OpenAsync(CancellationToken.None);
connectTask.Wait();
Assert.AreEqual(ConnectionState.Open, conn.State);

connectTask = conn.CloseAsync(CancellationToken.None);
connectTask.Wait();
Assert.AreEqual(ConnectionState.Closed, conn.State);

// Authenticate using the token
connectTask = conn.OpenAsync(CancellationToken.None);
connectTask.Wait();
Assert.AreEqual(ConnectionState.Open, conn.State);
}
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1624,7 +1624,7 @@ public void TestGetResultsOfUnknownQueryIdWithConfiguredRetry()
conn.Close();
}
}

[Test]
public void TestSetQueryTagOverridesConnectionString()
{
Expand All @@ -1633,14 +1633,14 @@ public void TestSetQueryTagOverridesConnectionString()
string expectedQueryTag = "Test QUERY_TAG 12345";
string connectQueryTag = "Test 123";
conn.ConnectionString = ConnectionString + $";query_tag={connectQueryTag}";

conn.Open();
var command = conn.CreateCommand();
((SnowflakeDbCommand)command).QueryTag = expectedQueryTag;
// This query itself will be part of the history and will have the query tag
command.CommandText = "SELECT QUERY_TAG FROM table(information_schema.query_history_by_session())";
var queryTag = command.ExecuteScalar();

Assert.AreEqual(expectedQueryTag, queryTag);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
/*
* Copyright (c) 2024 Snowflake Computing Inc. All rights reserved.
*/

namespace Snowflake.Data.Tests.UnitTests.CredentialManager
{
using Mono.Unix;
using Mono.Unix.Native;
using Moq;
using NUnit.Framework;
using Snowflake.Data.Client;
using Snowflake.Data.Core.Tools;
using System;
using System.IO;
using System.Runtime.InteropServices;

public abstract class SFBaseCredentialManagerTest
{
protected ISnowflakeCredentialManager _credentialManager;

[Test]
public void TestSavingAndRemovingCredentials()
{
// arrange
var key = "mockKey";
var expectedToken = "token";

// act
_credentialManager.SaveCredentials(key, expectedToken);
var actualToken = _credentialManager.GetCredentials(key);

// assert
Assert.AreEqual(expectedToken, actualToken);

// act
_credentialManager.RemoveCredentials(key);
actualToken = _credentialManager.GetCredentials(key);

// assert
Assert.IsTrue(string.IsNullOrEmpty(actualToken));
}

[Test]
public void TestSavingCredentialsForAnExistingKey()
{
// arrange
var key = "mockKey";
var firstExpectedToken = "mockToken1";
var secondExpectedToken = "mockToken2";

try
{
// act
_credentialManager.SaveCredentials(key, firstExpectedToken);

// assert
Assert.AreEqual(firstExpectedToken, _credentialManager.GetCredentials(key));

// act
_credentialManager.SaveCredentials(key, secondExpectedToken);

// assert
Assert.AreEqual(secondExpectedToken, _credentialManager.GetCredentials(key));
}
catch (Exception ex)
{
// assert
Assert.Fail("Should not throw an exception: " + ex.Message);
}
}

[Test]
public void TestRemovingCredentialsForKeyThatDoesNotExist()
{
// arrange
var key = "mockKey";

try
{
// act
_credentialManager.RemoveCredentials(key);

// assert
Assert.IsTrue(string.IsNullOrEmpty(_credentialManager.GetCredentials(key)));
}
catch (Exception ex)
{
// assert
Assert.Fail("Should not throw an exception: " + ex.Message);
}
}
}

[TestFixture]
[Platform("Win")]
public class SFAdysTechCredentialManagerTest : SFBaseCredentialManagerTest
{
[SetUp]
public void SetUp()
{
_credentialManager = SnowflakeCredentialManagerAdysTechImpl.Instance;
}
}

[TestFixture]
public class SFInMemoryCredentialManagerTest : SFBaseCredentialManagerTest
{
[SetUp]
public void SetUp()
{
_credentialManager = SnowflakeCredentialManagerInMemoryImpl.Instance;
}
}

[TestFixture]
public class SFFileCredentialManagerTest : SFBaseCredentialManagerTest
{
[SetUp]
public void SetUp()
{
_credentialManager = SnowflakeCredentialManagerFileImpl.Instance;
}
}

[TestFixture]
class SFCredentialManagerTest
{
ISnowflakeCredentialManager _credentialManager;

[ThreadStatic]
private static Mock<FileOperations> t_fileOperations;

[ThreadStatic]
private static Mock<DirectoryOperations> t_directoryOperations;

[ThreadStatic]
private static Mock<UnixOperations> t_unixOperations;

[ThreadStatic]
private static Mock<EnvironmentOperations> t_environmentOperations;

private static readonly string s_defaultJsonDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

private const string CustomJsonDir = "testdirectory";

private static readonly string s_defaultJsonPath = Path.Combine(s_defaultJsonDir, SnowflakeCredentialManagerFileImpl.CredentialCacheFileName);

private static readonly string s_customJsonPath = Path.Combine(CustomJsonDir, SnowflakeCredentialManagerFileImpl.CredentialCacheFileName);

[SetUp] public void SetUp()
{
t_fileOperations = new Mock<FileOperations>();
t_directoryOperations = new Mock<DirectoryOperations>();
t_unixOperations = new Mock<UnixOperations>();
t_environmentOperations = new Mock<EnvironmentOperations>();
SnowflakeCredentialManagerFactory.SetCredentialManager(SnowflakeCredentialManagerInMemoryImpl.Instance);
}

[TearDown] public void TearDown()
{
SnowflakeCredentialManagerFactory.UseDefaultCredentialManager();
}

[Test]
public void TestUsingDefaultCredentialManager()
{
// arrange
SnowflakeCredentialManagerFactory.UseDefaultCredentialManager();

// act
_credentialManager = SnowflakeCredentialManagerFactory.GetCredentialManager();

// assert
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Assert.IsInstanceOf<SnowflakeCredentialManagerAdysTechImpl>(_credentialManager);
}
else
{
Assert.IsInstanceOf<SnowflakeCredentialManagerInMemoryImpl>(_credentialManager);
}
}

[Test]
public void TestSettingCustomCredentialManager()
{
// arrange
SnowflakeCredentialManagerFactory.SetCredentialManager(SnowflakeCredentialManagerFileImpl.Instance);

// act
_credentialManager = SnowflakeCredentialManagerFactory.GetCredentialManager();

// assert
Assert.IsInstanceOf<SnowflakeCredentialManagerFileImpl>(_credentialManager);
}

[Test]
public void TestThatThrowsErrorWhenCacheFileIsNotCreated()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Assert.Ignore("skip test on Windows");
}

// arrange
t_directoryOperations
.Setup(d => d.Exists(s_defaultJsonDir))
.Returns(false);
t_unixOperations
.Setup(u => u.CreateFileWithPermissions(s_customJsonPath,
FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR))
.Returns(-1);
t_environmentOperations
.Setup(e => e.GetEnvironmentVariable(SnowflakeCredentialManagerFileImpl.CredentialCacheDirectoryEnvironmentName))
.Returns(CustomJsonDir);
SnowflakeCredentialManagerFactory.SetCredentialManager(new SnowflakeCredentialManagerFileImpl(t_fileOperations.Object, t_directoryOperations.Object, t_unixOperations.Object, t_environmentOperations.Object));
_credentialManager = SnowflakeCredentialManagerFactory.GetCredentialManager();

// act
var thrown = Assert.Throws<Exception>(() => _credentialManager.SaveCredentials("key", "token"));

// assert
Assert.That(thrown.Message, Does.Contain("Failed to create the JSON token cache file"));
}

[Test]
public void TestThatThrowsErrorWhenCacheFileCanBeAccessedByOthers()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Assert.Ignore("skip test on Windows");
}

// arrange
t_unixOperations
.Setup(u => u.CreateFileWithPermissions(s_defaultJsonPath,
FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR))
.Returns(0);
t_unixOperations
.Setup(u => u.GetFilePermissions(s_defaultJsonPath))
.Returns(FileAccessPermissions.AllPermissions);
t_environmentOperations
.Setup(e => e.GetEnvironmentVariable(SnowflakeCredentialManagerFileImpl.CredentialCacheDirectoryEnvironmentName))
.Returns(CustomJsonDir);
SnowflakeCredentialManagerFactory.SetCredentialManager(new SnowflakeCredentialManagerFileImpl(t_fileOperations.Object, t_directoryOperations.Object, t_unixOperations.Object, t_environmentOperations.Object));
_credentialManager = SnowflakeCredentialManagerFactory.GetCredentialManager();

// act
var thrown = Assert.Throws<Exception>(() => _credentialManager.SaveCredentials("key", "token"));

// assert
Assert.That(thrown.Message, Does.Contain("Permission for the JSON token cache file should contain only the owner access"));
}
}
}
Loading
Loading