c# .net

c# - ¿Cómo verifica los permisos para escribir en un directorio o archivo?



.net (8)

Como esto no está cerrado, me gustaría enviar una nueva entrada para cualquiera que busque tener algo que funcione correctamente para ellos ... usando una fusión de lo que encontré aquí, así como el uso de DirectoryServices para depurar el código y encontrar el código apropiado para usar, esto es lo que encontré que funciona para mí en cada situación ... tenga en cuenta que mi solución amplía el objeto DirectoryInfo ...:

public static bool IsReadable(this DirectoryInfo me) { AuthorizationRuleCollection rules; WindowsIdentity identity; try { rules = me.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); identity = WindowsIdentity.GetCurrent(); } catch (Exception ex) { //Posible UnauthorizedAccessException return false; } bool isAllow=false; string userSID = identity.User.Value; foreach (FileSystemAccessRule rule in rules) { if (rule.IdentityReference.ToString() == userSID || identity.Groups.Contains(rule.IdentityReference)) { if ((rule.FileSystemRights.HasFlag(FileSystemRights.Read) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadAndExecute) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadData) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadExtendedAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadPermissions)) && rule.AccessControlType == AccessControlType.Deny) return false; else if ((rule.FileSystemRights.HasFlag(FileSystemRights.Read) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadAndExecute) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadData) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadExtendedAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadPermissions)) && rule.AccessControlType == AccessControlType.Allow) isAllow = true; } } return isAllow; } public static bool IsWriteable(this DirectoryInfo me) { AuthorizationRuleCollection rules; WindowsIdentity identity; try { rules = me.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); identity = WindowsIdentity.GetCurrent(); } catch (Exception ex) { //Posible UnauthorizedAccessException return false; } bool isAllow = false; string userSID = identity.User.Value; foreach (FileSystemAccessRule rule in rules) { if (rule.IdentityReference.ToString() == userSID || identity.Groups.Contains(rule.IdentityReference)) { if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) || rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) || rule.FileSystemRights.HasFlag(FileSystemRights.WriteExtendedAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) || rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Deny) return false; else if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) || rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) || rule.FileSystemRights.HasFlag(FileSystemRights.WriteExtendedAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) || rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Allow) isAllow = true; } } return me.IsReadable() && isAllow; }

Obtuve un programa que escribe algunos datos en un archivo usando un método como el siguiente.

public void ExportToFile(string filename) { using(FileStream fstream = new FileStream(filename,FileMode.Create)) using (TextWriter writer = new StreamWriter(fstream)) { // try catch block for write permissions writer.WriteLine(text); } }

Cuando ejecuto el programa obtengo un error:

Excepción no controlada: System.UnauthorizedAccessException: Acceso denegado a la ruta ''mypath''. en System.IO .__ Error.WinIOError (Código de error Int32, String maybeFullPath) en System.IO.FileStream.Init (Ruta de cadena, modo Modo de archivo, acceso de acceso de archivo, derechos nt32, uso de los derechos booleanos, recurso compartido de FileShare, tamaño de búfer de Int32, opciones de archivo, SECTIVIDAD_TRATOS SECATISTAS , String msgPath, Boolean bFromProxy) en System.IO.FileStream..ctor (ruta de cadena, modo de modo de archivo, acceso de FileAccess, compartir de FileShare, opciones de Int2 bufferSize, FileOptions, String msgPath, Boolea bFromProxy)

Pregunta: ¿Qué código necesito para detectar esto y cómo otorgo el acceso?


Cuando su código hace lo siguiente:

  1. Verifica que el usuario actual tenga permiso para hacer algo.
  2. Realiza la acción que necesita los derechos marcados en 1.

Corre el riesgo de que los permisos cambien entre 1 y 2 porque no puede predecir qué más sucederá en el sistema en tiempo de ejecución. Por lo tanto, su código debe manejar la situación en la que se produce una excepción de acceso no autorizada, incluso si ha verificado previamente los permisos.

Tenga en cuenta que la clase SecurityManager se usa para verificar los permisos de CAS y no verifica realmente con el sistema operativo si el usuario actual tiene acceso de escritura a la ubicación especificada (a través de ACL y ACE). Como tal, IsGranted siempre será verdadero para las aplicaciones que se ejecutan localmente.

Ejemplo (derivado del ejemplo de Josh ):

//1. Provide early notification that the user does not have permission to write. FileIOPermission writePermission = new FileIOPermission(FileIOPermissionAccess.Write, filename); if(!SecurityManager.IsGranted(writePermission)) { //No permission. //Either throw an exception so this can be handled by a calling function //or inform the user that they do not have permission to write to the folder and return. } //2. Attempt the action but handle permission changes. try { using (FileStream fstream = new FileStream(filename, FileMode.Create)) using (TextWriter writer = new StreamWriter(fstream)) { writer.WriteLine("sometext"); } } catch (UnauthorizedAccessException ex) { //No permission. //Either throw an exception so this can be handled by a calling function //or inform the user that they do not have permission to write to the folder and return. }

Es complicado y no se recomienda intentar calcular programáticamente los permisos efectivos de la carpeta en función de las ACL sin procesar (que son todas las que están disponibles a través de las clases System.Security.AccessControl ). Otras respuestas en y en la red más amplia recomiendan intentar llevar a cabo la acción para saber si se permite el permiso. This publicación resume lo que se requiere para implementar el cálculo de permisos y debería ser suficiente para evitar que lo haga.


Es una versión fija del Code de MaxOvrdrv.

public static bool IsReadable(this DirectoryInfo di) { AuthorizationRuleCollection rules; WindowsIdentity identity; try { rules = di.GetAccessControl().GetAccessRules(true, true, typeof(SecurityIdentifier)); identity = WindowsIdentity.GetCurrent(); } catch (UnauthorizedAccessException uae) { Debug.WriteLine(uae.ToString()); return false; } bool isAllow = false; string userSID = identity.User.Value; foreach (FileSystemAccessRule rule in rules) { if (rule.IdentityReference.ToString() == userSID || identity.Groups.Contains(rule.IdentityReference)) { if ((rule.FileSystemRights.HasFlag(FileSystemRights.Read) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.ReadData)) && rule.AccessControlType == AccessControlType.Deny) return false; else if ((rule.FileSystemRights.HasFlag(FileSystemRights.Read) && rule.FileSystemRights.HasFlag(FileSystemRights.ReadAttributes) && rule.FileSystemRights.HasFlag(FileSystemRights.ReadData)) && rule.AccessControlType == AccessControlType.Allow) isAllow = true; } } return isAllow; } public static bool IsWriteable(this DirectoryInfo me) { AuthorizationRuleCollection rules; WindowsIdentity identity; try { rules = me.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); identity = WindowsIdentity.GetCurrent(); } catch (UnauthorizedAccessException uae) { Debug.WriteLine(uae.ToString()); return false; } bool isAllow = false; string userSID = identity.User.Value; foreach (FileSystemAccessRule rule in rules) { if (rule.IdentityReference.ToString() == userSID || identity.Groups.Contains(rule.IdentityReference)) { if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) || rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) || rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) || rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) || rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Deny) return false; else if ((rule.FileSystemRights.HasFlag(FileSystemRights.Write) && rule.FileSystemRights.HasFlag(FileSystemRights.WriteAttributes) && rule.FileSystemRights.HasFlag(FileSystemRights.WriteData) && rule.FileSystemRights.HasFlag(FileSystemRights.CreateDirectories) && rule.FileSystemRights.HasFlag(FileSystemRights.CreateFiles)) && rule.AccessControlType == AccessControlType.Allow) isAllow = true; } } return isAllow; }


Lo siento, pero ninguna de las soluciones anteriores me ayudó. Necesito comprobar ambos lados: SecurityManager y SO permisos. He aprendido mucho con el código de Josh y con una respuesta iain, pero me temo que necesito usar el código de Rakesh (también gracias a él). Solo un error: descubrí que solo verifica los permisos Permitir y no Denegar. Entonces mi propuesta es:

string folder; AuthorizationRuleCollection rules; try { rules = Directory.GetAccessControl(folder) .GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount)); } catch(Exception ex) { //Posible UnauthorizedAccessException throw new Exception("No permission", ex); } var rulesCast = rules.Cast<FileSystemAccessRule>(); if(rulesCast.Any(rule => rule.AccessControlType == AccessControlType.Deny) || !rulesCast.Any(rule => rule.AccessControlType == AccessControlType.Allow)) throw new Exception("No permission"); //Here I have permission, ole!


Ninguno de estos funcionó para mí ... vuelven como verdaderos, incluso cuando no lo son. El problema es que debe probar el permiso disponible contra los derechos de usuario actuales del proceso, esto prueba los derechos de creación de archivos, simplemente cambie la cláusula FileSystemRights a ''Escribir'' para probar el acceso de escritura.

/// <summary> /// Test a directory for create file access permissions /// </summary> /// <param name="DirectoryPath">Full directory path</param> /// <returns>State [bool]</returns> public static bool DirectoryCanCreate(string DirectoryPath) { 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 ((FileSystemRights.CreateFiles & rule.FileSystemRights) == FileSystemRights.CreateFiles) { if (rule.AccessControlType == AccessControlType.Allow) return true; } } } } catch {} return false; }


Puede intentar seguir el bloque de código para verificar si el directorio tiene acceso de escritura.

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


Wow ... hay un montón de código de seguridad de bajo nivel en este hilo, la mayoría de los cuales tampoco funcionó para mí, aunque aprendí mucho en el proceso. Una cosa que aprendí es que la mayor parte de este código no está orientado a las aplicaciones que buscan derechos de acceso por usuario: es para los administradores que desean alterar los derechos mediante programación, lo que, como se ha señalado, no es bueno. Como desarrollador, no puedo utilizar la "salida fácil", ejecutando como administrador, que no soy uno en la máquina que ejecuta el código ni mis usuarios, por lo que son tan ingeniosas como estas soluciones. - No son para mi situación, y probablemente tampoco para la mayoría de los desarrolladores de bases.

Al igual que la mayoría de los carteles de este tipo de preguntas, inicialmente sentí que también era "hackey". Desde entonces, he decidido que está perfectamente bien intentarlo y dejar que la posible excepción indique exactamente cuáles son los derechos del usuario, porque la información que recibí no me decía cuáles eran realmente los derechos. El código a continuación: lo hizo.

Private Function CheckUserAccessLevel(folder As String) As Boolean Try Dim newDir As String = String.Format("{0}{1}{2}", folder, If(folder.EndsWith("/"), "", "/"), "LookWhatICanDo") Dim lookWhatICanDo = Directory.CreateDirectory(newDir) Directory.Delete(newDir) Return True Catch ex As Exception Return False End Try

Función final


ACTUALIZAR:

Modificó el código basado en esta respuesta para deshacerse de los métodos obsoletos.

Puede usar el espacio de nombres de Seguridad para verificar esto:

public void ExportToFile(string filename) { var permissionSet = new PermissionSet(PermissionState.None); var writePermission = new FileIOPermission(FileIOPermissionAccess.Write, filename); permissionSet.AddPermission(writePermission); if (permissionSet.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet)) { using (FileStream fstream = new FileStream(filename, FileMode.Create)) using (TextWriter writer = new StreamWriter(fstream)) { // try catch block for write permissions writer.WriteLine("sometext"); } } else { //perform some recovery action here } }

En cuanto a obtener esos permisos, tendrá que pedirle al usuario que lo haga de alguna manera. Si pudieras hacer esto programáticamente, entonces todos estaríamos en problemas;)