Comprobando permisos de escritura de directorio y archivo en .NET

En mi aplicación .NET 2.0, necesito verificar si existen permisos suficientes para crear y escribir archivos en un directorio. Para este fin, tengo la siguiente función que intenta crear un archivo y escribirle un byte único, eliminándose después para probar que los permisos existen.

Pensé que la mejor manera de comprobar era intentar y hacerlo, capturando cualquier excepción que ocurriera. Aunque no estoy particularmente contento con la captura general de Excepción, ¿hay una forma mejor o quizás más aceptada de hacerlo?

private const string TEMP_FILE = "\\tempFile.tmp"; ///  /// Checks the ability to create and write to a file in the supplied directory. ///  /// String representing the directory path to check. /// True if successful; otherwise false. 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; } } 

Las respuestas de Richard y Jason están en la dirección correcta. Sin embargo, lo que debe hacer es calcular los permisos efectivos para la identidad del usuario que ejecuta su código. Ninguno de los ejemplos anteriores representa correctamente la pertenencia a un grupo, por ejemplo.

Estoy bastante seguro de que Keith Brown tenía algún código para hacer esto en su versión wiki (sin conexión en este momento) de The .NET Developers Guide to Windows Security . Esto también se discute con detalles razonables en su libro Programming Windows Security .

La computación de los permisos efectivos no es para los pusilánimes y su código para intentar crear un archivo y capturar la excepción de seguridad lanzada es probablemente la ruta de menor resistencia.

Directory.GetAcessControl(path) hace lo que está pidiendo.

 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 está usando algo llamado “Flags” por cierto, que si no sabes qué es lo que realmente deberías leer 🙂

Deny tiene prioridad sobre Allow . Las reglas locales tienen prioridad sobre las reglas heredadas. He visto muchas soluciones (incluidas algunas respuestas que se muestran aquí), pero ninguna de ellas tiene en cuenta si las reglas son heredadas o no. Por lo tanto, sugiero el siguiente enfoque que considera la herencia de reglas (cuidadosamente envuelto en una clase):

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

Sin embargo, hice la experiencia de que esto no siempre funciona en computadoras remotas, ya que no siempre tendrá derecho a consultar allí los derechos de acceso a los archivos. La solución en ese caso es intentar; posiblemente incluso tratando de crear un archivo temporal, si necesita saber el derecho de acceso antes de trabajar con los archivos "reales".

La respuesta aceptada por Kev a esta pregunta en realidad no da ningún código, solo apunta a otros recursos a los que no tengo acceso. Así que aquí está mi mejor bash en la función. En realidad comprueba que el permiso que está mirando es un permiso de “Escritura” y que el usuario actual pertenece al grupo apropiado.

Puede que no esté completo con respecto a las rutas de red o lo que sea, pero es lo suficientemente bueno para mi propósito, verificando los archivos de configuración local en “Archivos de progtwig” para la capacidad de escritura:

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

OMI, debe trabajar con dichos directorios como siempre, pero en lugar de verificar los permisos antes de usarlos, proporcione la forma correcta de manejar la excepción de acceso no autorizado y reaccione en consecuencia. Este método es más fácil y mucho menos propenso a errores.

Intenta trabajar con este fragmento de C # que acabo de elaborar:

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

Y aquí hay una referencia que también podrías ver. Mi código podría darle una idea de cómo puede verificar los permisos antes de intentar escribir en un directorio.

según este enlace: http://www.authorcode.com/how-to-check-file-permission-to-write-in-c/

es más fácil usar el SecurityManager de clase existente

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

pero parece que se ha quedado obsoleto, se sugiere usar PermissionSet en su lugar.

 [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.")] 
 private static void GrantAccess(string file) { bool exists = System.IO.Directory.Exists(file); if (!exists) { DirectoryInfo di = System.IO.Directory.CreateDirectory(file); Console.WriteLine("The Folder is created Sucessfully"); } else { Console.WriteLine("The Folder already exists"); } DirectoryInfo dInfo = new DirectoryInfo(file); DirectorySecurity dSecurity = dInfo.GetAccessControl(); dSecurity.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), FileSystemRights.FullControl, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.NoPropagateInherit, AccessControlType.Allow)); dInfo.SetAccessControl(dSecurity); }