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

Відповіді:


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

Запустіть це в додаток Console і подивіться, чи він виконує те, що вам потрібно.


Прямо в ціль! Мені дуже допомагає!
smwikipedia

Я отримую виняток під час виклику, GetAccessControlале моє програмне забезпечення насправді здатне писати в каталог, в який я переглядаю ..?
Джон Кейдж

@JonCage - який виняток ви отримуєте? Перше, що спадає на думку, це, як це не дивно, проблема безпеки. Чи має обліковий запис, у якому працює ваш додаток, дозвіл на отримання інформації про ACL?
Дункан Хоу

1
Потрібно додати чек для типу FileSystemAccessRule. Якщо це правило заборонити, ви невірно повідомте про це як для запису.
tdemay

2
Я намагаюся цим скористатися. Знайшла ще одну проблему. Якщо права призначені лише для груп, а не для конкретних користувачів, це буде неправильно повідомляти, що вони не мають доступу до запису. Наприклад, доступ до запису, наданий "Аутентифікованим користувачам"
вдруге

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
З усіх альтернатив, які я спробував нижче (і посилань на які посилаються) - це єдиний, який надійно працює.
ТармоПікаро

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

Дякую, Джон, у мене є помилковий позитив, поки я не використовую ваш код, щоб ще раз перевірити групу користувачів правило IdentifyReference!
Пол L

1
мені довелося додати додатковий чек на особистість.Власник == правило.ІдентичністьРеференція, оскільки у мене був користувач, який надав доступ, але не в будь-яких групах, як-от спеціалізований локальний обліковий запис для послуг
grinder22

2
AccessControlType заперечення має перевагу над дозволом, тому повинні бути перевірені також повністю ретельні правила, які заперечують право доступу, а при перевірці типів заборони - так, (AccessRight & rule.FileSystemRights) > 0тому що будь-який тип відмови в доступі, який є частиною AccessRightзасобів, у вас немає повного доступ доAccessRight
TJ Rockefeller

Як згаданий вище шліфувальний апарат22 мені потрібно було змінити; if (identity.Groups.Contains (rule.IdentityReference)) to if (ідентифікація.Groups.Contains (rule.IdentityReference) || Ident.Owner.Equals (rule.IdentityReference)), як у мене був користувач, який мав доступ, але не був ' t в будь-якій з груп.
ембрайт

13

IMHO - єдиний на 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;
    }
}

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 , яка пояснює чіткі правила заборони доступу. Функція проходить через всі FileSystemAccessRules для каталогу та перевіряє, чи є поточний користувач у ролі, яка має доступ до каталогу. Якщо таких ролей не знайдено або користувач перебуває в ролі з забороненим доступом, функція повертає помилкове значення. Щоб перевірити права читання, передайте FileSystemRights.Read до функції; для прав на запис передайте FileSystemRights.Write. Якщо ви хочете перевірити довільні права користувача, а не поточні, замініть поточний користувач 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 (але добре на моїй версії Dev Machine 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в цьому випадку це повернення буде хибним , навіть якщо користувач робить мати дозвіл на запис.
Кріс Б

Найзручніша відповідь для кодування :) Я також використовую лише цей, але коли є великі паралельні запити, то стільки читання / запису може уповільнити продуктивність, тому в цих випадках ви можете використовувати методологію accesscontrol, як вказано в інших відповідях.
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

Я погоджуюся з Ешем, це повинно бути добре. Крім того, ви можете використовувати декларативний 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 та перевірка відповідності Ідентифікатора безпеки не працює, оскільки користувач може бути членом групи, від якої він може отримати / втратити привілей. Гірше, ніж це вкладені групи.

Я знаю, що це стара тема, але є кращий спосіб для тих, хто зараз дивиться.

Якщо користувач має права доступу до читання, можна використовувати 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.