C#测试用户是否具有对文件夹的写权限


187

我需要在实际尝试之前测试用户是否可以写入文件夹。

我已经实现了以下方法(在C#2.0中),该方法尝试使用Directory.GetAccessControl()方法检索文件夹的安全权限。

private bool hasWriteAccessToFolder(string folderPath)
{
    try
    {
        // Attempt to get a list of security permissions from the folder. 
        // This will raise an exception if the path is read only or do not have access to view the permissions. 
        System.Security.AccessControl.DirectorySecurity ds = Directory.GetAccessControl(folderPath);
        return true;
    }
    catch (UnauthorizedAccessException)
    {
        return false;
    }
}

当我谷歌搜索如何测试写访问权限时,没有出现这样的情况,而实际上在Windows中测试权限似乎非常复杂。我担心我过分简化了该方法,尽管该方法确实可行,但该方法并不可靠。

我的方法是否可以测试当前用户是否具有写访问权限?


13
不能查看权限与被禁止写入权限真的一样吗?
deed02392 2013年

Answers:


60

这是在C#中检查文件夹访问的一种完全有效的方法。它可能跌倒的唯一地方是,如果您需要在一个紧密循环中调用它,在该循环中,异常的开销可能是一个问题。

之前还有其他类似的 问题


1
有趣的是,我在另一个选项卡中打开了其他问题之一,但没有看到有关DirectorySecurity的答案,教我阅读所有答案,而不仅仅是接受的答案;-)
Chris B

当您在Windows中使用长路径时,它也会掉下来吗?
亚历山德鲁

11
这不会告诉您是否具有写权限,而只会告诉您是否可以在该文件夹中查找权限。另外,您可能可以编写但不能查找权限。
RandomEngy 2015年

65

我感谢这篇文章来得太晚了,但是您可能会发现这段代码有用。

string path = @"c:\temp";
string NtAccountName = @"MyDomain\MyUserOrGroup";

DirectoryInfo di = new DirectoryInfo(path);
DirectorySecurity acl = di.GetAccessControl(AccessControlSections.All);
AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));

//Go through the rules returned from the DirectorySecurity
foreach (AuthorizationRule rule in rules)
{
    //If we find one that matches the identity we are looking for
    if (rule.IdentityReference.Value.Equals(NtAccountName,StringComparison.CurrentCultureIgnoreCase))
    {
        var filesystemAccessRule = (FileSystemAccessRule)rule;

        //Cast to a FileSystemAccessRule to check for access rights
        if ((filesystemAccessRule.FileSystemRights & FileSystemRights.WriteData)>0 && filesystemAccessRule.AccessControlType != AccessControlType.Deny)
        {
            Console.WriteLine(string.Format("{0} has write access to {1}", NtAccountName, path));
        }
        else
        {
            Console.WriteLine(string.Format("{0} does not have write access to {1}", NtAccountName, path));
        }
    }
}

Console.ReadLine();

将其放入控制台应用程序,看看它是否满足您的需求。


就在目标上!对我有很大帮助!
smwikipedia 2011年

我在打来电话时遇到了异常,GetAccessControl但是我的软件实际上能够写入我正在查看的目录。
乔恩·凯奇

@JonCage-您得到什么例外?具有讽刺意味的是,首先想到的是安全问题。您的应用程序所运行的帐户是否具有获取ACL信息的权限?
邓肯·豪

1
您需要添加对FileSystemAccessRule类型的检查。如果这是拒绝规则,则您会错误地将其报告为可写。
tdemay

2
我正在尝试使用它。发现了另一个问题。如果仅将权限分配给组而不是特定用户,这将错误地报告他们没有写权限。例如,授予“授权用户”的写访问权限
tdemay

63
public bool IsDirectoryWritable(string dirPath, bool throwIfFails = false)
{
    try
    {
        using (FileStream fs = File.Create(
            Path.Combine(
                dirPath, 
                Path.GetRandomFileName()
            ), 
            1,
            FileOptions.DeleteOnClose)
        )
        { }
        return true;
    }
    catch
    {
        if (throwIfFails)
            throw;
        else
            return false;
    }
}

7
该答案将捕获尝试写入文件时可能发生的所有异常,而不仅仅是权限冲突。
Matt Ellen

7
@GY,string tempFileName = Path.GetRandomFileName();显然
Alexey Khoroshikh

3
@Matt,这将完全回答询问的问题“目录是否可写”,而不管失败的原因如何。您宁愿回答“ 为什么我不能写目录”。:)
Alexey Khoroshikh 2014年

1
我收到此代码的误报。即使执行用户没有写该文件夹的权限,File.Create()也可以正常运行(如果更改了最后一个选项,将留下一个临时文件)。真的很奇怪-花了一个小时试图弄清楚为什么,但是我很沮丧。
NickG 2015年

4
在我下面尝试过的所有替代方法(和参考链接)中,这是唯一可靠地起作用的方法。
TarmoPikaro 2015年

23

我尝试了大多数方法,但是出于相同的原因,它们都给出了误报。.仅测试目录中的可用权限是不够的,您必须检查登录用户是否是具有该权限的组的成员。允许。为此,您需要获取用户身份,并检查它是否是包含FileSystemAccessRule IdentityReference的组的成员。我已经对此进行了测试,完美无瑕。

    /// <summary>
    /// Test a directory for create file access permissions
    /// </summary>
    /// <param name="DirectoryPath">Full path to directory </param>
    /// <param name="AccessRight">File System right tested</param>
    /// <returns>State [bool]</returns>
    public static bool DirectoryHasPermission(string DirectoryPath, FileSystemRights AccessRight)
    {
        if (string.IsNullOrEmpty(DirectoryPath)) return false;

        try
        {
            AuthorizationRuleCollection rules = Directory.GetAccessControl(DirectoryPath).GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));
            WindowsIdentity identity = WindowsIdentity.GetCurrent();

            foreach (FileSystemAccessRule rule in rules)
            {
                if (identity.Groups.Contains(rule.IdentityReference))
                {
                    if ((AccessRight & rule.FileSystemRights) == AccessRight)
                    {
                        if (rule.AccessControlType == AccessControlType.Allow)
                            return true;
                    }
                }
            }
        }
        catch { }
        return false;
    }

谢谢约翰,在我使用您的代码再次检查用户组规则IdentifyReference之前,我也得到了误报!
Paul L

1
我必须添加一个对identity.Owner == rule.IdentityReference的附加检查,因为我有一个授予访问权限但没有任何组的用户,例如服务的专用本地帐户
grinder22 '18

1
AccessControlType deny优先于allow,因此要完全彻底地确定拒绝访问权限的规则,并且在检查拒绝类型时应该是(AccessRight & rule.FileSystemRights) > 0因为被拒绝的任何子访问类型都AccessRight意味着您没有完整权限访问AccessRight
TJ洛克菲勒

就像上面提到的grinder22一样,我需要进行更改。if(identity.Groups.Contains(rule.IdentityReference))到if(identity.Groups.Contains(rule.IdentityReference)|| identity.Owner.Equals(rule.IdentityReference)),因为我有一个有权访问但不是的用户t在任何组中。
ehambright19年

13

例如,对于所有用户(Builtin \ Users),此方法都可以正常工作-享受。

public static bool HasFolderWritePermission(string destDir)
{
   if(string.IsNullOrEmpty(destDir) || !Directory.Exists(destDir)) return false;
   try
   {
      DirectorySecurity security = Directory.GetAccessControl(destDir);
      SecurityIdentifier users = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
      foreach(AuthorizationRule rule in security.GetAccessRules(true, true, typeof(SecurityIdentifier)))
      {
          if(rule.IdentityReference == users)
          {
             FileSystemAccessRule rights = ((FileSystemAccessRule)rule);
             if(rights.AccessControlType == AccessControlType.Allow)
             {
                    if(rights.FileSystemRights == (rights.FileSystemRights | FileSystemRights.Modify)) return true;
             }
          }
       }
       return false;
    }
    catch
    {
        return false;
    }
}


8

试试这个:

try
{
    DirectoryInfo di = new DirectoryInfo(path);
    DirectorySecurity acl = di.GetAccessControl();
    AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));

    WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
    WindowsPrincipal principal = new WindowsPrincipal(currentUser);
    foreach (AuthorizationRule rule in rules)
    {
        FileSystemAccessRule fsAccessRule = rule as FileSystemAccessRule;
        if (fsAccessRule == null)
            continue;

        if ((fsAccessRule.FileSystemRights & FileSystemRights.WriteData) > 0)
        {
            NTAccount ntAccount = rule.IdentityReference as NTAccount;
            if (ntAccount == null)
            {
                continue;
            }

            if (principal.IsInRole(ntAccount.Value))
            {
                Console.WriteLine("Current user is in role of {0}, has write access", ntAccount.Value);
                continue;
            }
            Console.WriteLine("Current user is not in role of {0}, does not have write access", ntAccount.Value);                        
        }
    }
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("does not have write access");
}

如果我没记错的话,这很接近,但还不是很远–它忽略了fsAccessRule.AccessControlType可能的事实AccessControlType.Deny
乔纳森·吉尔伯特

这在我的Win7开发机上为我工作,但在Win10上却失败了(这对于测试仪和我自己的测试机而言)。ssds的修改(请参见下文)似乎已修复。
Winwaed

6

您的代码获取DirectorySecurity给定目录的,并正确处理异常(由于您无法访问安全信息)。但是,在您的示例中,您实际上并没有询问返回的对象以查看允许的访问权限-我认为您需要添加此对象。


+1-我只是遇到了这样的问题:调用GetAccessControl时没有引发异常,但是尝试写入同一目录时却得到了未经授权的异常。
梅奥

6

这是CsabaS的answer的修改版本,它说明了明确的拒绝访问规则。该功能遍历目录的所有FileSystemAccessRules,并检查当前用户是否具有可以访问目录的角色。如果找不到这样的角色,或者用户处于拒绝访问的角色,则该函数返回false。要检查读取权限,请将FileSystemRights.Read传递给该函数;否则,返回false。对于写权限,请传递FileSystemRights.Write。如果要检查任意用户的权限而不是当前用户的权限,请用currentUser WindowsIdentity替换所需的WindowsIdentity。我也建议不要依赖此类功能来确定用户是否可以安全地使用目录。这个答案很好地解释了原因。

    public static bool UserHasDirectoryAccessRights(string path, FileSystemRights accessRights)
    {
        var isInRoleWithAccess = false;

        try
        {
            var di = new DirectoryInfo(path);
            var acl = di.GetAccessControl();
            var rules = acl.GetAccessRules(true, true, typeof(NTAccount));

            var currentUser = WindowsIdentity.GetCurrent();
            var principal = new WindowsPrincipal(currentUser);
            foreach (AuthorizationRule rule in rules)
            {
                var fsAccessRule = rule as FileSystemAccessRule;
                if (fsAccessRule == null)
                    continue;

                if ((fsAccessRule.FileSystemRights & accessRights) > 0)
                {
                    var ntAccount = rule.IdentityReference as NTAccount;
                    if (ntAccount == null)
                        continue;

                    if (principal.IsInRole(ntAccount.Value))
                    {
                        if (fsAccessRule.AccessControlType == AccessControlType.Deny)
                            return false;
                        isInRoleWithAccess = true;
                    }
                }
            }
        }
        catch (UnauthorizedAccessException)
        {
            return false;
        }
        return isInRoleWithAccess;
    }

在Windows 10上,Csaba的代码对我来说失败了(但在我的Win7开发机上很好)。以上似乎解决了该问题。
Winwaed

4

上面的解决方案很好,但是对我来说,我觉得这段代码简单可行。只需创建一个临时文件。如果文件已创建,则其普通用户具有写访问权限。

        public static bool HasWritePermission(string tempfilepath)
        {
            try
            {
                System.IO.File.Create(tempfilepath + "temp.txt").Close();
                System.IO.File.Delete(tempfilepath + "temp.txt");
            }
            catch (System.UnauthorizedAccessException ex)
            {

                return false;
            }

            return true;
        }

3
真好!但是,有一件事是该用户可能具有Create权限,但是Delete在这种情况下,即使该用户确实具有写权限,也会返回false 。
克里斯·B

编码的最方便答案:)我也仅使用此答案,但是,当有大量并发请求时,那么太多的读/写操作可能会降低性能,因此在这种情况下,您可以使用其他答案中给出的访问控制方法。
vibs2006

1
使用Path.Combine代替,例如Path.Combine(tempfilepath, "temp.txt")
ΩmegaMan

3

您可以尝试使用以下代码块来检查目录是否具有写访问权限。它检查FileSystemAccessRule。

string directoryPath = "C:\\XYZ"; //folderBrowserDialog.SelectedPath;
bool isWriteAccess = false;
try
{
    AuthorizationRuleCollection collection =
        Directory.GetAccessControl(directoryPath)
            .GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
    foreach (FileSystemAccessRule rule in collection)
    {
        if (rule.AccessControlType == AccessControlType.Allow)
        {
            isWriteAccess = true;
            break;
        }
    }
}
catch (UnauthorizedAccessException ex)
{
    isWriteAccess = false;
}
catch (Exception ex)
{
    isWriteAccess = false;
}
if (!isWriteAccess)
{
    //handle notifications 
}

2

您的代码中存在潜在的竞争条件-如果在检查时用户具有写该文件夹的权限,但是在用户实际写该文件夹之前,此权限被撤消,会发生什么情况呢?写操作将引发您需要捕获并处理的异常。因此,初始检查毫无意义。您也可以只写并处理任何异常。这是适合您情况的标准模式。



1

仅仅尝试访问有问题的文件并不一定足够。该测试将在运行该程序的用户权限下运行-不一定是您要测试的用户权限。


0

我同意Ash的观点,这应该没问题。或者,您可以使用声明性CAS,并且实际上阻止程序在没有访问权的情况下首先运行。

我相信,根据我所听到的,某些CAS功能可能不会出现在C#4.0中,不确定是否可能是一个问题。


0

按照接受的答案中的建议,我无法让GetAccessControl()在Windows 7上引发异常。

我最终使用了sdds答案的变体:

        try
        {
            bool writeable = false;
            WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
            DirectorySecurity security = Directory.GetAccessControl(pstrPath);
            AuthorizationRuleCollection authRules = security.GetAccessRules(true, true, typeof(SecurityIdentifier));

            foreach (FileSystemAccessRule accessRule in authRules)
            {

                if (principal.IsInRole(accessRule.IdentityReference as SecurityIdentifier))
                {
                    if ((FileSystemRights.WriteData & accessRule.FileSystemRights) == FileSystemRights.WriteData)
                    {
                        if (accessRule.AccessControlType == AccessControlType.Allow)
                        {
                            writeable = true;
                        }
                        else if (accessRule.AccessControlType == AccessControlType.Deny)
                        {
                            //Deny usually overrides any Allow
                            return false;
                        }

                    } 
                }
            }
            return writeable;
        }
        catch (UnauthorizedAccessException)
        {
            return false;
        }

希望这可以帮助。


0

我遇到了同样的问题:如何验证我是否可以在特定目录中进行读取/写入。我最终得到了一个简单的解决方案,以...进行实际测试。这是我简单但有效的解决方案。

 class Program
{

    /// <summary>
    /// Tests if can read files and if any are present
    /// </summary>
    /// <param name="dirPath"></param>
    /// <returns></returns>
    private genericResponse check_canRead(string dirPath)
    {
        try
        {
            IEnumerable<string> files = Directory.EnumerateFiles(dirPath);
            if (files.Count().Equals(0))
                return new genericResponse() { status = true, idMsg = genericResponseType.NothingToRead };

            return new genericResponse() { status = true, idMsg = genericResponseType.OK };
        }
        catch (DirectoryNotFoundException ex)
        {

            return new genericResponse() { status = false, idMsg = genericResponseType.ItemNotFound };

        }
        catch (UnauthorizedAccessException ex)
        {

            return new genericResponse() { status = false, idMsg = genericResponseType.CannotRead };

        }

    }

    /// <summary>
    /// Tests if can wirte both files or Directory
    /// </summary>
    /// <param name="dirPath"></param>
    /// <returns></returns>
    private genericResponse check_canWrite(string dirPath)
    {

        try
        {
            string testDir = "__TESTDIR__";
            Directory.CreateDirectory(string.Join("/", dirPath, testDir));

            Directory.Delete(string.Join("/", dirPath, testDir));


            string testFile = "__TESTFILE__.txt";
            try
            {
                TextWriter tw = new StreamWriter(string.Join("/", dirPath, testFile), false);
                tw.WriteLine(testFile);
                tw.Close();
                File.Delete(string.Join("/", dirPath, testFile));

                return new genericResponse() { status = true, idMsg = genericResponseType.OK };
            }
            catch (UnauthorizedAccessException ex)
            {

                return new genericResponse() { status = false, idMsg = genericResponseType.CannotWriteFile };

            }


        }
        catch (UnauthorizedAccessException ex)
        {

            return new genericResponse() { status = false, idMsg = genericResponseType.CannotWriteDir };

        }
    }


}

public class genericResponse
{

    public bool status { get; set; }
    public genericResponseType idMsg { get; set; }
    public string msg { get; set; }

}

public enum genericResponseType
{

    NothingToRead = 1,
    OK = 0,
    CannotRead = -1,
    CannotWriteDir = -2,
    CannotWriteFile = -3,
    ItemNotFound = -4

}

希望能帮助到你 !


0

这里的大多数答案都不检查写访问权限。它只是检查用户/组是否可以“读取权限”(读取文件/目录的ACE列表)。

同样,通过ACE进行迭代并检查其是否与安全标识符匹配也无法正常进行,因为该用户可能是他可能从中获得/失去特权的组的成员。最糟糕的是嵌套组。

我知道这是一个旧线程,但是对于任何人现在都有更好的方法。

如果用户具有“读取权限”特权,则可以使用Authz API来检查“有效访问”。

https://docs.microsoft.com/zh-cn/windows/win32/secauthz/using-authz-api

https://docs.microsoft.com/zh-cn/windows/win32/secauthz/checking-access-with-authz-api

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.