C # Testen Sie, ob der Benutzer Schreibzugriff auf einen Ordner hat

187

Ich muss testen, ob ein Benutzer in einen Ordner schreiben kann, bevor ich dies tatsächlich versuche.

Ich habe die folgende Methode (in C # 2.0) implementiert, die versucht, die Sicherheitsberechtigungen für den Ordner mithilfe der Directory.GetAccessControl () -Methode abzurufen .

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

Als ich googelte, wie man auf Schreibzugriff testet, kam nichts dergleichen auf und es schien sehr kompliziert, Berechtigungen in Windows tatsächlich zu testen. Ich mache mir Sorgen, dass ich die Dinge zu stark vereinfache und dass diese Methode nicht robust ist, obwohl sie zu funktionieren scheint.

Funktioniert meine Methode zum Testen, ob der aktuelle Benutzer über Schreibzugriff verfügt, ordnungsgemäß?

Chris B.
quelle
13
Ist es nicht dasselbe, keinen Zugriff zum Anzeigen der Berechtigungen zu haben, als nicht darauf schreiben zu dürfen?
deed02392

Antworten:

60

Dies ist eine absolut gültige Methode, um in C # nach Ordnerzugriff zu suchen. Der einzige Ort , es fallen könnte nach unten ist , wenn Sie diese in einer engen Schleife anrufen müssen , wo der Aufwand einer Ausnahme kann ein Problem sein.

Es wurden bereits andere ähnliche Fragen gestellt .

Asche
quelle
1
Lustigerweise hatte ich eine dieser anderen Fragen in einem anderen Tab geöffnet, aber die Antwort zu DirectorySecurity nicht gesehen. Bringen Sie mir bei, alle Antworten zu lesen, nicht nur die akzeptierte ;-)
Chris B
Wird es nicht auch herunterfallen, wenn Sie lange Pfade in Windows verwenden?
Alexandru
11
Das sagt Ihnen nicht, ob Sie Schreibberechtigung haben, sondern nur, ob Sie nach Berechtigungen für diesen Ordner suchen können oder nicht. Möglicherweise können Sie auch schreiben, aber nicht nach Berechtigungen suchen.
RandomEngy
65

Ich weiß zu schätzen, dass dies für diesen Beitrag etwas spät am Tag ist, aber Sie finden diesen Code möglicherweise hilfreich.

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

Legen Sie das in einer Konsolen-App ab und prüfen Sie, ob es das tut, was Sie brauchen.

Duncan Howe
quelle
Genau am Ziel! Hilft mir sehr!
Smwikipedia
Ich erhalte eine Ausnahme beim Aufruf von, GetAccessControlaber meine Software kann tatsächlich in das Verzeichnis schreiben, das ich gerade betrachte.
Jon Cage
@ JonCage - welche Ausnahme bekommen Sie? Das erste, was mir in den Sinn kommt, ist ironischerweise ein Sicherheitsproblem. Hat das Konto, unter dem Ihre App ausgeführt wird, die Berechtigung, die ACL-Informationen abzurufen?
Duncan Howe
1
Sie müssen eine Prüfung für den Typ FileSystemAccessRule hinzufügen. Wenn es sich um eine Verweigerungsregel handelt, werden Sie sie fälschlicherweise als beschreibbar melden.
tdemay
2
Ich versuche das zu benutzen. Ich habe ein anderes Problem gefunden. Wenn Rechte nur Gruppen und nicht bestimmten Benutzern zugewiesen werden, wird fälschlicherweise gemeldet, dass sie keinen Schreibzugriff haben. Beispiel: Schreibzugriff für "Authentifizierte Benutzer"
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;
    }
}
Priit
quelle
7
Diese Antwort fängt alle Ausnahmen ab, die beim Schreiben einer Datei auftreten können, nicht nur Berechtigungsverletzungen.
Matt Ellen
7
@ GY, string tempFileName = Path.GetRandomFileName();offensichtlich
Alexey Khoroshikh
3
@Matt, dies beantwortet genau die gestellte Frage "Ist das Verzeichnis beschreibbar?", Unabhängig vom Grund des Fehlers. Sie antworten eher auf " Warum kann ich nicht in das Verzeichnis schreiben". :)
Alexey Khoroshikh
1
Ich bekomme mit diesem Code ein falsches Positiv. File.Create () wird in Ordnung ausgeführt (und hinterlässt eine temporäre Datei, wenn Sie die letzte Option ändern), obwohl der ausführende Benutzer keine Berechtigung zum Schreiben in diesen Ordner hat. Wirklich sehr, sehr seltsam - ich habe eine Stunde lang versucht herauszufinden, warum, aber ich bin ratlos.
NickG
4
Von allen Alternativen, die ich unten ausprobiert habe (und auf die verwiesen wird), ist dies die einzige, die zuverlässig funktioniert.
TarmoPikaro
23

Ich habe die meisten davon ausprobiert, aber sie geben aus demselben Grund falsch positive Ergebnisse aus. Es reicht nicht aus, das Verzeichnis auf eine verfügbare Berechtigung zu testen. Sie müssen überprüfen, ob der angemeldete Benutzer Mitglied einer Gruppe ist, die über diese Berechtigung verfügt Genehmigung. Dazu erhalten Sie die Benutzeridentität und prüfen, ob sie Mitglied einer Gruppe ist, die die FileSystemAccessRule IdentityReference enthält. Ich habe dies getestet, funktioniert einwandfrei ..

    /// <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;
    }
JGU
quelle
Danke John, ich habe auch falsch positive Ergebnisse, bis ich Ihren Code verwendet habe, um die Benutzergruppe erneut zu überprüfen. Die Regel IdentifyReference!
Paul L
1
Ich musste eine zusätzliche Überprüfung auf Identität hinzufügen. Eigentümer == Regel. Identitätsreferenz, da ich einen Benutzer hatte, der Zugriff gewährte, aber nicht in einer Gruppe, wie ein dediziertes lokales Konto für Dienste
grinder22
1
Die Verweigerung von AccessControlType hat Vorrang vor dem Zulassen. Daher sollten auch vollständig gründliche Regeln überprüft werden, die das Zugriffsrecht verweigern. Wenn Sie nach Verweigerungstypen suchen, sollte dies darauf zurückzuführen sein, dass jeder verweigerte Unterzugriffstyp, der (AccessRight & rule.FileSystemRights) > 0Teil der AccessRightMittel ist, nicht vollständig ist Zugang zuAccessRight
TJ Rockefeller
Wie oben erwähnt, musste ich mich ändern. if (identity.Groups.Contains (rule.IdentityReference)) to if (identity.Groups.Contains (rule.IdentityReference) || identity.Owner.Equals (rule.IdentityReference)), da ich einen Benutzer hatte, der Zugriff hatte, aber nicht war. t in einer der Gruppen.
Ehambright
13

Zum Beispiel funktioniert diese Methode für alle Benutzer (Builtin \ Users) einwandfrei - viel Spaß.

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;
    }
}
UGEEN
quelle
12

IMHO ist die einzige 100% zuverlässige Möglichkeit zu testen, ob Sie in ein Verzeichnis schreiben können, darin zu schreiben und schließlich Ausnahmen abzufangen.

Darin Dimitrov
quelle
8

Versuche dies:

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");
}
CsabaS
quelle
Wenn ich mich nicht irre, ist dies nah, aber nicht ganz da - es übersieht die Tatsache, dass es sein fsAccessRule.AccessControlTypekönnte AccessControlType.Deny.
Jonathan Gilbert
Dies funktionierte für mich auf meinem Win7-Entwicklungscomputer, schlägt jedoch unter Win10 fehl (sowohl für einen Tester als auch für meinen eigenen Testcomputer). Die Änderung von ssds (siehe unten) scheint das Problem zu beheben.
Winwaed
6

Ihr Code erhält das DirectorySecurityfür ein bestimmtes Verzeichnis und behandelt eine Ausnahme (da Sie keinen Zugriff auf die Sicherheitsinformationen haben) korrekt. In Ihrem Beispiel fragen Sie das zurückgegebene Objekt jedoch nicht ab, um festzustellen, welcher Zugriff zulässig ist - und ich denke, Sie müssen dies hinzufügen.

Vinay Sajip
quelle
+1 - Ich bin gerade auf dieses Problem gestoßen, bei dem beim Aufrufen von GetAccessControl keine Ausnahme ausgelöst wurde. Beim Versuch, in dasselbe Verzeichnis zu schreiben, wird jedoch eine nicht autorisierte Ausnahme angezeigt.
Mayo
6

Hier ist eine modifizierte Version der Antwort von CsabaS , die explizite Regeln für die Verweigerung des Zugriffs berücksichtigt. Die Funktion durchläuft alle FileSystemAccessRules für ein Verzeichnis und prüft, ob sich der aktuelle Benutzer in einer Rolle befindet, die Zugriff auf ein Verzeichnis hat. Wenn keine solchen Rollen gefunden werden oder sich der Benutzer in einer Rolle mit verweigertem Zugriff befindet, gibt die Funktion false zurück. Übergeben Sie FileSystemRights.Read an die Funktion, um die Leserechte zu überprüfen. Übergeben Sie für Schreibrechte FileSystemRights.Write. Wenn Sie die Rechte eines beliebigen Benutzers und nicht die Rechte des aktuellen Benutzers überprüfen möchten, ersetzen Sie die gewünschte WindowsIdentity durch die aktuelle Benutzer-Windows-Identität. Ich würde auch davon abraten, sich auf solche Funktionen zu verlassen, um festzustellen, ob der Benutzer das Verzeichnis sicher verwenden kann. Diese Antwort erklärt perfekt warum.

    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;
    }
sdds
quelle
Csabas Code schlug unter Windows 10 für mich fehl (aber auf meinem Win7-Entwicklungscomputer in Ordnung). Das Obige scheint das Problem zu beheben.
Winwaed
4

Die oben genannten Lösungen sind gut, aber für mich finde ich diesen Code einfach und praktikabel. Erstellen Sie einfach eine temporäre Datei. Wenn die Datei erstellt wird, hat der mittlere Benutzer Schreibzugriff.

        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;
        }
Ali Asad
quelle
3
Nett! Eine Sache ist jedoch, dass der Benutzer möglicherweise über eine CreateBerechtigung verfügt, dies jedoch nicht Deleteder Fall ist. Dies würde false zurückgeben, obwohl der Benutzer über eine Schreibberechtigung verfügt.
Chris B
Die bequemste Antwort für die Codierung :) Ich verwende diese auch nur, wenn jedoch große gleichzeitige Anforderungen vorliegen, kann so viel Lesen / Schreiben die Leistung beeinträchtigen, sodass Sie in diesen Fällen die in anderen Antworten angegebene Zugriffssteuerungsmethode verwenden können.
Vibs2006
1
Verwenden Sie Path.Combinestattdessen wie Path.Combine(tempfilepath, "temp.txt").
ΩmegaMan
3

Sie können versuchen, den folgenden Codeblock zu verwenden, um zu überprüfen, ob das Verzeichnis Schreibzugriff hat. Es überprüft die 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 
}
RockWorld
quelle
2

Ihr Code enthält eine potenzielle Race-Bedingung. Was passiert, wenn der Benutzer beim Überprüfen die Berechtigung zum Schreiben in den Ordner hat, diese Berechtigung jedoch entzogen wird, bevor der Benutzer tatsächlich in den Ordner schreibt? Das Schreiben löst eine Ausnahme aus, die Sie abfangen und behandeln müssen. Die erste Überprüfung ist also sinnlos. Sie können genauso gut einfach schreiben und Ausnahmen behandeln. Dies ist das Standardmuster für Ihre Situation.


quelle
1

Es reicht nicht unbedingt aus, nur zu versuchen, auf die betreffende Datei zuzugreifen. Der Test wird mit den Berechtigungen des Benutzers ausgeführt, der das Programm ausführt. Dies sind nicht unbedingt die Benutzerberechtigungen, gegen die Sie testen möchten.

Mort
quelle
0

Ich stimme Ash zu, das sollte in Ordnung sein. Alternativ können Sie deklaratives CAS verwenden und verhindern, dass das Programm überhaupt ausgeführt wird, wenn es keinen Zugriff hat.

Ich glaube, einige der CAS-Funktionen sind in C # 4.0 möglicherweise nicht vorhanden, was ich gehört habe. Ich bin mir nicht sicher, ob dies ein Problem sein könnte oder nicht.

Ian
quelle
0

GetAccessControl () konnte unter Windows 7 keine Ausnahme auslösen, wie in der akzeptierten Antwort empfohlen.

Am Ende habe ich eine Variation der Antwort von sdds verwendet :

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

Hoffe das hilft.

Patrick
quelle
0

Ich hatte das gleiche Problem: Wie kann ich überprüfen, ob ich in einem bestimmten Verzeichnis lesen / schreiben kann? Am Ende hatte ich die einfache Lösung, um ... es tatsächlich zu testen. Hier ist meine einfache, aber effektive Lösung.

 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

}

Ich hoffe es hilft !

l.raimondi
quelle
0

Die meisten Antworten hier prüfen nicht auf Schreibzugriff. Es wird nur geprüft, ob der Benutzer / die Gruppe die Berechtigung lesen kann (Lesen Sie die ACE-Liste der Datei / des Verzeichnisses).

Das Durchlaufen von ACE und das Überprüfen, ob es mit der Sicherheitskennung übereinstimmt, funktioniert nicht, da der Benutzer Mitglied einer Gruppe sein kann, von der er möglicherweise Berechtigungen erhält / verliert. Schlimmer noch sind verschachtelte Gruppen.

Ich weiß, dass dies ein alter Thread ist, aber es gibt einen besseren Weg für jeden, der jetzt sucht.

Vorausgesetzt, der Benutzer verfügt über die Berechtigung zum Lesen von Berechtigungen, kann die Authz-API verwendet werden, um den effektiven Zugriff zu überprüfen.

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

Kamaal
quelle