简体   繁体   English

检查 .NET 中的目录和文件写入权限

[英]Checking for directory and file write permissions in .NET

In my .NET 2.0 application, I need to check if sufficient permissions exist to create and write to files to a directory.在我的 .NET 2.0 应用程序中,我需要检查是否存在足够的权限来创建文件并将其写入目录。 To this end, I have the following function that attempts to create a file and write a single byte to it, deleting itself afterwards to test that permissions do exist.为此,我有以下函数尝试创建一个文件并向其中写入一个字节,然后删除自身以测试权限确实存在。

I figured the best way to check was to actually try and do it, catching any exceptions that occur.我认为最好的检查方法是实际尝试并执行它,捕获发生的任何异常。 I'm not particularly happy about the general Exception catch though, so is there a better or perhaps a more accepted way of doing this?不过,我对一般的异常捕获并不是特别满意,那么是否有更好或更可接受的方法来做到这一点?

private const string TEMP_FILE = "\\tempFile.tmp";

/// <summary>
/// Checks the ability to create and write to a file in the supplied directory.
/// </summary>
/// <param name="directory">String representing the directory path to check.</param>
/// <returns>True if successful; otherwise false.</returns>
private static bool CheckDirectoryAccess(string directory)
{
    bool success = false;
    string fullPath = directory + TEMP_FILE;

    if (Directory.Exists(directory))
    {
        try
        {
            using (FileStream fs = new FileStream(fullPath, FileMode.CreateNew, 
                                                            FileAccess.Write))
            {
                fs.WriteByte(0xff);
            }

            if (File.Exists(fullPath))
            {
                File.Delete(fullPath);
                success = true;
            }
        }
        catch (Exception)
        {
            success = false;
        }
    }

Directory.GetAccessControl(path) does what you are asking for. Directory.GetAccessControl(path)您的要求。

public static bool HasWritePermissionOnDir(string path)
{
    var writeAllow = false;
    var writeDeny = false;
    var accessControlList = Directory.GetAccessControl(path);
    if (accessControlList == null)
        return false;
    var accessRules = accessControlList.GetAccessRules(true, true, 
                                typeof(System.Security.Principal.SecurityIdentifier));
    if (accessRules ==null)
        return false;

    foreach (FileSystemAccessRule rule in accessRules)
    {
        if ((FileSystemRights.Write & rule.FileSystemRights) != FileSystemRights.Write) 
            continue;

        if (rule.AccessControlType == AccessControlType.Allow)
            writeAllow = true;
        else if (rule.AccessControlType == AccessControlType.Deny)
            writeDeny = true;
    }

    return writeAllow && !writeDeny;
}

(FileSystemRights.Write & rights) == FileSystemRights.Write is using something called "Flags" btw which if you don't know what it is you should really read up on :) (FileSystemRights.Write & rights) == FileSystemRights.Write正在使用一种叫做“Flags”的东西,顺便说一句,如果你不知道它是什么,你应该仔细阅读:)

Deny takes precedence over Allow . Deny优先于Allow Local rules take precedence over inherited rules.本地规则优先于继承规则。 I have seen many solutions (including some answers shown here), but none of them takes into account whether rules are inherited or not.我见过很多解决方案(包括这里显示的一些答案),但没有一个考虑到规则是否被继承 Therefore I suggest the following approach that considers rule inheritance (neatly wrapped into a class):因此,我建议采用以下考虑规则继承的方法(整齐地包装到一个类中):

public class CurrentUserSecurity
{
    WindowsIdentity _currentUser;
    WindowsPrincipal _currentPrincipal;

    public CurrentUserSecurity()
    {
        _currentUser = WindowsIdentity.GetCurrent();
        _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;
    }
}

However, I made the experience that this does not always work on remote computers as you will not always have the right to query the file access rights there.但是,我的经验是这并不总是适用于远程计算机,因为您并不总是有权在那里查询文件访问权限。 The solution in that case is to try;这种情况下的解决方案是尝试; possibly even by just trying to create a temporary file, if you need to know the access right before working with the "real" files.如果您需要在使用“真实”文件之前知道访问权限,甚至可能只是尝试创建一个临时文件。

The answers by Richard and Jason are sort of in the right direction.理查德杰森的答案在某种程度上是正确的。 However what you should be doing is computing the effective permissions for the user identity running your code.但是,您应该做的是计算运行代码的用户身份的有效权限 None of the examples above correctly account for group membership for example.例如,上面的示例都没有正确说明组成员身份。

I'm pretty sure Keith Brown had some code to do this in his wiki version (offline at this time) of The .NET Developers Guide to Windows Security .我很确定Keith Brown在他的The .NET Developers Guide to Windows Security 的wiki 版本(此时离线)中有一些代码可以做到这一点。 This is also discussed in reasonable detail in his Programming Windows Security book.这也在他的Windows 安全编程一书中进行了合理的详细讨论。

Computing effective permissions is not for the faint hearted and your code to attempt creating a file and catching the security exception thrown is probably the path of least resistance.计算有效权限不适合胆小的人,您的代码尝试创建文件并捕获抛出的安全异常可能是阻力最小的路径。

The accepted answer by Kev to this question doesn't actually give any code, it just points to other resources that I don't have access to. Kev 对这个问题的公认答案实际上并没有给出任何代码,它只是指向我无权访问的其他资源。 So here's my best attempt at the function.所以这是我对该功能的最佳尝试。 It actually checks that the permission it's looking at is a "Write" permission and that the current user belongs to the appropriate group.它实际上会检查它正在查看的权限是否为“写入”权限,以及当前用户是否属于适当的组。

It might not be complete with regard to network paths or whatever, but it's good enough for my purpose, checking local configuration files under "Program Files" for writability:关于网络路径或其他内容,它可能不完整,但对于我的目的来说已经足够了,检查“程序文件”下的本地配置文件的可写性:

using System.Security.Principal;
using System.Security.AccessControl;

private static bool HasWritePermission(string FilePath)
{
    try
    {
        FileSystemSecurity security;
        if (File.Exists(FilePath))
        {
            security = File.GetAccessControl(FilePath);
        }
        else
        {
            security = Directory.GetAccessControl(Path.GetDirectoryName(FilePath));
        }
        var rules = security.GetAccessRules(true, true, typeof(NTAccount));

        var currentuser = new WindowsPrincipal(WindowsIdentity.GetCurrent());
        bool result = false;
        foreach (FileSystemAccessRule rule in rules)
        {
            if (0 == (rule.FileSystemRights &
                (FileSystemRights.WriteData | FileSystemRights.Write)))
            {
                continue;
            }

            if (rule.IdentityReference.Value.StartsWith("S-1-"))
            {
                var sid = new SecurityIdentifier(rule.IdentityReference.Value);
                if (!currentuser.IsInRole(sid))
                {
                    continue;
                }
            }
            else
            {
                if (!currentuser.IsInRole(rule.IdentityReference.Value))
                {
                    continue;
                }
            }

            if (rule.AccessControlType == AccessControlType.Deny)
                return false;
            if (rule.AccessControlType == AccessControlType.Allow)
                result = true;
        }
        return result;
    }
    catch
    {
        return false;
    }
}

IMO, you need to work with such directories as usual, but instead of checking permissions before use, provide the correct way to handle UnauthorizedAccessException and react accordingly. IMO,您需要像往常一样使用此类目录,但不要在使用前检查权限,而是提供正确的方法来处理 UnauthorizedAccessException 并做出相应的反应。 This method is easier and much less error prone.这种方法更容易,也更不容易出错。

Try working with this C# snippet I just crafted:尝试使用我刚刚制作的这个 C# 片段:

using System;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string directory = @"C:\downloads";

            DirectoryInfo di = new DirectoryInfo(directory);

            DirectorySecurity ds = di.GetAccessControl();

            foreach (AccessRule rule in ds.GetAccessRules(true, true, typeof(NTAccount)))
            {
                Console.WriteLine("Identity = {0}; Access = {1}", 
                              rule.IdentityReference.Value, rule.AccessControlType);
            }
        }
    }
}

And here's a reference you could also look at. 这是您也可以查看的参考。 My code might give you an idea as to how you could check for permissions before attempting to write to a directory.我的代码可能会让您了解如何在尝试写入目录之前检查权限。

according to this link: http://www.authorcode.com/how-to-check-file-permission-to-write-in-c/根据此链接: http : //www.authorcode.com/how-to-check-file-permission-to-write-in-c/

it's easier to use existing class SecurityManager使用现有的 SecurityManager 类更容易

string FileLocation = @"C:\test.txt";
FileIOPermission writePermission = new FileIOPermission(FileIOPermissionAccess.Write, FileLocation);
if (SecurityManager.IsGranted(writePermission))
{
  // you have permission
}
else
{
 // permission is required!
}

but it seems it's been obsoleted, it is suggested to use PermissionSet instead.不过好像已经过时了,建议改用PermissionSet。

[Obsolete("IsGranted is obsolete and will be removed in a future release of the .NET Framework.  Please use the PermissionSet property of either AppDomain or Assembly instead.")]

Since the static method 'GetAccessControl' seems to be missing from the present version of .Net core/Standard I had to modify @Bryce Wagner's answer (I went ahead and used more modern syntax): 由于当前版本的 .Net core/Standard 似乎缺少静态方法“GetAccessControl”,因此我不得不修改 @Bryce Wagner 的答案(我继续使用更现代的语法):

public static class PermissionHelper
{
  public static bool? CurrentUserHasWritePermission(string filePath)

     => new WindowsPrincipal(WindowsIdentity.GetCurrent())
        .SelectWritePermissions(filePath)
        .FirstOrDefault();


  private static IEnumerable<bool?> SelectWritePermissions(this WindowsPrincipal user, string filePath)
     => from rule in filePath
                    .GetFileSystemSecurity()
                    .GetAccessRules(true, true, typeof(NTAccount))
                    .Cast<FileSystemAccessRule>()
        let right = user.HasRightSafe(rule)
        where right.HasValue
        // Deny takes precedence over allow
        orderby right.Value == false descending
        select right;


  private static bool? HasRightSafe(this WindowsPrincipal user, FileSystemAccessRule rule)
  {
     try
     {
        return user.HasRight(rule);
     }
     catch
     {
        return null;
     }
  }

  private static bool? HasRight(this WindowsPrincipal user,FileSystemAccessRule rule )
     => rule switch
     {
        { FileSystemRights: FileSystemRights fileSystemRights } when (fileSystemRights &
                                                                      (FileSystemRights.WriteData | FileSystemRights.Write)) == 0 => null,
        { IdentityReference: { Value: string value } } when value.StartsWith("S-1-") &&
                                                            !user.IsInRole(new SecurityIdentifier(rule.IdentityReference.Value)) => null,
        { IdentityReference: { Value: string value } } when value.StartsWith("S-1-") == false &&
                                                            !user.IsInRole(rule.IdentityReference.Value) => null,
        { AccessControlType: AccessControlType.Deny } => false,
        { AccessControlType: AccessControlType.Allow } => true,
        _ => null
     };


  private static FileSystemSecurity GetFileSystemSecurity(this string filePath)
    => new FileInfo(filePath) switch
    {
       { Exists: true } fileInfo => fileInfo.GetAccessControl(),
       { Exists: false } fileInfo => (FileSystemSecurity)fileInfo.Directory.GetAccessControl(),
       _ => throw new Exception($"Check the file path, {filePath}: something's wrong with it.")
    };
}
private static void GrantAccess(string file)
        {
            bool exists = System.IO.Directory.Exists(file);
            if (!exists)
            {
                DirectoryInfo di = System.IO.Directory.CreateDirectory(file);
                Console.WriteLine("The Folder is created Sucessfully");
            }
            else
            {
                Console.WriteLine("The Folder already exists");
            }
            DirectoryInfo dInfo = new DirectoryInfo(file);
            DirectorySecurity dSecurity = dInfo.GetAccessControl();
            dSecurity.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), FileSystemRights.FullControl, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.NoPropagateInherit, AccessControlType.Allow));
            dInfo.SetAccessControl(dSecurity);

        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM