Test C # se l'utente ha accesso in scrittura a una cartella


187

Devo verificare se un utente può scrivere in una cartella prima di tentare effettivamente di farlo.

Ho implementato il seguente metodo (in C # 2.0) che tenta di recuperare le autorizzazioni di sicurezza per la cartella utilizzando il metodo 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;
    }
}

Quando cercavo su Google come testare l'accesso in scrittura non è emerso nulla di simile e mi è sembrato molto complicato testare le autorizzazioni in Windows. Temo di semplificare eccessivamente le cose e che questo metodo non è solido, anche se sembra funzionare.

Il mio metodo per verificare se l'utente corrente ha accesso in scrittura funzionerà correttamente?


13
Avere accesso per visualizzare le autorizzazioni non equivale a non poterle scrivere?
deed02392,

Risposte:


61

Questo è un modo perfettamente valido per verificare l'accesso alle cartelle in C #. L'unico posto in cui potrebbe cadere è se è necessario chiamare questo in un ciclo stretto in cui l'overhead di un'eccezione potrebbe essere un problema.

Ci sono state altre domande simili poste in precedenza.


1
Stranamente ho avuto una di quelle altre domande aperte in un'altra scheda ma non avevo visto la risposta su DirectorySecurity, insegnami a leggere tutte le risposte non solo quella accettata ;-)
Chris B

Non cadrà anche quando usi percorsi lunghi in Windows?
Alexandru,

11
Questo non ti dirà se hai i permessi di scrittura, ti dirà solo se puoi cercare i permessi su quella cartella o no. Inoltre potresti essere in grado di scrivere ma non essere in grado di cercare le autorizzazioni.
RandomEngy,

65

Apprezzo che questo post sia un po 'in ritardo per questo post, ma potresti trovare utile questo codice.

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

Rilascialo in un'app Console e vedi se fa quello che ti serve.


Proprio sul bersaglio! Mi aiuta molto!
smwikipedia,

Ricevo un'eccezione sulla chiamata GetAccessControlma il mio software è effettivamente in grado di scrivere nella directory che sto guardando ..?
Jon Cage,

@JonCage - quale eccezione stai ricevendo? La prima cosa che mi viene in mente è, ironia della sorte, un problema di sicurezza. L'account in cui è in esecuzione l'app ha l'autorizzazione per ottenere le informazioni ACL?
Duncan Howe,

1
È necessario aggiungere un controllo per il tipo FileSystemAccessRule. Se è una regola Nega, la segnalerai erroneamente come scrivibile.
martedì

2
Sto cercando di usare questo. Trovato un altro problema. Se i diritti vengono assegnati solo a gruppi e non a utenti specifici, ciò segnalerà erroneamente che non hanno accesso in scrittura. Ad esempio, l'accesso in scrittura concesso a "Utenti autenticati"
oggi

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

7
Questa risposta catturerà tutte le eccezioni che potrebbero verificarsi durante il tentativo di scrivere un file, non solo le violazioni delle autorizzazioni.
Matt Ellen,

7
@GY, string tempFileName = Path.GetRandomFileName();evidentemente
Alexey Khoroshikh,

3
@Matt, questo risponde esattamente alla domanda "è la directory è scrivibile", indipendentemente dal motivo dell'errore. Preferisci rispondere a " perché non riesco a scrivere nella directory". :)
Alexey Khoroshikh,

1
Ottengo un falso positivo con questo codice. File.Create () funziona correttamente (e lascia un file temporaneo se si modifica l'ultima opzione) anche se l'utente che esegue non ha l'autorizzazione per scrivere in quella cartella. Davvero davvero strano - ho passato un'ora a cercare di capire perché, ma sono perplesso.
NickG

4
Da tutte le alternative che ho provato di seguito (e link di riferimento) - questa è l'unica che funziona in modo affidabile.
TarmoPikaro,

24

Ho provato la maggior parte di questi, ma danno falsi positivi, tutti per lo stesso motivo. Non è sufficiente testare la directory per un'autorizzazione disponibile, è necessario verificare che l'utente che ha effettuato l'accesso sia un membro di un gruppo che ha quello autorizzazione. Per fare ciò si ottiene l'identità degli utenti e si controlla se è un membro di un gruppo che contiene FileSystemAccessRule IdentityReference. L'ho provato, funziona perfettamente ..

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

Grazie John, ho anche dei falsi positivi fino a quando non uso il tuo codice per controllare nuovamente il gruppo di utenti la regola IdentifyReference!
Paul L,

1
ho dovuto aggiungere un controllo aggiuntivo per identity.Owner == rule.IdentityReference in quanto avevo un utente che aveva accesso ma non in alcun gruppo, come un account locale dedicato per i servizi
grinder22

2
AccessControlType deny ha la precedenza su allow, quindi per essere completamente controllate devono essere controllate anche le regole che negano il diritto di accesso, e quando si controllano i tipi di negazione dovrebbe essere (AccessRight & rule.FileSystemRights) > 0perché qualsiasi tipo di sub-accesso negato che fa parte AccessRightsignifica che non si ha pieno accesso aAccessRight
TJ Rockefeller,

Come grinder22 menzionato sopra, avevo bisogno di cambiare; if (identity.Groups.Contains (rule.IdentityReference)) a if (identity.Groups.Contains (rule.IdentityReference) || identity.Owner.Equals (rule.IdentityReference)) poiché avevo un utente che aveva accesso ma non era " t in uno qualsiasi dei gruppi.
ehambright,

13

IMHO l'unico modo affidabile al 100% per verificare se è possibile scrivere in una directory è effettivamente scrivere su di essa e alla fine catturare le eccezioni.


13

Ad esempio per tutti gli utenti (Builtin \ Users), questo metodo funziona perfettamente. Divertiti.

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

8

Prova questo:

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

Se non sbaglio, questo è vicino ma non proprio lì - trascura il fatto che fsAccessRule.AccessControlTypepotrebbe essere AccessControlType.Deny.
Jonathan Gilbert,

Questo funzionava per me sulla mia macchina di sviluppo Win7 ma fallisce su Win10 (sia per un tester che per la mia macchina di prova). La modifica di ssds (vedi sotto) sembra risolverlo.
vinto il

6

Il codice ottiene il valore DirectorySecurityper una determinata directory e gestisce un'eccezione (a causa del fatto che non hai accesso alle informazioni di sicurezza) correttamente. Tuttavia, nel tuo esempio in realtà non si interroga l'oggetto restituito per vedere quale accesso è consentito - e penso che sia necessario aggiungere questo.


+1 - Ho appena riscontrato questo problema in cui non è stata generata un'eccezione quando si chiama GetAccessControl, ma si ottiene un'eccezione non autorizzata quando si tenta di scrivere nella stessa directory.
Mayo,

6

Ecco una versione modificata della risposta di CsabaS , che spiega esplicitamente le regole di accesso negato. La funzione passa attraverso tutti i FileSystemAccessRules per una directory e verifica se l'utente corrente ha un ruolo che ha accesso a una directory. Se tali ruoli non vengono trovati o l'utente ha un ruolo con accesso negato, la funzione restituisce false. Per verificare i diritti di lettura, passare FileSystemRights.Read alla funzione; per i diritti di scrittura, passare FileSystemRights.Write. Se si desidera verificare i diritti di un utente arbitrario e non quello attuale, sostituire l'attuale ID utente di Windows con l'ID Windows desiderato. Vorrei anche sconsigliare di fare affidamento su funzioni come questa per determinare se l'utente può utilizzare la directory in modo sicuro. Questa risposta spiega perfettamente il perché.

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

Il codice di Csaba non funzionava su Windows 10 (ma andava bene sulla mia macchina sviluppatore Win7). Quanto sopra sembra risolvere il problema.
vinto il

4

Le soluzioni sopra sono buone ma per me trovo questo codice semplice e funzionale. Basta creare un file temporaneo. Se il file viene creato, l'utente medio ha accesso in scrittura.

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

3
Bello! Una cosa però è il che utente potrebbe avere Createil permesso, ma non Deletein questo caso questo sarebbe return false anche se l'utente ha ha permesso di scrittura.
Chris B,

Risposta più comoda per la codifica :) La uso anche solo per questa, tuttavia, quando ci sono richieste simultanee di grandi dimensioni, molta lettura / scrittura potrebbe rallentare le prestazioni, quindi in quei casi è possibile utilizzare la metodologia di controllo degli accessi come indicato in altre risposte.
vibs2006,

1
Usa Path.Combineinvece come Path.Combine(tempfilepath, "temp.txt").
ΩmegaMan

3

Puoi provare a seguire il seguente blocco di codice per verificare se la directory ha accesso in scrittura. Controlla il 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 
}

2

Hai una potenziale condizione di competizione nel tuo codice: cosa succede se l'utente ha le autorizzazioni per scrivere nella cartella quando controlla, ma prima che l'utente scriva effettivamente nella cartella questa autorizzazione viene revocata? La scrittura genererà un'eccezione che dovrai catturare e gestire. Quindi il controllo iniziale è inutile. Potresti anche solo scrivere e gestire eventuali eccezioni. Questo è lo schema standard per la tua situazione.



1

Il semplice tentativo di accedere al file in questione non è necessariamente sufficiente. Il test verrà eseguito con le autorizzazioni dell'utente che esegue il programma, che non sono necessariamente le autorizzazioni dell'utente su cui si desidera eseguire il test.


0

Sono d'accordo con Ash, dovrebbe andare bene. In alternativa, è possibile utilizzare CAS dichiarativo e impedire effettivamente l'esecuzione del programma in primo luogo se non hanno accesso.

Credo che alcune delle funzionalità CAS potrebbero non essere presenti in C # 4.0 da quello che ho sentito, non sono sicuro che ciò possa costituire un problema o meno.


0

Non sono riuscito a ottenere GetAccessControl () per generare un'eccezione su Windows 7 come raccomandato nella risposta accettata.

Ho finito per usare una variante della risposta di sdds :

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

Spero che questo ti aiuti.


0

Ho riscontrato lo stesso problema: come verificare se riesco a leggere / scrivere in una directory particolare. Ho finito con la soluzione semplice per ... effettivamente testarlo. Ecco la mia soluzione semplice ma 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

}

Spero che sia d'aiuto !


0

La maggior parte delle risposte qui non controlla l'accesso in scrittura. Controlla solo se l'utente / il gruppo può "Leggere l'autorizzazione" (Leggi l'elenco ACE del file / directory).

Inoltre, iterando tramite ACE e controllando se corrisponde all'identificatore di sicurezza non funziona perché l'utente può essere un membro di un gruppo dal quale potrebbe ottenere / perdere privilegi. Peggio ancora è gruppi nidificati.

So che questo è un vecchio thread, ma c'è un modo migliore per chiunque guardi ora.

A condizione che l'utente disponga del privilegio di autorizzazione di lettura, è possibile utilizzare l'API Authz per verificare l'accesso effettivo.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.