87 votes

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

Dans mon .NET 2.0 de l'application, j'ai besoin de vérifier si les autorisations suffisantes existent 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 écrire qu'un seul octet, la suppression de lui-même par la suite pour vérifier que les autorisations n'existe pas.

J'ai pensé que le meilleur moyen de vérifier est de réellement essayer de le faire, attraper toutes les exceptions qui se produisent, mais je ne suis pas particulièrement heureux de l'Exception générale de capture, ainsi il y a de mieux, ou peut-être plus accepté de le faire?

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

50voto

Richard Points 1071

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 :)

35voto

Deny l'emporte sur Allow. Les règles locales priment sur les règles héritées. J'ai vu beaucoup de solutions (y compris quelques réponses ici), mais aucun d'eux ne prend en compte que les règles sont hérités ou non. Donc je vous propose la démarche suivante qui tient compte de la règle de l'héritage (soigneusement enveloppée dans une classe):

public class CurrentUserSecurity
{
    WindowsIdentity _currentUser;
    WindowsPrincipal _currentPrincipal;

    public CurrentUserSecurity()
    {
        _currentUser = WindowsIdentity.GetCurrent();
        _currentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
    }

    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++) {
            FileSystemAccessRule 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 fait l'expérience que cela ne fonctionne pas toujours sur des ordinateurs distants que vous n'aurez pas toujours le droit d'interroger le fichier des droits d'accès. La solution dans ce cas est d'essayer; peut-être même en essayant juste de créer un fichier temporaire, si vous avez besoin de savoir l'accès à droite avant de travailler avec le "réel" des fichiers.

25voto

Kev Points 60744

Les réponses par Richard et Jason sont en quelque sorte dans la bonne direction. Cependant, ce que vous devez faire, c'est le calcul de l'effectif autorisations pour l'identité de l'utilisateur de l'exécution de votre code. Aucun des exemples ci-dessus correctement compte de l'appartenance à un groupe par exemple.

Je suis sûr que Keith Brown avait un peu de code pour le faire, dans sa version du wiki (hors ligne en ce moment) de L' .NET Développeurs Guide de Sécurité Windows. C'est également discuté de façon raisonnablement détaillée dans sa Programmation de Sécurité Windows livre.

Le calcul efficace des autorisations n'est pas pour les faibles de cœur et de votre code de tenter la création d'un fichier et d'attraper la sécurité de l'exception levée est probablement le chemin de moindre résistance.

19voto

Bryce Wagner Points 1241

La accepté de répondre par Kev à cette question n'est pas réellement donner tout le code, c'est juste des points à d'autres ressources que je n'ai pas accès. Voici donc ma meilleure tentative à la fonction. Il fait les vérifications que l'autorisation qu'il regarde est une "Écriture" et que l'utilisateur appartient au groupe approprié.

Il pourrait ne pas être complète à l'égard des chemins d'accès au réseau ou que ce soit, mais il est assez bon pour mon but, la vérification des fichiers de configuration locale dans "Program Files" pour writability:

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

7voto

arbiter Points 5503

Messagerie Internet uniquement, vous devez utiliser 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 plus sujette aux erreurs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X