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 19.09.13

Ответы:

61

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

Были и другие подобные вопросы, задаваемые ранее.

ясень
источник
1
Как ни странно, один из этих других вопросов был открыт на другой вкладке, но я не видел ответа о DirectorySecurity, научите меня читать все ответы, а не только принятые ;-)
Крис Б.
Не упадет ли он и при использовании длинных путей в Windows?
Александру
11
Это не скажет вам, если у вас есть разрешение на запись, оно только скажет вам, можете ли вы искать разрешения для этой папки или нет. Также вы можете писать, но не можете искать разрешения.
RandomEngy
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
Я получаю исключение при вызове, 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
Этот ответ будет перехватывать все исключения, которые могут возникнуть при попытке записи файла, а не только нарушения прав доступа.
Мэтт Эллен
7
@GY, string tempFileName = Path.GetRandomFileName();видимо
Алексей Хороших
3
@ Matt, это точно отвечает на заданный вопрос «каталог доступен для записи», независимо от причины сбоя. Вы скорее отвечаете на « почему я не могу написать в каталог». :)
Алексей Хороших
1
Я получаю ложное срабатывание с этим кодом. File.Create () работает нормально (и оставляет временный файл, если вы изменили последний параметр), даже если исполняющий пользователь не имеет разрешения на запись в эту папку. Действительно очень странно - потратил час, пытаясь понять, почему, но я в тупике.
NickG
4
Из всех альтернатив, которые я попробовал ниже (и ссылки) - это единственный, который работает надежно.
TarmoPikaro
24

Я пробовал большинство из них, но они дают ложные срабатывания, все по той же причине. Недостаточно проверить каталог на наличие доступных разрешений, вы должны проверить, что вошедший в систему пользователь является членом группы, которая имеет это разрешение. Для этого вы получаете удостоверение пользователя и проверяете, является ли он членом группы, содержащей 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;
    }
JGU
источник
Спасибо Джон, у меня также есть ложные срабатывания, пока я не использую твой код, чтобы снова проверить группу пользователей по правилу IdentifyReference!
Paul L
1
Мне нужно было добавить дополнительную проверку для идентичности. Владелец == rule.IdentityReference, поскольку у меня был пользователь, который предоставил доступ, но не в каких-либо группах, как выделенная локальная учетная запись для служб
grinder22
2
AccessControlType deny имеет преимущество перед разрешением, поэтому следует также проверять, чтобы были полностью проработаны правила, запрещающие право доступа, и при проверке запрещенных типов это должно быть (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)), поскольку у меня был пользователь, который имел доступ, но не был т в любой из групп.
Ehambright
13

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

Дарин димитров
источник
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;
    }
}
UGEEN
источник
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");
}
CsabaS
источник
Если я не ошибаюсь, это близко, но не совсем там - оно упускает из виду тот факт, что fsAccessRule.AccessControlTypeможет быть AccessControlType.Deny.
Джонатан Гилберт
У меня это работало на моей машине с Win7, но не работало на Win10 (как для тестера, так и для моей собственной тестовой машины). Модификация SSD (см. ниже), кажется, исправить это.
winwaed
6

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

Винай Саджип
источник
+1 - Я только что столкнулся с этой проблемой, когда исключение не было выдано при вызове GetAccessControl, но я получаю несанкционированное исключение при попытке записи в тот же каталог.
Майо
6

Вот модифицированная версия ответа CsabaS , которая учитывает явные правила запрета доступа. Функция просматривает все FileSystemAccessRules для каталога и проверяет, находится ли текущий пользователь в роли, которая имеет доступ к каталогу. Если такие роли не найдены или пользователь находится в роли с запрещенным доступом, функция возвращает false. Чтобы проверить права на чтение, передайте FileSystemRights.Read функции; для прав записи передайте 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;
    }
ССРД
источник
Код Csaba для меня не удался в Windows 10 (но хорошо на моей машине с 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в этом случае это возвращение будет ложным , даже если пользователь делает иметь разрешение на запись.
Крис Б.
Самый удобный ответ для кодирования :) Я также использую только этот, однако, когда есть большие параллельные запросы, то слишком большое чтение / запись может снизить производительность, поэтому в этих случаях вы можете использовать методологию контроля доступа, как указано в других ответах.
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 
}
RockWorld
источник
2

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


источник
1

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

Mort
источник
0

Я согласен с Эшем, это должно быть хорошо. В качестве альтернативы вы можете использовать декларативный 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

}

Надеюсь, поможет !

l.raimondi
источник
0

Большинство ответов здесь не проверяет доступ на запись. Он просто проверяет, может ли пользователь / группа «Чтение разрешений» (Чтение списка ACE файла / каталога).

Также итерация ACE и проверка соответствия идентификатора безопасности не работает, поскольку пользователь может быть членом группы, из которой он может получить / потерять привилегию. Хуже, чем вложенные группы.

Я знаю, что это старая тема, но есть способ лучше для любого, кто смотрит сейчас.

При условии, что у пользователя есть права на чтение, можно использовать API-интерфейс Authz для проверки эффективного доступа.

https://docs.microsoft.com/en-us/windows/win32/secauthz/using-authz-api

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

Kamaal
источник