// Copyright (C) Stichting Deltares 2016. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using NUnit.Framework;
namespace Core.Common.TestUtil.Test
{
[TestFixture]
public class DirectoryPermissionsRevokerTest
{
private readonly TestDataPath testWorkDir = TestDataPath.Core.Common.TestUtils;
[Test]
[TestCase(null)]
[TestCase("")]
[TestCase(" ")]
public void Constructor_NullPath_ThrowsArgumentException(string invalidPath)
{
// Call
TestDelegate test = () => new DirectoryPermissionsRevoker(invalidPath, FileSystemRights.Modify);
// Assert
string paramName = Assert.Throws(test).ParamName;
Assert.AreEqual("folderPath", paramName);
}
[Test]
public void Constructor_PathDoesNotExist_ThrowsDirectoryNotFoundException()
{
// Setup
const string invalidPath = @".\DirectoryDoesNotExist\fileDoesNotExist";
// Call
TestDelegate test = () => new DirectoryPermissionsRevoker(invalidPath, FileSystemRights.Modify);
// Assert
Assert.Throws(test);
}
[Test]
public void Constructor_UnsupportedRight_ThrowsNotSupportedException()
{
// Setup
const FileSystemRights rights = FileSystemRights.Synchronize;
string rootFolder = TestHelper.GetTestDataPath(testWorkDir);
string subfolder = "UnsupportedRight" + rights;
string folderPath = Path.Combine(rootFolder, subfolder);
using (new DirectoryDisposeHelper(rootFolder, subfolder))
{
// Call
TestDelegate test = () => new DirectoryPermissionsRevoker(folderPath, rights);
// Assert
Assert.Throws(test);
}
}
[Test]
[TestCase(FileSystemRights.AppendData, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(AppendData)")]
[TestCase(FileSystemRights.ChangePermissions, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(ChangePermissions)")]
[TestCase(FileSystemRights.Delete, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(Delete)")]
[TestCase(FileSystemRights.DeleteSubdirectoriesAndFiles, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(DeleteSubdirectoriesAndFiles)")]
[TestCase(FileSystemRights.ExecuteFile, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(ExecuteFile)")]
[TestCase(FileSystemRights.FullControl, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(FullControl)")]
[TestCase(FileSystemRights.Modify, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(Modify)")]
[TestCase(FileSystemRights.Read, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(Read)")]
[TestCase(FileSystemRights.ReadAndExecute, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(ReadAndExecute)")]
[TestCase(FileSystemRights.ReadAttributes, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(ReadAttributes)")]
[TestCase(FileSystemRights.ReadData, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(ReadData)")]
[TestCase(FileSystemRights.ReadExtendedAttributes, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(ReadExtendedAttributes)")]
[TestCase(FileSystemRights.ReadPermissions, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(ReadPermissions)")]
[TestCase(FileSystemRights.TakeOwnership, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(TakeOwnership)")]
[TestCase(FileSystemRights.Write, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(Write)")]
[TestCase(FileSystemRights.WriteData, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(WriteData)")]
[TestCase(FileSystemRights.WriteAttributes, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(WriteAttributes)")]
[TestCase(FileSystemRights.WriteExtendedAttributes, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(WriteExtendedAttributes)")]
[TestCase(FileSystemRights.Delete | FileSystemRights.Read, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(DeleteRead)")]
[TestCase(FileSystemRights.Delete | FileSystemRights.Synchronize, TestName = "Constructor_ValidPathDenyRight_SetsDenyRight(DeleteSynchronize)")]
public void Constructor_ValidPathDenyRight_SetsDenyRight(FileSystemRights rights)
{
// Setup
string rootFolder = TestHelper.GetTestDataPath(testWorkDir);
string subfolder = "ValidPathDenyRight_SetsDenyRight" + rights;
string folderPath = Path.Combine(rootFolder, subfolder);
using (new DirectoryDisposeHelper(rootFolder, subfolder))
{
// Call
using (new DirectoryPermissionsRevoker(folderPath, rights))
{
// Assert
AssertPathHasAccessRuleSet(folderPath, rights);
}
AssertPathHasAccessRuleNotSet(folderPath, rights);
}
}
[Test]
[TestCase(FileSystemRights.AppendData, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(AppendData)")]
[TestCase(FileSystemRights.ChangePermissions, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(ChangePermissions)")]
[TestCase(FileSystemRights.Delete, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(Delete)")]
[TestCase(FileSystemRights.DeleteSubdirectoriesAndFiles, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(DeleteSubdirectoriesAndFiles)")]
[TestCase(FileSystemRights.ExecuteFile, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(ExecuteFile)")]
[TestCase(FileSystemRights.FullControl, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(FullControl)")]
[TestCase(FileSystemRights.Modify, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(Modify)")]
[TestCase(FileSystemRights.Read, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(Read)")]
[TestCase(FileSystemRights.ReadAndExecute, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(ReadAndExecute)")]
[TestCase(FileSystemRights.ReadAttributes, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(ReadAttributes)")]
[TestCase(FileSystemRights.ReadData, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(ReadData)")]
[TestCase(FileSystemRights.ReadExtendedAttributes, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(ReadExtendedAttributes)")]
[TestCase(FileSystemRights.ReadPermissions, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(ReadPermissions)")]
[TestCase(FileSystemRights.TakeOwnership, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(TakeOwnership)")]
[TestCase(FileSystemRights.Write, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(Write)")]
[TestCase(FileSystemRights.WriteData, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(WriteData)")]
[TestCase(FileSystemRights.WriteAttributes, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(WriteAttributes)")]
[TestCase(FileSystemRights.WriteExtendedAttributes, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(WriteExtendedAttributes)")]
[TestCase(FileSystemRights.Delete | FileSystemRights.Read, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(DeleteRead)")]
[TestCase(FileSystemRights.Delete | FileSystemRights.Synchronize, TestName = "Dispose_RightAlreadySet_DoesNotRemoveRight(DeleteSynchronize)")]
public void Dispose_RightAlreadySet_DoesNotRemoveRight(FileSystemRights rights)
{
// Setup
string rootFolder = TestHelper.GetTestDataPath(testWorkDir);
string subfolder = "RightAlreadySet" + rights;
string folderPath = Path.Combine(rootFolder, subfolder);
using (new DirectoryDisposeHelper(rootFolder, subfolder))
{
AddDirectoryAccessRule(folderPath, rights);
// Precondition
AssertPathHasAccessRuleSet(folderPath, rights);
try
{
// Call
using (new DirectoryPermissionsRevoker(folderPath, rights))
{
// Assert
AssertPathHasAccessRuleSet(folderPath, rights);
}
AssertPathHasAccessRuleSet(folderPath, rights);
}
finally
{
RemoveDirectoryAccessRule(folderPath, rights);
}
}
}
[Test]
public void Dispose_DirectoryAlreadyRemoved_DoesNotThrowException()
{
// Setup
string rootFolder = TestHelper.GetTestDataPath(testWorkDir);
string subfolder = "Deleted";
string folderPath = Path.Combine(rootFolder, subfolder);
using (new DirectoryDisposeHelper(rootFolder, subfolder))
{
TestDelegate test = () =>
{
// Call
using (new DirectoryPermissionsRevoker(folderPath, FileSystemRights.Write))
{
Directory.Delete(folderPath, true);
}
};
// Assert
Assert.DoesNotThrow(test);
}
}
#region Assert access rules
private static void AssertPathHasAccessRuleNotSet(string filePath, FileSystemRights rights)
{
FileSystemRights supportedFileSystemRights = GetSupportedFileSystemRights(rights);
FileSystemAccessRule fileSystemAccessRule = GetFirstFileSystemAccessRuleForRights(filePath, supportedFileSystemRights);
Assert.IsNull(fileSystemAccessRule, $"Rights '{AccessControlType.Deny} {supportedFileSystemRights}' are set for '{filePath}'");
}
private static void AssertPathHasAccessRuleSet(string filePath, FileSystemRights rights)
{
FileSystemRights supportedFileSystemRights = GetSupportedFileSystemRights(rights);
FileSystemAccessRule fileSystemAccessRule = GetFirstFileSystemAccessRuleForRights(filePath, supportedFileSystemRights);
Assert.IsNotNull(fileSystemAccessRule, $"Rights '{AccessControlType.Deny} {supportedFileSystemRights}' not set for '{filePath}'");
}
private static FileSystemRights GetSupportedFileSystemRights(FileSystemRights rights)
{
return rights & ~FileSystemRights.Synchronize;
}
private static FileSystemAccessRule GetFirstFileSystemAccessRuleForRights(string filePath, FileSystemRights supportedFileSystemRights)
{
var directoryInfo = new DirectoryInfo(filePath);
DirectorySecurity directorySecurity = directoryInfo.GetAccessControl();
AuthorizationRuleCollection rules = directorySecurity.GetAccessRules(true, false, typeof(SecurityIdentifier));
FileSystemAccessRule fileSystemAccessRule = rules.OfType()
.FirstOrDefault(fs => fs.FileSystemRights.HasFlag(supportedFileSystemRights) &&
fs.AccessControlType == AccessControlType.Deny);
return fileSystemAccessRule;
}
#endregion
#region Apply access rules
private static void AddDirectoryAccessRule(string filePath, FileSystemRights rights)
{
SecurityIdentifier sid = GetSecurityIdentifier();
DirectoryInfo directoryInfo = new DirectoryInfo(filePath);
var directorySecurity = directoryInfo.GetAccessControl();
var fileSystemAccessRule = new FileSystemAccessRule(sid, rights, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
PropagationFlags.None, AccessControlType.Deny);
directorySecurity.AddAccessRule(fileSystemAccessRule);
directoryInfo.SetAccessControl(directorySecurity);
}
private static void RemoveDirectoryAccessRule(string filePath, FileSystemRights rights)
{
SecurityIdentifier sid = GetSecurityIdentifier();
DirectoryInfo directoryInfo = new DirectoryInfo(filePath);
var directorySecurity = directoryInfo.GetAccessControl();
var fileSystemAccessRule = new FileSystemAccessRule(sid, rights, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
PropagationFlags.None, AccessControlType.Deny);
directorySecurity.RemoveAccessRule(fileSystemAccessRule);
directoryInfo.SetAccessControl(directorySecurity);
}
private static SecurityIdentifier GetSecurityIdentifier()
{
SecurityIdentifier id = WindowsIdentity.GetCurrent().User.AccountDomainSid;
return new SecurityIdentifier(WellKnownSidType.WorldSid, id);
}
#endregion
}
}