From e9e8a45808356a0945df423dc0c62ff116e59a42 Mon Sep 17 00:00:00 2001 From: Andrea Cattaneo Date: Thu, 4 Aug 2022 10:15:45 +0200 Subject: [PATCH] FileUtils - workaround for file permission on move --- ACUtils.AXRepository/ArxivarRepository.cs | 17 +-- ACUtils.FileUtils/ACUtils.FileUtils.csproj | 8 +- ACUtils.FileUtils/FileUtils.cs | 49 +++++-- Tests/FileUtilsTests.cs | 157 +++++++++++++++++++++ 4 files changed, 204 insertions(+), 27 deletions(-) diff --git a/ACUtils.AXRepository/ArxivarRepository.cs b/ACUtils.AXRepository/ArxivarRepository.cs index c0eb31f..396a892 100644 --- a/ACUtils.AXRepository/ArxivarRepository.cs +++ b/ACUtils.AXRepository/ArxivarRepository.cs @@ -766,7 +766,7 @@ public string[] DownloadAttachments(int docnumber, string outputFolder, bool ign var attachment = attachmentsApi.AttachmentsGetById(info.Id); var doc = documentsApi.DocumentsGetForExternalAttachment(info.Id, false); var path = Path.Combine(outputFolder, attachment.Originalname); - _write_stream_to_file(doc, path); + FileUtils.Write(doc, path).Close(); // fix ACL ( permessi ) sul file try @@ -830,7 +830,7 @@ public string DownloadDocument(long docnumber, string outputFolder, bool forView using (stream) { var fullPath = Path.Combine(outputFolder, filename); - _write_stream_to_file(stream, fullPath).Close(); + FileUtils.Write(stream, fullPath).Close(); return fullPath; } } @@ -1452,18 +1452,5 @@ public void DeleteWorkflow(int? processId) #endregion - public Stream _write_stream_to_file(Stream stream, string filepath) - { - var fileStream = File.Create(filepath); - stream.Seek(0, SeekOrigin.Begin); - stream.CopyTo(fileStream); - fileStream.Close(); - return stream; - } - public void _write_stream_to_file(byte[] stream, string filepath) - { - File.WriteAllBytes(filepath, stream); - } - } } diff --git a/ACUtils.FileUtils/ACUtils.FileUtils.csproj b/ACUtils.FileUtils/ACUtils.FileUtils.csproj index afb8c36..5c1ef77 100644 --- a/ACUtils.FileUtils/ACUtils.FileUtils.csproj +++ b/ACUtils.FileUtils/ACUtils.FileUtils.csproj @@ -1,12 +1,12 @@ - + net461;netstandard2.0 Andrea Cattaneo true false - 1.0.0.141 - 1.0.0.141 + 1.0.0.142 + 1.0.0.142 Collezione di utility varie it true @@ -15,7 +15,7 @@ - + diff --git a/ACUtils.FileUtils/FileUtils.cs b/ACUtils.FileUtils/FileUtils.cs index f4200d3..2f5f39c 100644 --- a/ACUtils.FileUtils/FileUtils.cs +++ b/ACUtils.FileUtils/FileUtils.cs @@ -3,6 +3,7 @@ using System.IO; using System.IO.Compression; using System.Linq; +using System.Security.AccessControl; using System.Text; using System.Text.RegularExpressions; using System.Xml; @@ -131,12 +132,16 @@ public static string MoveFile(string sFilePathSrc, string sFilePathDest, bool bO } // move - File.Move(sourceFileName: sFilePathSrc, destFileName: sFilePathDest); - if (fixAcl) { + File.Copy(sourceFileName: sFilePathSrc, destFileName: sFilePathDest); + File.Delete(sFilePathSrc); CopyAcl(sFilePathDest); } + else + { + File.Move(sourceFileName: sFilePathSrc, destFileName: sFilePathDest); + } return sFilePathDest; } @@ -193,19 +198,47 @@ public static bool CopyAcl(string sPathSrc, string sPathDest, bool throwExceptio { try { - System.Security.AccessControl.FileSecurity directoryAcl = new FileInfo(sPathSrc).GetAccessControl(); - new FileInfo(sPathDest).SetAccessControl(directoryAcl); + CopyAcl(sPathSrc, sPathDest, AccessControlSections.Audit); return true; } - catch (Exception e) + catch { - if (throwException) + try { - throw e; + CopyAcl(sPathSrc, sPathDest, null); + return true; } - return false; + catch + { + if (throwException) + { + throw; + } + return false; + } + } + } + + + + + private static bool CopyAcl(string sPathSrc, string sPathDest, AccessControlSections? accessControlSections = null) + { + if (IsDirectory(sPathDest) && IsDirectory(sPathSrc)) + { + FileSystemSecurity acl = accessControlSections == null ? (new DirectoryInfo(sPathSrc)).GetAccessControl() : (new DirectoryInfo(sPathSrc)).GetAccessControl(accessControlSections.Value); + acl.SetAuditRuleProtection(false, false); + new DirectoryInfo(sPathDest).SetAccessControl(acl as DirectorySecurity); + } + else + { + FileSystemSecurity acl = accessControlSections == null ? (new FileInfo(sPathSrc)).GetAccessControl() : (new FileInfo(sPathSrc)).GetAccessControl(accessControlSections.Value); + acl.SetAuditRuleProtection(false, false); + new FileInfo(sPathDest).SetAccessControl(acl as FileSecurity); } + return true; } + /// /// applica le ACL della cartella in cui è contenuto il file al file /// diff --git a/Tests/FileUtilsTests.cs b/Tests/FileUtilsTests.cs index d6b7fb0..ed8a639 100644 --- a/Tests/FileUtilsTests.cs +++ b/Tests/FileUtilsTests.cs @@ -1,6 +1,11 @@ using ACUtils; + using NUnit.Framework; + using System; +using System.IO; +using System.Security.AccessControl; +using System.Security.Principal; namespace Tests { @@ -212,5 +217,157 @@ public void FtpFtpDelete() Assert.IsTrue(listFtpFiles.Count == 0); } + + + private bool _hasPermission(string filePath, string accountName) + { + var fileInfo = new FileInfo(filePath); + var acl = fileInfo.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount)); + for (int i = 0; i < acl.Count; i++) + { + var currentRule = (FileSystemAccessRule)acl[i]; + Console.WriteLine(currentRule.IdentityReference.Value); + if (currentRule.IdentityReference.Value.Equals(accountName, StringComparison.CurrentCultureIgnoreCase)) return true; + } + + acl = fileInfo.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); + for (int i = 0; i < acl.Count; i++) + { + var currentRule = (FileSystemAccessRule)acl[i]; + Console.WriteLine(currentRule.IdentityReference.Value); + if (currentRule.IdentityReference.Value.Equals(accountName, StringComparison.CurrentCultureIgnoreCase)) return true; + } + + return false; + } + + [Test] + [TestCase(@"ANDREA-C-2021-1\test")] + [TestCase(@"NT AUTHORITY\NETWORK")] + [Parallelizable(ParallelScope.All)] + public void FixAclTest(string identityUPN) + { + var basePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + System.IO.Directory.CreateDirectory(basePath); + var sourceFolderPath = Path.Combine(basePath, "source"); + System.IO.Directory.CreateDirectory(sourceFolderPath); + var destFolderPath = Path.Combine(basePath, "destination"); + System.IO.Directory.CreateDirectory(destFolderPath); + DirectoryInfo destFolder = new DirectoryInfo(destFolderPath); + + + DirectorySecurity security = destFolder.GetAccessControl(); + var rule = new FileSystemAccessRule( + identityUPN, + FileSystemRights.FullControl, + InheritanceFlags.ObjectInherit, + PropagationFlags.InheritOnly, + AccessControlType.Allow + ); + security.AddAccessRule(rule); + destFolder.SetAccessControl(security); + var filename = Path.GetRandomFileName(); + var sourcePath = System.IO.Path.Combine(sourceFolderPath, filename); + var destPath = System.IO.Path.Combine(destFolderPath, filename); + + File.WriteAllText(sourcePath, "hello there!"); + + File.Move(sourcePath, destPath); + Console.WriteLine($"{sourcePath} -> {destPath}"); + + Assert.IsFalse(_hasPermission( destPath, identityUPN)); + + + File.WriteAllText(sourcePath, "hello there!"); + ACUtils.FileUtils.MoveFile(sourcePath, destPath, bOverride: true, fixAcl: true); + + Assert.IsTrue(_hasPermission(destPath, identityUPN)); + } + + } + + + + public class UserSecurity + { + WindowsIdentity _currentUser; + WindowsPrincipal _currentPrincipal; + + public UserSecurity(WindowsIdentity user) + { + _currentUser = user; + _currentPrincipal = new WindowsPrincipal(_currentUser); + } + + public bool HasAccess(DirectoryInfo directory, FileSystemRights right) + { + // Get the collection of authorization rules that apply to the directory. + AuthorizationRuleCollection acl = directory.GetAccessControl() + .GetAccessRules(true, true, typeof(SecurityIdentifier)); + return HasFileOrDirectoryAccess(right, acl); + } + + public bool HasAccess(FileInfo file, FileSystemRights right) + { + // Get the collection of authorization rules that apply to the file. + AuthorizationRuleCollection acl = file.GetAccessControl() + .GetAccessRules(true, true, typeof(SecurityIdentifier)); + return HasFileOrDirectoryAccess(right, acl); + } + + private bool HasFileOrDirectoryAccess(FileSystemRights right, + AuthorizationRuleCollection acl) + { + bool allow = false; + bool inheritedAllow = false; + bool inheritedDeny = false; + + for (int i = 0; i < acl.Count; i++) + { + var currentRule = (FileSystemAccessRule)acl[i]; + // If the current rule applies to the current user. + if (_currentUser.User.Equals(currentRule.IdentityReference) || + _currentPrincipal.IsInRole( + (SecurityIdentifier)currentRule.IdentityReference)) + { + + if (currentRule.AccessControlType.Equals(AccessControlType.Deny)) + { + if ((currentRule.FileSystemRights & right) == right) + { + if (currentRule.IsInherited) + { + inheritedDeny = true; + } + else + { // Non inherited "deny" takes overall precedence. + return false; + } + } + } + else if (currentRule.AccessControlType + .Equals(AccessControlType.Allow)) + { + if ((currentRule.FileSystemRights & right) == right) + { + if (currentRule.IsInherited) + { + inheritedAllow = true; + } + else + { + allow = true; + } + } + } + } + } + + if (allow) + { // Non inherited "allow" takes precedence over inherited rules. + return true; + } + return inheritedAllow && !inheritedDeny; + } } } \ No newline at end of file