diff --git a/Snowflake.Data.Tests/IntegrationTests/EasyLoggingIT.cs b/Snowflake.Data.Tests/IntegrationTests/EasyLoggingIT.cs new file mode 100644 index 000000000..52a981f8c --- /dev/null +++ b/Snowflake.Data.Tests/IntegrationTests/EasyLoggingIT.cs @@ -0,0 +1,97 @@ +using System.Data; +using System.IO; +using System.Runtime.InteropServices; +using Mono.Unix.Native; +using NUnit.Framework; +using Snowflake.Data.Client; +using Snowflake.Data.Configuration; +using Snowflake.Data.Core; +using Snowflake.Data.Log; +using static Snowflake.Data.Tests.UnitTests.Configuration.EasyLoggingConfigGenerator; + +namespace Snowflake.Data.Tests.IntegrationTests +{ + [TestFixture, NonParallelizable] + public class EasyLoggingIT: SFBaseTest + { + [OneTimeSetUp] + public static void BeforeAll() + { + if (!Directory.Exists(WorkingDirectory)) + { + Directory.CreateDirectory(WorkingDirectory); + } + } + + [OneTimeTearDown] + public static void AfterAll() + { + Directory.Delete(WorkingDirectory, true); + } + + [TearDown] + public static void AfterEach() + { + EasyLoggingStarter.Instance.Reset(EasyLoggingLogLevel.Warn); + } + + [Test] + public void TestEnableEasyLogging() + { + // arrange + var configFilePath = CreateConfigTempFile(Config("WARN", WorkingDirectory)); + using (IDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString + $"CLIENT_CONFIG_FILE={configFilePath}"; + + // act + conn.Open(); + + // assert + Assert.IsTrue(EasyLoggerManager.HasEasyLoggingAppender()); + } + } + + [Test] + public void TestFailToEnableEasyLoggingForWrongConfiguration() + { + // arrange + var configFilePath = CreateConfigTempFile("random config content"); + using (IDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString + $"CLIENT_CONFIG_FILE={configFilePath}"; + + // act + var thrown = Assert.Throws(() => conn.Open()); + + // assert + Assert.That(thrown.Message, Does.Contain("Connection string is invalid: Unable to connect")); + Assert.IsFalse(EasyLoggerManager.HasEasyLoggingAppender()); + } + } + + [Test] + public void TestFailToEnableEasyLoggingWhenConfigHasWrongPermissions() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Ignore("skip test on Windows"); + } + + // arrange + var configFilePath = CreateConfigTempFile(Config("WARN", WorkingDirectory)); + Syscall.chmod(configFilePath, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IWGRP); + using (IDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString + $"CLIENT_CONFIG_FILE={configFilePath}"; + + // act + var thrown = Assert.Throws(() => conn.Open()); + + // assert + Assert.That(thrown.Message, Does.Contain("Connection string is invalid: Unable to connect")); + Assert.IsFalse(EasyLoggerManager.HasEasyLoggingAppender()); + } + } + } +} \ No newline at end of file diff --git a/Snowflake.Data.Tests/Tools/UnixOperationsTest.cs b/Snowflake.Data.Tests/Tools/UnixOperationsTest.cs new file mode 100644 index 000000000..3e52f1eb8 --- /dev/null +++ b/Snowflake.Data.Tests/Tools/UnixOperationsTest.cs @@ -0,0 +1,115 @@ +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using Mono.Unix; +using Mono.Unix.Native; +using NUnit.Framework; +using Snowflake.Data.Core.Tools; +using static Snowflake.Data.Tests.UnitTests.Configuration.EasyLoggingConfigGenerator; + +namespace Snowflake.Data.Tests.Tools +{ + [TestFixture, NonParallelizable] + public class UnixOperationsTest + { + private static UnixOperations s_unixOperations; + + [OneTimeSetUp] + public static void BeforeAll() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return; + if (!Directory.Exists(WorkingDirectory)) + { + Directory.CreateDirectory(WorkingDirectory); + } + s_unixOperations = new UnixOperations(); + } + + [OneTimeTearDown] + public static void AfterAll() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return; + Directory.Delete(WorkingDirectory, true); + } + + [Test] + public void TestDetectGroupOrOthersWritablePermissions( + [ValueSource(nameof(GroupOrOthersWritablePermissions))] FilePermissions groupOrOthersWritablePermissions, + [ValueSource(nameof(GroupNotWritablePermissions))] FilePermissions groupNotWritablePermissions, + [ValueSource(nameof(OtherNotWritablePermissions))] FilePermissions otherNotWritablePermissions) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Ignore("skip test on Windows"); + } + + // arrange + var filePath = CreateConfigTempFile("random text"); + var readWriteUserPermissions = FilePermissions.S_IRUSR | FilePermissions.S_IWUSR; + var filePermissions = readWriteUserPermissions | groupOrOthersWritablePermissions | groupNotWritablePermissions | otherNotWritablePermissions; + Syscall.chmod(filePath, filePermissions); + + // act + var result = s_unixOperations.CheckFileHasAnyOfPermissions(filePath, FileAccessPermissions.GroupWrite | FileAccessPermissions.OtherWrite); + + // assert + Assert.IsTrue(result); + } + + [Test] + public void TestDetectGroupOrOthersNotWritablePermissions( + [ValueSource(nameof(UserPermissions))] FilePermissions userPermissions, + [ValueSource(nameof(GroupNotWritablePermissions))] FilePermissions groupNotWritablePermissions, + [ValueSource(nameof(OtherNotWritablePermissions))] FilePermissions otherNotWritablePermissions) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Ignore("skip test on Windows"); + } + + var filePath = CreateConfigTempFile("random text"); + var filePermissions = userPermissions | groupNotWritablePermissions | otherNotWritablePermissions; + Syscall.chmod(filePath, filePermissions); + + // act + var result = s_unixOperations.CheckFileHasAnyOfPermissions(filePath, FileAccessPermissions.GroupWrite | FileAccessPermissions.OtherWrite); + + // assert + Assert.IsFalse(result); + } + + + public static IEnumerable UserPermissions() + { + yield return FilePermissions.S_IRUSR; + yield return FilePermissions.S_IWUSR; + yield return FilePermissions.S_IXUSR; + yield return FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR; + } + + public static IEnumerable GroupOrOthersWritablePermissions() + { + yield return FilePermissions.S_IWGRP; + yield return FilePermissions.S_IWOTH; + yield return FilePermissions.S_IWGRP | FilePermissions.S_IWOTH; + } + + public static IEnumerable GroupNotWritablePermissions() + { + yield return 0; + yield return FilePermissions.S_IRGRP; + yield return FilePermissions.S_IXGRP; + yield return FilePermissions.S_IRGRP | FilePermissions.S_IXGRP; + } + + public static IEnumerable OtherNotWritablePermissions() + { + yield return 0; + yield return FilePermissions.S_IROTH; + yield return FilePermissions.S_IXOTH; + yield return FilePermissions.S_IROTH | FilePermissions.S_IXOTH; + } + } +} diff --git a/Snowflake.Data.Tests/UnitTests/Configuration/EasyLoggingConfigFinderTest.cs b/Snowflake.Data.Tests/UnitTests/Configuration/EasyLoggingConfigFinderTest.cs index bea1e14d0..ba4460531 100644 --- a/Snowflake.Data.Tests/UnitTests/Configuration/EasyLoggingConfigFinderTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Configuration/EasyLoggingConfigFinderTest.cs @@ -194,7 +194,7 @@ public void TestThatDoesNotFailWhenHomeDirectoryDoesNotExist() private static void MockHasFlagReturnsTrue() { t_unixOperations - .Setup(f => f.CheckFileHasPermissions(s_homeConfigFilePath, + .Setup(f => f.CheckFileHasAnyOfPermissions(s_homeConfigFilePath, It.Is(p => p.Equals(FileAccessPermissions.GroupWrite | FileAccessPermissions.OtherWrite)))) .Returns(true); } diff --git a/Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs b/Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs index 3fed8850e..fab097027 100644 --- a/Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs +++ b/Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs @@ -107,7 +107,7 @@ private void CheckIfValidPermissions(string filePath) return; // Check if others have permissions to modify the file and fail if so - if (_unixOperations.CheckFileHasPermissions(filePath, FileAccessPermissions.GroupWrite | FileAccessPermissions.OtherWrite)) + if (_unixOperations.CheckFileHasAnyOfPermissions(filePath, FileAccessPermissions.GroupWrite | FileAccessPermissions.OtherWrite)) { var errorMessage = $"Error due to other users having permission to modify the config file: {filePath}"; s_logger.Error(errorMessage); diff --git a/Snowflake.Data/Core/Session/EasyLoggingStarter.cs b/Snowflake.Data/Core/Session/EasyLoggingStarter.cs index cd82d3904..894e9308a 100644 --- a/Snowflake.Data/Core/Session/EasyLoggingStarter.cs +++ b/Snowflake.Data/Core/Session/EasyLoggingStarter.cs @@ -83,6 +83,15 @@ public virtual void Init(string configFilePathFromConnectionString) } } + internal void Reset(EasyLoggingLogLevel logLevel) + { + lock (_lockForExclusiveInit) + { + _initTrialParameters = null; + _easyLoggerManager.ResetEasyLogging(logLevel); + } + } + private bool AllowedToInitialize(string configFilePathFromConnectionString) { var everTriedToInitialize = _initTrialParameters != null; diff --git a/Snowflake.Data/Core/Tools/UnixOperations.cs b/Snowflake.Data/Core/Tools/UnixOperations.cs index d0e1fdf36..2437e7b74 100644 --- a/Snowflake.Data/Core/Tools/UnixOperations.cs +++ b/Snowflake.Data/Core/Tools/UnixOperations.cs @@ -22,10 +22,10 @@ public virtual FileAccessPermissions GetDirPermissions(string path) return dirInfo.FileAccessPermissions; } - public virtual bool CheckFileHasPermissions(string path, FileAccessPermissions permissions) + public virtual bool CheckFileHasAnyOfPermissions(string path, FileAccessPermissions permissions) { var fileInfo = new UnixFileInfo(path); - return fileInfo.FileAccessPermissions.HasFlag(permissions); + return (permissions & fileInfo.FileAccessPermissions) != 0; } } } diff --git a/Snowflake.Data/Logger/EasyLoggerManager.cs b/Snowflake.Data/Logger/EasyLoggerManager.cs index da1748912..ca7800f0e 100644 --- a/Snowflake.Data/Logger/EasyLoggerManager.cs +++ b/Snowflake.Data/Logger/EasyLoggerManager.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using System.Linq; using log4net; using log4net.Appender; using log4net.Layout; @@ -36,6 +37,26 @@ public virtual void ReconfigureEasyLogging(EasyLoggingLogLevel easyLoggingLogLev repository.RaiseConfigurationChanged(EventArgs.Empty); } } + + internal void ResetEasyLogging(EasyLoggingLogLevel easyLoggingLogLevel) + { + var log4netLevel = _levelMapper.ToLog4NetLevel(easyLoggingLogLevel); + lock (_lockForExclusiveConfigure) + { + var repository = (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository(); + var rootLogger = (log4net.Repository.Hierarchy.Logger)repository.GetLogger("Snowflake.Data"); + rootLogger.Level = log4netLevel; + RemoveOtherEasyLoggingAppenders(rootLogger, null); + repository.RaiseConfigurationChanged(EventArgs.Empty); + } + } + + internal static bool HasEasyLoggingAppender() + { + var repository = (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository(); + var rootLogger = (log4net.Repository.Hierarchy.Logger)repository.GetLogger("Snowflake.Data"); + return rootLogger.Appenders.ToArray().Any(IsEasyLoggingAppender); + } private static void RemoveOtherEasyLoggingAppenders(log4net.Repository.Hierarchy.Logger logger, IAppender appender) {