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

And*_*ndy 75 .net c# directory file-permissions winforms

在我的.NET 2.0应用程序中,我需要检查是否有足够的权限来创建和写入目录的文件.为此,我有以下函数尝试创建一个文件并向其写入一个字节,然后删除自己以测试权限是否存在.

我认为检查的最佳方法是实际尝试并执行此操作,捕获发生的任何异常.虽然我对一般的异常捕获并不是特别高兴,所以有更好的或者更可接受的方式吗?

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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

ric*_*den 46

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;
}
Run Code Online (Sandbox Code Playgroud)

(FileSystemRights.Write & rights) == FileSystemRights.Write 正在使用一些名为"Flags"btw的东西,如果你不知道它是什么,你应该真正阅读:)

  • 当然,如果您无法在目录上获得ACL,那么这将抛出异常. (6认同)
  • 尝试在具有非管理应用程序的Windows 7上的系统磁盘上运行它,它将返回true,但是当您尝试写入c:\时,您将收到一条说明您无权访问的异常! (6认同)
  • 它检查什么?该目录具有写权限,但对于哪个用户?:) (3认同)
  • 如果您只想查看当前用户是否具有写访问权限,则可以使用它. (2认同)

Oli*_*bes 32

Deny优先于Allow.本地规则优先于继承的规则.我已经看到了很多解决方案(包括这里显示的一些答案),但它们都没有考虑规则是否是继承的.因此,我建议采用以下方法来考虑规则继承(整齐地包装到类中):

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我的经验是,这并不总是适用于远程计算机,因为您无法始终查询那里的文件访问权限.在这种情况下的解决方案是尝试; 甚至可能只是尝试创建一个临时文件,如果您需要在使用"真实"文件之前知道访问权限.

  • 我认为这个答案是实现它的最佳方式,其他答案也使用相同的方式来获得结果,但由于只有这个答案计算了继承的规则和本地规则,我认为这是最准确的.感谢和祝贺. (2认同)

Kev*_*Kev 21

理查德杰森的答案正朝着正确的方向发展.但是,您应该做的是计算运行代码的用户标识的有效权限.例如,以上示例中没有一个正确地考虑了组成员身份.

我很确定Keith Brown在他的Windows安全Windows开发人员指南的维基版本(此时是离线)中有一些代码可以执行此操作.在他的Programming Windows Security书籍中也对此进行了详细讨论.

计算有效权限不适合胆小的人,而您的代码尝试创建文件并捕获抛出的安全异常可能是阻力最小的路径.

  • @Triynko - 我建议你阅读我引用的文章:https://groups.google.com/group/microsoft.public.dotnet.languages.csharp/msg/208347aa99a2de3b?hl = en - 计算**有效**权限是并不像听起来那么简单.做我的客人并提供一个证明我错的答案. (3认同)
  • 它也是唯一可靠的方法,否则有人可以在检查和实际尝试保存之间更改权限(不太可能,但可能). (2认同)
  • 为什么一切都必须如此复杂! (2认同)

Bry*_*ner 18

Kev对这个问题的接受答案实际上并没有给出任何代码,只是指向我无法访问的其他资源.所以这是我对该功能的最佳尝试.它实际上检查它正在查看的权限是否为"写入"权限,并且当前用户属于相应的组.

关于网络路径或其他什么可能不完整,但它足以满足我的目的,检查"程序文件"下的本地配置文件是否可写:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这个解决方案对我有用,但网络文件夹有一个问题.该文件夹具有允许写入"BUILTIN\Administrators"的访问规则.由于我是当地电台的管理员,因此该片段错误地返回了"真实". (2认同)

arb*_*ter 5

IMO,您需要像往常一样使用这些目录,但不是在使用前检查权限,而是提供正确的方法来处理UnauthorizedAccessException并做出相应的反应.这种方法更容易,更不容易出错.

  • 您可能想说“这种方法更简单,而且*少*容易出错。” (2认同)