web-dev-qa-db-fra.com

Vérification des autorisations d'écriture de répertoire et de fichier dans .NET

Dans mon application .NET 2.0, je dois vérifier s'il existe des autorisations suffisantes pour créer et écrire dans des fichiers d'un répertoire. À cette fin, j'ai la fonction suivante qui tente de créer un fichier et de lui écrire un seul octet, en se supprimant ensuite pour vérifier que les autorisations existent.

Je pensais que le meilleur moyen de vérifier consistait à essayer de le faire, en détectant les exceptions éventuelles. Cependant, je ne suis pas particulièrement heureux de la capture générale d'Exception. Existe-t-il un moyen meilleur ou peut-être plus accepté de procéder?

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

Les réponses de Richard et Jason vont en quelque sorte dans la bonne direction. Cependant, vous devez utiliser le calcul des autorisations effectives pour l'identité de l'utilisateur exécutant votre code. Aucun des exemples ci-dessus ne prend correctement en compte l'appartenance à un groupe, par exemple.

Je suis presque sûr que Keith Brown disposait d'un code pour le faire dans sa version wiki (actuellement hors connexion) de Le Guide du développeur .NET pour la sécurité Windows . Ceci est également discuté avec suffisamment de détails dans son livre Programming Windows Security .

Le calcul des autorisations effectives n’est pas pour les timorés et votre code pour tenter de créer un fichier et attraper l’exception de sécurité levée est probablement le chemin de moindre résistance. 

21
Kev

Directory.GetAcessControl(path) fait ce que vous demandez.

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 utilise quelque chose appelé "Flags" qui, si vous ne savez pas ce que c'est, vous devriez vraiment lire :)

46
richardwiden

Deny a priorité sur Allow. Les règles locales ont priorité sur les règles héritées. J'ai vu de nombreuses solutions (y compris certaines des réponses présentées ici), mais aucune d'elles ne tient compte du fait que les règles sont héritées ou non. Par conséquent, je suggère l'approche suivante qui prend en compte l'héritage des règles (soigneusement intégré dans une classe):

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

Cependant, j’ai appris que cela ne fonctionnait pas toujours sur des ordinateurs distants car vous n’auriez pas toujours le droit d’interroger les droits d’accès aux fichiers. La solution dans ce cas est d'essayer; éventuellement même en essayant simplement de créer un fichier temporaire, si vous devez connaître le droit d'accès avant de travailler avec les "vrais" fichiers.

31

La réponse acceptée par Kev à cette question ne donne en réalité aucun code, elle indique simplement d'autres ressources auxquelles je n'ai pas accès. Alors voici ma meilleure tentative à la fonction. Il vérifie en réalité que l'autorisation est une autorisation "Écriture" et que l'utilisateur actuel appartient au groupe approprié.

Cela peut ne pas être complet en ce qui concerne les chemins réseau ou autre, mais c'est assez bon pour moi, vérifier les fichiers de configuration locaux dans "Program Files" pour en écriture:

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;
    }
}
18
Bryce Wagner

Messagerie Internet uniquement, vous devez travailler avec ces répertoires comme d'habitude, mais au lieu de vérifier les autorisations avant utilisation, indiquez le moyen approprié pour gérer UnauthorizedAccessException et réagir en conséquence. Cette méthode est plus facile et beaucoup moins sujette aux erreurs.

5
arbiter

Essayez de travailler avec cet extrait de code C # que je viens de créer:

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

Et voici une référence que vous pourriez également consulter. Mon code peut vous donner une idée sur la manière de vérifier les autorisations avant d'essayer d'écrire dans un répertoire.

3
Jason Evans

selon ce lien: http://www.authorcode.com/how-to-check-file-permission-to-write-in-c/

il est plus facile d'utiliser la classe existante SecurityManager

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

mais il semble que cela soit obsolète, il est suggéré d'utiliser PermissionSet à la place.

[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.")]
0
Mulder2008