Вопрос по file-permissions, .net, c#, directory, winforms – Проверка разрешений на запись в каталог и файл в .NET

74

В моем приложении .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;
        }
    }
Спасибо за код, хотя, с одной стороны, у вызывающей стороны может сложиться ложное впечатление, что разрешение на запись отсутствует, если пользователь может писать, но не может удалять. Я бы изменил это, чтобы использовать FileMode.Create и избавиться от удаления файла. Очевидно, вам больше не понадобится этот код, но я пишу его для будущих читателей. n00b
Что делать, если кто-то пробивает, а затем пробивает на следующий день. Что, если они пробьют, а затем выбьют два дня спустя? Я уверен, что люди не должны делать такие вещи, но поведение должно быть определено. Scott Hannen
string fullPath = directory + TEMP_FILE; Пожалуйста, используйте метод Path.Combine вместо объединения строк, чтобы получить fullPath.Path.Combine(directory, TEMP_FILE) nomail

Ваш Ответ

8   ответов
0

по этой ссылке: http://www.authorcode.com/how-to-check-file-permission-to-write-in-c/

проще использовать существующий класс SecurityManager

string FileLocation = @"C:\test.txt";
FileIOPermission writePermission = new FileIOPermission(FileIOPermissionAccess.Write, FileLocation);
if (SecurityManager.IsGranted(writePermission))
{
  // you have permission
}
else
{
 // permission is required!
}

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

[Obsolete("IsGranted is obsolete and will be removed in a future release of the .NET Framework.  Please use the PermissionSet property of either AppDomain or Assembly instead.")]
5

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

Вы, вероятно, хотели сказать: «Этот метод проще и намногоless подвержен ошибкам.
21

ОтветыРичард а такжеДжейсон вроде как в правильном направлении. Однако то, что вы должны делать, этовычисление эффективных разрешений для идентификации пользователя, выполняющего ваш код. Например, ни один из приведенных выше примеров не учитывает членство в группе.

Я уверенКит Браун был некоторый код, чтобы сделать это в еговики версия (офлайн в это время) изРуководство разработчика по .NET по безопасности Windows, Это также обсуждается достаточно подробно в егоПрограммирование Windows Security книга.

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

Это также единственный надежный метод, так как в противном случае кто-то может изменить разрешение между проверкой и попыткой сохранения (маловероятно, но возможно).
почему все должно быть чертовски сложно!
@Triynko - предлагаю прочитать статью, которую я цитировал:groups.google.com/group/… - вычисленияeffective разрешения не так просто, как кажется. Будь моим гостем и предоставь ответ, чтобы доказать, что я не прав.
@ Andy - да, это путь наименьшего сопротивления, если вы не хотите писать код для вычисления эффективных разрешений.
Спасибо за это. Таким образом, единственное изменение, которое я должен сделать в своем коде, это перехватить исключение безопасности вместо общего «исключения»? Andy
31

Deny имеет приоритет надAllow, Локальные правила имеют приоритет над унаследованными правилами. Я видел много решений (включая некоторые ответы, показанные здесь), но ни одно из них не учитывает, являются ли правилаinherited или нет. Поэтому я предлагаю следующий подход, который учитывает наследование правил (аккуратно заключенное в класс):

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

Однако я понял, что это не всегда работает на удаленных компьютерах, поскольку у вас не всегда будет право запрашивать права доступа к файлам. Решение в этом случае состоит в том, чтобы попытаться; возможно, даже просто пытаясь создать временный файл, если вам нужно знать право доступа, прежде чем работать с & quot; реальным & quot; файлы.

Я думаю, что этот ответ - лучший способ для достижения этого, другие ответы используют тот же способ, чтобы получить результат, но так как только этот ответ вычисляет унаследованные правила и локальные правила, он является наиболее точным, я думаю. Благодаря & амп; Congrats.
46

Directory.GetAcessControl(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;
}

(FileSystemRights.Write & rights) == FileSystemRights.Write использует что-то под названием "Флаги" Между прочим, если вы не знаете, что это такое, вам действительно стоит прочесть :)

@aloneguid: & quot; GetAccessRules & quot; методы возвращают AuthorizationRuleCollection. Класс AthorizationRule имеет свойство IdentityReference, тип среды выполнения которого на самом деле будет одним из двух, которые наследуются от типа IdenityReference (NTAccount или Security), который, как вы видите, указан в вызове GetAccessRules. Именно с помощью экземпляра IdentityReference (или его производных типов) вы можете узнать, к какому пользователю применяется правило. Это будет в форме SID или имени NTAccount.
Это, конечно, вызовет исключение, если вы на самом деле не можете получить ACL для каталога.
Что он проверяет? Этот каталог имеет права на запись, но для какого пользователя? :)
Попробуйте запустить это на системном диске в Windows 7 с приложением без прав администратора, оно вернет true, но когда вы попытаетесь записать в c: \, вы получите исключение, сообщающее, что у вас нет доступа!
Это работает, если вы просто хотите посмотреть, есть ли у текущего пользователя доступ на запись.
18

Принятый ответ Кева на этот вопрос фактически не дает никакого кода, он просто указывает на другие ресурсы, к которым у меня нет доступа. Итак, вот моя лучшая попытка использования этой функции. На самом деле он проверяет, является ли разрешение, которое он просматривает, «записью». разрешение и что текущий пользователь принадлежит к соответствующей группе.

Возможно, он не является полным в отношении сетевых путей или чего-либо еще, но он достаточно хорош для моей цели, проверяя локальные файлы конфигурации в разделе «Программные файлы». для возможности записи:

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;
    }
}
Когда вы добавляете & quot; rule.IdentityReference.Value & quot; в качестве параметра currentuser.IsInRole () вы используете метод IsInRole (string), который пытается сопоставить по обычному & quot; домену \ пользователю & quot; значение. Таким образом, вы нажимаете строку SID вместо строки имени пользователя. Однако, если вы используете мою строку перед этим, вы получите объект SecurityIdentifier, который соответствует пользователю данного SID. Это & quot; строка & quot; Перегрузка аргумента - небольшая ловушка для разработчиков, еще раз она принимает имя учетной записи или группы в формате, пригодном для использования человеком, а не в представлении строки SID.
Этот не работает для групп, но для буквально добавленных имен учетных записей только в моем случае
Проблема в том, что «новый SecurityIdentifier (SDDLFormat)» не работает с обычными именами групп (вы получаете исключение для аргумента). Поэтому я добавил проверку того, находится ли он в формате SDDL.
Это решение работало для меня, но было одна проблема с сетевой папкой. В папке есть правило доступа, позволяющее писать вBUILTIN\Administrators, И поскольку я являюсь администратором на своей местной станции, фрагмент по ошибке вернулсяtrue.
Так что же это как-то связано с "(S-1-5-21-397955417-626881126-188441444-512)"? тип формата? Преобразование строки в SecurityIdentifier, как это, решило вашу проблему? Из вашего комментария неясно, работает ли он сейчас для вас или нет.
3

Попробуйте поработать с этим фрагментом C #, который я только что создал:

using System;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string directory = @"C:\downloads";

            DirectoryInfo di = new DirectoryInfo(directory);

            DirectorySecurity ds = di.GetAccessControl();

            foreach (AccessRule rule in ds.GetAccessRules(true, true, typeof(NTAccount)))
            {
                Console.WriteLine("Identity = {0}; Access = {1}", 
                              rule.IdentityReference.Value, rule.AccessControlType);
            }
        }
    }
}

А такжездесь & APOS; s Ссылка, которую вы также можете посмотреть. Мой код может дать вам представление о том, как вы могли бы проверить наличие разрешений перед попыткой записи в каталог.

В этом случае да.NTAccount представляет учетную запись пользователя на ПК с Windows, поэтому она необходима в приведенном выше коде.
typeof возвращает тип объекта, в этом случаеNTAccount. docs.microsoft.com/en-us/dotnet/csharp/language-reference/…     Призыв кGetAccessRules() нужен тип учетной записи при вызове.msdn.microsoft.com/en-us/library/…
Зачем использоватьNTAccount? всегда использоватьNTAccount ?
-1
private static void GrantAccess(string file)
        {
            bool exists = System.IO.Directory.Exists(file);
            if (!exists)
            {
                DirectoryInfo di = System.IO.Directory.CreateDirectory(file);
                Console.WriteLine("The Folder is created Sucessfully");
            }
            else
            {
                Console.WriteLine("The Folder already exists");
            }
            DirectoryInfo dInfo = new DirectoryInfo(file);
            DirectorySecurity dSecurity = dInfo.GetAccessControl();
            dSecurity.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), FileSystemRights.FullControl, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.NoPropagateInherit, AccessControlType.Allow));
            dInfo.SetAccessControl(dSecurity);

        }
Что такоеWellKnownSidType.WorldSid ?

Похожие вопросы