diff --git a/Snowflake.Data.Tests/UnitTests/CredentialManager/SFCredentialManager.cs b/Snowflake.Data.Tests/UnitTests/CredentialManager/SFCredentialManager.cs index 31693a28e..af2dd2686 100644 --- a/Snowflake.Data.Tests/UnitTests/CredentialManager/SFCredentialManager.cs +++ b/Snowflake.Data.Tests/UnitTests/CredentialManager/SFCredentialManager.cs @@ -28,6 +28,9 @@ class SFCredentialManager [ThreadStatic] private static Mock t_unixOperations; + [ThreadStatic] + private static Mock t_environmentOperations; + private static readonly string s_expectedJsonDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); private static readonly string s_expectedJsonPath = Path.Combine(s_expectedJsonDir, "temporary_credential.json"); @@ -37,6 +40,7 @@ [SetUp] public void SetUp() t_fileOperations = new Mock(); t_directoryOperations = new Mock(); t_unixOperations = new Mock(); + t_environmentOperations = new Mock(); SnowflakeCredentialManagerFactory.SetCredentialManager(SnowflakeCredentialManagerInMemoryImpl.Instance); } @@ -47,6 +51,7 @@ [TearDown] public void TearDown() private void TestCredentialManagerImplementation() { + // arrange var key = SnowflakeCredentialManagerFactory.BuildCredentialKey("host", "user", "tokentype"); var expectedToken = "token"; @@ -122,6 +127,8 @@ public void TestAdysTechCredentialManager() // arrange SnowflakeCredentialManagerFactory.SetCredentialManager(SnowflakeCredentialManagerAdysTechImpl.Instance); _credentialManager = SnowflakeCredentialManagerFactory.GetCredentialManager(); + + // act & assert TestCredentialManagerImplementation(); } @@ -131,6 +138,8 @@ public void TestInMemoryCredentialManager() // arrange SnowflakeCredentialManagerFactory.SetCredentialManager(SnowflakeCredentialManagerInMemoryImpl.Instance); _credentialManager = SnowflakeCredentialManagerFactory.GetCredentialManager(); + + // act & assert TestCredentialManagerImplementation(); } @@ -140,6 +149,8 @@ public void TestJsonCredentialManager() // arrange SnowflakeCredentialManagerFactory.SetCredentialManager(SnowflakeCredentialManagerIFileImpl.Instance); _credentialManager = SnowflakeCredentialManagerFactory.GetCredentialManager(); + + // act & assert TestCredentialManagerImplementation(); } @@ -152,23 +163,21 @@ public void TestThatThrowsErrorWhenCacheFileIsNotCreated() } // arrange - var key = SnowflakeCredentialManagerFactory.BuildCredentialKey("host", "user", "tokentype"); - var token = "token"; - t_directoryOperations .Setup(d => d.Exists(s_expectedJsonDir)) .Returns(false); - t_unixOperations .Setup(u => u.CreateFileWithPermissions(s_expectedJsonPath, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR)) .Returns(-1); - - SnowflakeCredentialManagerFactory.SetCredentialManager(new SnowflakeCredentialManagerIFileImpl(t_fileOperations.Object, t_directoryOperations.Object, t_unixOperations.Object)); + t_environmentOperations + .Setup(e => e.GetEnvironmentVariable(SnowflakeCredentialManagerIFileImpl.CredentialCacheDirectoryEnvironmentName)) + .Returns("testdirectory"); + SnowflakeCredentialManagerFactory.SetCredentialManager(new SnowflakeCredentialManagerIFileImpl(t_fileOperations.Object, t_directoryOperations.Object, t_unixOperations.Object, t_environmentOperations.Object)); _credentialManager = SnowflakeCredentialManagerFactory.GetCredentialManager(); // act - var thrown = Assert.Throws(() => _credentialManager.SaveCredentials(key, token)); + var thrown = Assert.Throws(() => _credentialManager.SaveCredentials("key", "token")); // assert Assert.That(thrown.Message, Does.Contain("Failed to create the JSON token cache file")); @@ -183,9 +192,6 @@ public void TestThatThrowsErrorWhenCacheFileCanBeAccessedByOthers() } // arrange - var key = SnowflakeCredentialManagerFactory.BuildCredentialKey("host", "user", "tokentype"); - var token = "token"; - t_unixOperations .Setup(u => u.CreateFileWithPermissions(s_expectedJsonPath, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR)) @@ -193,12 +199,14 @@ public void TestThatThrowsErrorWhenCacheFileCanBeAccessedByOthers() t_unixOperations .Setup(u => u.GetFilePermissions(s_expectedJsonPath)) .Returns(FileAccessPermissions.AllPermissions); - - SnowflakeCredentialManagerFactory.SetCredentialManager(new SnowflakeCredentialManagerIFileImpl(t_fileOperations.Object, t_directoryOperations.Object, t_unixOperations.Object)); + t_environmentOperations + .Setup(e => e.GetEnvironmentVariable(SnowflakeCredentialManagerIFileImpl.CredentialCacheDirectoryEnvironmentName)) + .Returns("testdirectory"); + SnowflakeCredentialManagerFactory.SetCredentialManager(new SnowflakeCredentialManagerIFileImpl(t_fileOperations.Object, t_directoryOperations.Object, t_unixOperations.Object, t_environmentOperations.Object)); _credentialManager = SnowflakeCredentialManagerFactory.GetCredentialManager(); // act - var thrown = Assert.Throws(() => _credentialManager.SaveCredentials(key, token)); + var thrown = Assert.Throws(() => _credentialManager.SaveCredentials("key", "token")); // assert Assert.That(thrown.Message, Does.Contain("Permission for the JSON token cache file should contain only the owner access")); diff --git a/Snowflake.Data.Tests/UnitTests/SFSessionTest.cs b/Snowflake.Data.Tests/UnitTests/SFSessionTest.cs index b9530b83b..e96f9d792 100644 --- a/Snowflake.Data.Tests/UnitTests/SFSessionTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFSessionTest.cs @@ -9,6 +9,7 @@ namespace Snowflake.Data.Tests.UnitTests { using Snowflake.Data.Core; using NUnit.Framework; + using System; [TestFixture] class SFSessionTest @@ -61,5 +62,22 @@ public void TestThatConfiguresEasyLogging(string configPath) // assert easyLoggingStarter.Verify(starter => starter.Init(configPath)); } + + [Test] + public void TestThatRetriesAuthenticationForInvalidIdToken() + { + // arrange + var connectionString = "account=test;user=test;password=test;allow_sso_token_caching=true"; + var session = new SFSession(connectionString, null); + LoginResponse authnResponse = new LoginResponse + { + code = SFError.ID_TOKEN_INVALID.GetAttribute().errorCode, + message = "", + success = false + }; + + // assert + Assert.Throws(() => session.ProcessLoginResponse(authnResponse)); + } } } diff --git a/Snowflake.Data/Client/SnowflakeCredentialManager/SnowflakeCredentialManagerIFileImpl.cs b/Snowflake.Data/Client/SnowflakeCredentialManager/SnowflakeCredentialManagerIFileImpl.cs index 89b4bcade..521425e36 100644 --- a/Snowflake.Data/Client/SnowflakeCredentialManager/SnowflakeCredentialManagerIFileImpl.cs +++ b/Snowflake.Data/Client/SnowflakeCredentialManager/SnowflakeCredentialManagerIFileImpl.cs @@ -18,17 +18,13 @@ namespace Snowflake.Data.Client { public class SnowflakeCredentialManagerIFileImpl : ISnowflakeCredentialManager { - private const string CredentialCacheDirectoryEnvironmentName = "SF_TEMPORARY_CREDENTIAL_CACHE_DIR"; - - private static readonly string CustomCredentialCacheDirectory = Environment.GetEnvironmentVariable(CredentialCacheDirectoryEnvironmentName); - - private static readonly string DefaultCredentialCacheDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - - private static readonly string JsonCacheDirectory = string.IsNullOrEmpty(CustomCredentialCacheDirectory) ? DefaultCredentialCacheDirectory : CustomCredentialCacheDirectory; + internal const string CredentialCacheDirectoryEnvironmentName = "SF_TEMPORARY_CREDENTIAL_CACHE_DIR"; private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger(); - private static readonly string s_jsonPath = Path.Combine(JsonCacheDirectory, "temporary_credential.json"); + private readonly string _jsonCacheDirectory; + + private readonly string _jsonCacheFilePath; private readonly FileOperations _fileOperations; @@ -36,21 +32,32 @@ public class SnowflakeCredentialManagerIFileImpl : ISnowflakeCredentialManager private readonly UnixOperations _unixOperations; - public static readonly SnowflakeCredentialManagerIFileImpl Instance = new SnowflakeCredentialManagerIFileImpl(FileOperations.Instance, DirectoryOperations.Instance, UnixOperations.Instance); + private readonly EnvironmentOperations _environmentOperations; - internal SnowflakeCredentialManagerIFileImpl(FileOperations fileOperations, DirectoryOperations directoryOperations, UnixOperations unixOperations) + public static readonly SnowflakeCredentialManagerIFileImpl Instance = new SnowflakeCredentialManagerIFileImpl(FileOperations.Instance, DirectoryOperations.Instance, UnixOperations.Instance, EnvironmentOperations.Instance); + + internal SnowflakeCredentialManagerIFileImpl(FileOperations fileOperations, DirectoryOperations directoryOperations, UnixOperations unixOperations, EnvironmentOperations environmentOperations) { _fileOperations = fileOperations; _directoryOperations = directoryOperations; _unixOperations = unixOperations; + _environmentOperations = environmentOperations; + SetCredentialCachePath(ref _jsonCacheDirectory, ref _jsonCacheFilePath); + } + + private void SetCredentialCachePath(ref string _jsonCacheDirectory, ref string _jsonCacheFilePath) + { + var customDirectory = _environmentOperations.GetEnvironmentVariable(CredentialCacheDirectoryEnvironmentName); + _jsonCacheDirectory = string.IsNullOrEmpty(customDirectory) ? _environmentOperations.GetFolderPath(Environment.SpecialFolder.UserProfile) : customDirectory; + _jsonCacheFilePath = Path.Combine(_jsonCacheDirectory, "temporary_credential.json"); } internal void WriteToJsonFile(string content) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - _fileOperations.Write(s_jsonPath, content); - FileInfo info = new FileInfo(s_jsonPath); + _fileOperations.Write(_jsonCacheFilePath, content); + FileInfo info = new FileInfo(_jsonCacheFilePath); FileSecurity security = info.GetAccessControl(); FileSystemAccessRule rule = new FileSystemAccessRule( new SecurityIdentifier(WellKnownSidType.CreatorOwnerSid, null), @@ -61,11 +68,11 @@ internal void WriteToJsonFile(string content) } else { - if (!_directoryOperations.Exists(JsonCacheDirectory)) + if (!_directoryOperations.Exists(_jsonCacheDirectory)) { - _directoryOperations.CreateDirectory(JsonCacheDirectory); + _directoryOperations.CreateDirectory(_jsonCacheDirectory); } - var createFileResult = _unixOperations.CreateFileWithPermissions(s_jsonPath, + var createFileResult = _unixOperations.CreateFileWithPermissions(_jsonCacheFilePath, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR); if (createFileResult == -1) { @@ -75,10 +82,10 @@ internal void WriteToJsonFile(string content) } else { - _fileOperations.Write(s_jsonPath, content); + _fileOperations.Write(_jsonCacheFilePath, content); } - var jsonPermissions = _unixOperations.GetFilePermissions(s_jsonPath); + var jsonPermissions = _unixOperations.GetFilePermissions(_jsonCacheFilePath); if (jsonPermissions != FileAccessPermissions.UserReadWriteExecute) { var errorMessage = "Permission for the JSON token cache file should contain only the owner access"; @@ -90,12 +97,12 @@ internal void WriteToJsonFile(string content) internal KeyToken ReadJsonFile() { - return JsonConvert.DeserializeObject(File.ReadAllText(s_jsonPath)); + return JsonConvert.DeserializeObject(File.ReadAllText(_jsonCacheFilePath)); } public string GetCredentials(string key) { - if (_fileOperations.Exists(s_jsonPath)) + if (_fileOperations.Exists(_jsonCacheFilePath)) { var keyTokenPairs = ReadJsonFile(); @@ -111,7 +118,7 @@ public string GetCredentials(string key) public void RemoveCredentials(string key) { - if (_fileOperations.Exists(s_jsonPath)) + if (_fileOperations.Exists(_jsonCacheFilePath)) { var keyTokenPairs = ReadJsonFile(); keyTokenPairs.Remove(key); @@ -121,7 +128,7 @@ public void RemoveCredentials(string key) public void SaveCredentials(string key, string token) { - KeyToken keyTokenPairs = _fileOperations.Exists(s_jsonPath) ? ReadJsonFile() : new KeyToken(); + KeyToken keyTokenPairs = _fileOperations.Exists(_jsonCacheFilePath) ? ReadJsonFile() : new KeyToken(); keyTokenPairs[key] = token; string jsonString = JsonConvert.SerializeObject(keyTokenPairs);