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-990111 Easy Logging more tests and permission check fix #862

Merged
merged 4 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading