С тест# если пользователь имеет доступ на запись в папку

Мне нужно проверить, может ли пользователь писать в папку, прежде чем пытаться это сделать.

я реализовал следующий метод (в C# 2.0), который пытается получить права доступа к папку, используя

18 ответов


Это совершенно правильный способ проверить доступ к папке в C#. Единственное место, где он может упасть, - это если вам нужно вызвать это в узком цикле, где накладные расходы исключения мая быть проблемой.

были и другие как вопросы ранее спрашивали.


Я ценю, что это немного поздно для этого поста, но вы можете найти этот бит кода полезным.

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();

поместите это в консольное приложение и посмотрите, делает ли оно то, что вам нужно.


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;
    }
}

Я пробовал большинство из них, но они дают ложные срабатывания, все по той же причине.. Недостаточно проверить каталог на наличие доступного разрешения, необходимо проверить, является ли вошедший пользователь членом группы, имеющей это разрешение. Для этого вы получаете удостоверение пользователя и проверяете, является ли оно членом группы, содержащей 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;
    }

например, для всех пользователей (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;
    }
}

IMHO единственный 100% надежный способ проверить, можете ли вы написать в каталог, - это фактически написать в него и в конечном итоге поймать исключения.


попробуйте это:

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");
}

ваш код получает DirectorySecurity для данного каталога и обрабатывает исключение (из-за того, что у вас нет доступа к информации о безопасности) правильно. Однако в вашем примере вы фактически не опрашиваете возвращаемый объект, чтобы увидеть, какой доступ разрешен, и я думаю, что вам нужно добавить это.


я использовал ту же функцию для проверки, если файл hasWriteAccess:

    private static bool HasWriteAccessToFile(string filePath)
    {
        try
        {
            // Attempt to get a list of security permissions from the file. 
            // This will raise an exception if the path is read only or do not have access to view the permissions. 
            File.GetAccessControl(filePath);
            return true;
        }
        catch (UnauthorizedAccessException)
        {
            return false;
        }
    }

вот измененная версия ответ CsabaS, который учитывает явные правила запрещения доступа. Функция проходит через все FileSystemAccessRules для каталога и проверяет, является ли текущий пользователь в роли, которая имеет доступ к каталогу. Если такие роли не найдены или пользователь находится в роли с запрещенным доступом, функция возвращает false. Чтобы проверить права чтения, передайте FileSystemRights.Чтение функции; для прав записи передайте FileSystemRights.Писать. Если хочешь ... проверьте права произвольного пользователя, а не текущего, замените 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;
    }

вы можете попробовать следующий блок кода, чтобы проверить, имеет ли каталог доступ для записи. Он проверяет 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 
}

У вас есть потенциальное условие гонки в вашем коде-что произойдет, если пользователь имеет разрешения на запись в папку при проверке, но до того, как пользователь фактически записывает в папку, это разрешение удаляется? Запись выдаст исключение, которое вам нужно будет поймать и обработать. Так что первоначальная проверка бессмысленна. Вы также можете просто написать и обработать любые исключения. Это стандартная модель для вашей ситуации.


http://www.codeproject.com/KB/files/UserFileAccessRights.aspx

очень полезный класс, проверьте улучшенную версию в сообщениях ниже.


просто пытаться получить доступ к файлу, о котором идет речь, не обязательно достаточно. Тест будет выполняться с разрешениями пользователя, запускающего программу , что не обязательно является разрешениями пользователя, с которыми вы хотите протестировать.


вышеуказанные решения хороши, но для меня я нахожу этот код простым и работоспособным. Просто создайте временный файл. Если файл создан, его средний пользователь имеет доступ на запись.

        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;
        }

Я согласен с Эшем, это должно быть хорошо. В качестве альтернативы вы можете использовать декларативный CAS и фактически запретить запуск программы, если у них нет доступа.

Я считаю, что некоторые функции CAS могут отсутствовать в C# 4.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;
        }

надеюсь, что это помогает.


я столкнулся с той же проблемой: как проверить, если я могу читать/писать в определенном каталоге. В итоге я нашел простое решение...на самом деле проверьте. Вот мое простое но эффективное решение.

 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

}

надеюсь, что это помогает !