web-dev-qa-db-fra.com

Test C # si l'utilisateur a un accès en écriture à un dossier

Je dois vérifier si un utilisateur peut écrire dans un dossier avant de tenter de le faire.

J'ai implémenté la méthode suivante (en C # 2.0) qui tente de récupérer les autorisations de sécurité pour le dossier à l'aide de la méthode 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;
    }
}

Lorsque je cherchais sur Google des tests d'accès en écriture, rien de tel ne s'est présenté et il est apparu très compliqué de tester les autorisations dans Windows. Je crains que je simplifie excessivement les choses et que cette méthode ne soit pas robuste, même si elle semble fonctionner.

Ma méthode pour vérifier si l'utilisateur actuel dispose d'un accès en écriture fonctionne correctement?

173
Chris B

C'est un moyen parfaitement valide de vérifier l'accès aux dossiers en C #. Le seul endroit où il pourrait tomber est si vous devez appeler ceci dans une boucle serrée où le surcoût d'une exception peut être un problème.

Il y a eu d'autres similairesquestions posées précédemment.

59
Ash

J'apprécie que ce post soit un peu tard dans la journée, mais vous trouverez peut-être ce code utile.

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

Déposez-le dans une application console et voyez s'il répond à vos besoins.

61
Duncan Howe
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;
    }
}
58
priit

J'ai essayé la plupart d'entre eux, mais ils donnent des faux positifs, tous pour la même raison. Il ne suffit pas de tester l'annuaire pour vérifier qu'il dispose d'une autorisation disponible, vous devez vérifier que l'utilisateur connecté est membre d'un groupe disposant de ce droit. autorisation. Pour ce faire, vous obtenez l'identité de l'utilisateur et vérifiez s'il est membre d'un groupe contenant FileSystemAccessRule IdentityReference. J'ai testé cela, fonctionne parfaitement.

    /// <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;
    }
20
JGU

Par exemple, pour tous les utilisateurs (Builtin\Users), cette méthode fonctionne bien - amusez-vous.

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

À mon humble avis, le seul moyen fiable à 100% de vérifier si vous pouvez écrire dans un répertoire consiste à l'écrire et éventuellement à intercepter des exceptions.

11
Darin Dimitrov

Essaye ça:

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");
}
8
CsabaS

Votre code obtient la DirectorySecurity pour un répertoire donné et gère une exception (en raison de votre manque d'accès aux informations de sécurité). Cependant, dans votre exemple, vous n'interrogez pas réellement l'objet renvoyé pour savoir quel accès est autorisé - et je pense que vous devez ajouter ceci.

6
Vinay Sajip

Voici une version modifiée de réponse de CsabaS , qui prend en compte les règles d'accès interdites. La fonction parcourt toutes les FileSystemAccessRules d'un répertoire et vérifie si l'utilisateur actuel est dans un rôle ayant accès à un répertoire. Si aucun rôle de ce type n'est trouvé ou si l'utilisateur occupe un rôle dont l'accès est refusé, la fonction renvoie false. Pour vérifier les droits de lecture, passez FileSystemRights.Read à la fonction; pour les droits en écriture, passez FileSystemRights.Write. Si vous souhaitez vérifier les droits d'un utilisateur arbitraire et non des droits actuels, remplacez l'élément WindowsIdentity currentUser par l'identificateur WindowsIdentity souhaité. Je vous conseillerais également de ne pas utiliser de telles fonctions pour déterminer si l'utilisateur peut utiliser le répertoire en toute sécurité. Cette réponse explique parfaitement pourquoi.

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

J'ai utilisé la même fonction pour vérifier si le fichier hasWriteAccess:

    private static bool HasWriteAccessToFile(string filePath)
    {
        try
        {
            // Attempt to get a list of security permissions from the file. 
            // This will raise an exception if the path is read only or do not have access to view the permissions. 
            File.GetAccessControl(filePath);
            return true;
        }
        catch (UnauthorizedAccessException)
        {
            return false;
        }
    }
5
Bruno Bieri

Vous pouvez essayer le bloc de code suivant pour vérifier si le répertoire a un accès en écriture. Il vérifie le 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 
}
3
RockWorld

http://www.codeproject.com/KB/files/UserFileAccessRights.aspx

Classe très utile, recherchez la version améliorée dans les messages ci-dessous.

2
user187083

Les solutions ci-dessus sont bonnes, mais pour moi, je trouve ce code simple et pratique. Il suffit de créer un fichier temporaire. Si le fichier est créé, son utilisateur moyen dispose d'un accès en écriture.

        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;
        }
2
Ali Asad

Votre code contient une condition de concurrence potentielle - que se passe-t-il si l'utilisateur dispose des autorisations nécessaires pour écrire dans le dossier lorsque vous vérifiez, mais avant que l'utilisateur n'écrit réellement dans le dossier, cette autorisation est retirée? L'écriture lève une exception que vous devrez capturer et gérer. La vérification initiale est donc inutile. Vous pouvez aussi bien écrire et gérer les exceptions. C'est le modèle standard pour votre situation.

2
ShellShock

Essayer d'accéder au fichier en question ne suffit pas forcément. Le test s'exécutera avec les autorisations de l'utilisateur exécutant le programme. Ces autorisations ne sont pas nécessairement les autorisations d'utilisateur pour lesquelles vous souhaitez effectuer un test.

1
Mort

J'ai rencontré le même problème: comment vérifier si je peux lire/écrire dans un répertoire particulier. Je me suis retrouvé avec la solution facile pour ... réellement la tester. Voici ma solution simple mais efficace.

 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

}

J'espère que ça aide !

0
l.raimondi

Je suis d'accord avec Ash, ça devrait aller. Sinon, vous pouvez utiliser un CAS déclaratif et empêcher le programme de s'exécuter s'il n'a pas d'accès.

D'après ce que j'ai entendu, certaines fonctionnalités de la CAS ne sont peut-être pas présentes en C # 4.0, sans savoir si cela pourrait poser problème ou non.

0
Ian

Je ne pouvais pas obtenir GetAccessControl () pour lever une exception sur Windows 7 comme recommandé dans la réponse acceptée.

J'ai fini par utiliser une variante de sdds answer:

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

J'espère que cela t'aides.

0
Patrick