Skip to content

Commit

Permalink
SNOW-990111 Easy Logging more tests and permission check fix (#862)
Browse files Browse the repository at this point in the history
### Description
Easy Logging more tests and permission check fix

### Checklist
- [x] Code compiles correctly
- [x] Code is formatted according to [Coding
Conventions](../CodingConventions.md)
- [x] Created tests which fail without the change (if possible)
- [x] All tests passing (`dotnet test`)
- [x] Extended the README / documentation, if necessary
- [x] Provide JIRA issue id (if possible) or GitHub issue id in PR name
  • Loading branch information
sfc-gh-knozderko authored Feb 8, 2024
1 parent 80bfb2c commit 9da3d62
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 20 deletions.
99 changes: 99 additions & 0 deletions Snowflake.Data.Tests/IntegrationTests/EasyLoggingIT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
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
{
private static readonly string s_workingDirectory = Path.Combine(Path.GetTempPath(), "easy_logging_test_configs_", Path.GetRandomFileName());

[OneTimeSetUp]
public static void BeforeAll()
{
if (!Directory.Exists(s_workingDirectory))
{
Directory.CreateDirectory(s_workingDirectory);
}
}

[OneTimeTearDown]
public static void AfterAll()
{
Directory.Delete(s_workingDirectory, true);
}

[TearDown]
public static void AfterEach()
{
EasyLoggingStarter.Instance.Reset(EasyLoggingLogLevel.Warn);
}

[Test]
public void TestEnableEasyLogging()
{
// arrange
var configFilePath = CreateConfigTempFile(s_workingDirectory, Config("WARN", s_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(s_workingDirectory, "random config content");
using (IDbConnection conn = new SnowflakeDbConnection())
{
conn.ConnectionString = ConnectionString + $"CLIENT_CONFIG_FILE={configFilePath}";

// act
var thrown = Assert.Throws<SnowflakeDbException>(() => 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(s_workingDirectory, Config("WARN", s_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<SnowflakeDbException>(() => conn.Open());

// assert
Assert.That(thrown.Message, Does.Contain("Connection string is invalid: Unable to connect"));
Assert.IsFalse(EasyLoggerManager.HasEasyLoggingAppender());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<FileAccessPermissions>(p => p.Equals(FileAccessPermissions.GroupWrite | FileAccessPermissions.OtherWrite))))
.Returns(true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ public class EasyLoggingConfigGenerator
public const string EmptyCommonConfig = @"{
""common"": {}
}";

public static readonly string WorkingDirectory = Path.Combine(Path.GetTempPath(), "easy_logging_test_configs_", Path.GetRandomFileName());

public static string CreateConfigTempFile(string fileContent)

public static string CreateConfigTempFile(string workingDirectory, string fileContent)
{
var filePath = NewConfigFilePath();
var filePath = NewConfigFilePath(workingDirectory);
using (var writer = File.CreateText(filePath))
{
writer.Write(fileContent);
Expand All @@ -26,9 +24,9 @@ public static string CreateConfigTempFile(string fileContent)
return filePath;
}

private static string NewConfigFilePath()
private static string NewConfigFilePath(string workingDirectory)
{
return Path.Combine(WorkingDirectory, Path.GetRandomFileName());
return Path.Combine(workingDirectory, Path.GetRandomFileName());
}

public static string Config(string logLevel, string logPath)
Expand All @@ -40,7 +38,8 @@ public static string Config(string logLevel, string logPath)
}
}"
.Replace("{logLevel}", SerializeParameter(logLevel))
.Replace("{logPath}", SerializeParameter(logPath));
.Replace("{logPath}", SerializeParameter(logPath))
.Replace("\\", "\\\\");
}

private static string SerializeParameter(string parameter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,29 @@ public class EasyLoggingConfigParserTest
private const string NotExistingFilePath = "../../../Resources/EasyLogging/not_existing_config.json";
private const string LogLevel = "info";
private const string LogPath = "./test-logs/log_file.log";
private static readonly string s_workingDirectory = Path.Combine(Path.GetTempPath(), "easy_logging_test_configs_", Path.GetRandomFileName());

[OneTimeSetUp]
public static void BeforeAll()
{
if (!Directory.Exists(WorkingDirectory))
if (!Directory.Exists(s_workingDirectory))
{
Directory.CreateDirectory(WorkingDirectory);
Directory.CreateDirectory(s_workingDirectory);
}
}

[OneTimeTearDown]
public static void AfterAll()
{
Directory.Delete(WorkingDirectory, true);
Directory.Delete(s_workingDirectory, true);
}

[Test]
public void TestThatParsesConfigFile()
{
// arrange
var parser = new EasyLoggingConfigParser();
var configFilePath = CreateConfigTempFile(Config(LogLevel, LogPath));
var configFilePath = CreateConfigTempFile(s_workingDirectory, Config(LogLevel, LogPath));

// act
var config = parser.Parse(configFilePath);
Expand Down Expand Up @@ -113,8 +114,8 @@ public static IEnumerable<string> ConfigFilesWithoutValues()
BeforeAll();
return new[]
{
CreateConfigTempFile(EmptyCommonConfig),
CreateConfigTempFile(Config(null, null))
CreateConfigTempFile(s_workingDirectory, EmptyCommonConfig),
CreateConfigTempFile(s_workingDirectory, Config(null, null))
};
}

Expand All @@ -123,8 +124,8 @@ public static IEnumerable<string> WrongConfigFiles()
BeforeAll();
return new[]
{
CreateConfigTempFile(EmptyConfig),
CreateConfigTempFile(Config("unknown", LogPath)),
CreateConfigTempFile(s_workingDirectory, EmptyConfig),
CreateConfigTempFile(s_workingDirectory, Config("unknown", LogPath)),
};
}
}
Expand Down
116 changes: 116 additions & 0 deletions Snowflake.Data.Tests/UnitTests/Tools/UnixOperationsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
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;
private static readonly string s_workingDirectory = Path.Combine(Path.GetTempPath(), "easy_logging_test_configs_", Path.GetRandomFileName());

[OneTimeSetUp]
public static void BeforeAll()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
if (!Directory.Exists(s_workingDirectory))
{
Directory.CreateDirectory(s_workingDirectory);
}
s_unixOperations = new UnixOperations();
}

[OneTimeTearDown]
public static void AfterAll()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
Directory.Delete(s_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(s_workingDirectory, "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(s_workingDirectory, "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<FilePermissions> 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<FilePermissions> GroupOrOthersWritablePermissions()
{
yield return FilePermissions.S_IWGRP;
yield return FilePermissions.S_IWOTH;
yield return FilePermissions.S_IWGRP | FilePermissions.S_IWOTH;
}

public static IEnumerable<FilePermissions> 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<FilePermissions> OtherNotWritablePermissions()
{
yield return 0;
yield return FilePermissions.S_IROTH;
yield return FilePermissions.S_IXOTH;
yield return FilePermissions.S_IROTH | FilePermissions.S_IXOTH;
}
}
}
2 changes: 1 addition & 1 deletion Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions Snowflake.Data/Core/Session/EasyLoggingStarter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions Snowflake.Data/Core/Tools/UnixOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
21 changes: 21 additions & 0 deletions Snowflake.Data/Logger/EasyLoggerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.IO;
using System.Linq;
using log4net;
using log4net.Appender;
using log4net.Layout;
Expand Down Expand Up @@ -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)
{
Expand Down

0 comments on commit 9da3d62

Please sign in to comment.