2008-09-24 10 views
52

Obtuve un programa que escribe algunos datos en un archivo usando un método como el siguiente.¿Cómo verifica los permisos para escribir en un directorio o archivo?


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 se ejecuta el programa me sale un error:

Unhandled Exception: System.UnauthorizedAccessException: Access to the path 'mypath' is denied. at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, nt32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions ptions, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolea bFromProxy)

Pregunta: ¿Qué código Qué necesito para coger esto y cómo puedo permitir el acceso?

Respuesta

60

ACTUALIZACIÓN:

modificado el código basado en this answer para deshacerse de los métodos obsoletos.

Usted puede utilizar el espacio de nombres de Seguridad para comprobar 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 conseguir los permisos, se le va a tener que pedir al usuario que hacer eso para usted de alguna manera. Si se pudiera hacer programáticamente esto, entonces todos estaríamos en problemas;)

+5

Esta solución no funciona, SecurityManager.IsGranted devuelve verdadero a pesar de que no tengo permiso de escritura en el archivo (Win XP SP3) –

+1

¿Está ejecutando como administrador local o como un usuario menos privilegiado que no tiene acceso? – Josh

+2

SecurityManager.IsGranted se atribuye como Obsoleto. Esta respuesta podría no funcionar para siempre. – MarkPflug

29

Cuando el código hace lo siguiente:

  1. cheques el usuario actual tiene permiso para hacer algo.
  2. lleva a cabo la acción que necesita los derechos comprobados en 1.

Usted corre el riesgo de que los permisos cambian entre 1 y 2 porque no se puede predecir qué otra cosa va a suceder en el sistema en tiempo de ejecución. Por lo tanto, su código debe manejar la situación en la que se genera UnauthorisedAccessException aunque haya verificado previamente los permisos.

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

Ejemplo (derivado de Josh's example):

//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 recomienda para tratar de calcular programáticamente los permisos efectivos de la carpeta basado en las ACL primas (que son todos los que están disponibles a través de las clases System.Security.AccessControl). Otras respuestas en Stack Overflow y en la red más amplia recomiendan intentar llevar a cabo la acción para saber si se permite el permiso. This post resume lo que se necesita para implementar el cálculo del permiso y debería ser suficiente para evitar que lo haga.

+2

Esta filosofía está muy bien, pero no funciona universalmente. Por ejemplo, necesito mostrar un cuadro de diálogo donde el usuario selecciona una ruta a la carpeta en la que quiero escribir, y quiero completar el diálogo con un valor inicial razonable como sugerencia, con la carpeta de destino siendo creada si no lo hace existo Por razones de compatibilidad con versiones anteriores, esa ruta predeterminada es "C: \ ". Si el usuario no puede escribir allí, quiero volver a un valor predeterminado en AppData. No hay forma de que quiera intentar y crear una carpeta, luego eliminarla de nuevo, solo para determinar el valor inicial para un diálogo de búsqueda de carpeta. –

+0

@RogerSanders - buen punto. – Iain

2

Puede intentar el siguiente bloque de código para comprobar 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     
      } 
+0

No funciona. Devuelve verdadero para C: \\ pero obtengo una excepción cuando intento crear cualquier archivo en C: \\. – Pedro77

6

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. Así que 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! 
+2

lol, el "ole!" : D –

3

Ninguno de ellos trabajó para mí .. regresan como verdadero, aun cuando no lo son. El problema es, usted tiene que probar el permiso disponibles contra los derechos de los usuarios actuales del proceso, estas pruebas para los derechos de creación de archivos, basta con cambiar la cláusula FileSystemRights a 'escritura' para acceder prueba 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; 
} 
+0

esta es de lejos la mejor respuesta ... sin embargo, debe modificar su código para tener en cuenta que Deny tiene prioridad sobre permitir usando 2 booleanos ... – MaxOvrdrv

+0

Devuelve verdadero para C: \\ pero obtengo un excepción cuando trato de crear cualquier archivo en C: \\ – Pedro77

4

Dado que esto no es 't closed, me gustaría enviar una nueva entrada para cualquier persona que busque tener algo que funcione correctamente para ellos ... utilizando una fusión de lo que he encontrado aquí, así como el uso de DirectoryServices para depurar el código en sí y encontrar el código adecuado para uso, esto es lo que encontré que me funciona en todas las situaciones ... 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; 
    } 
+1

Me di cuenta de dos posibles mejoras (IMO). Primero, verifique la existencia del directorio (me.Exists) al inicio de IsReadable. Segundo, dentro de IsWritable, mueva el control de IsReadable a la parte superior. También tenga en cuenta que algunas situaciones permiten escribir archivos pero no leerlos, por lo que algunas personas querrían mantener los dos métodos "desvinculados". –

+1

Además, creo que funcionará para combinar las enumeraciones fuera de la llamada al método y hacerlas estáticas: public static FileSystemRights _ReadRights = FileSystemRights.Read | FileSystemRights.ReadAndExecute | FileSystemRights.ReadAttributes | FileSystemRights.ReadData | FileSystemRights.ReadExtendedAttributes | FileSystemRights.ReadPermissions; Luego simplifique sus llamadas de prueba: if (rule.FileSystemRights.HasFlag (_ReadRights) && rule.AccessControlType == AccessControlType.Deny) –

+0

Tampoco funciona. ¿Sin solución? – Pedro77

5

Es una versión fija de MaxOvrdrv's Code.

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

Esto realmente funcionó, ¡para mí! ¡Gracias! – Smartis

0

Wow ... hay una gran cantidad de código de seguridad de bajo nivel en este tema - la mayoría de los cuales no funcionaba para mí, ya sea - aunque he aprendido mucho en el proceso. Una cosa que aprendí es que la mayor parte de este código no está orientado a aplicaciones que buscan derechos de acceso de usuario: es para Administradores que desean alterar los derechos mediante programación, lo cual, como se ha señalado, es no algo 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, al principio también sentí que era "hackey". Desde entonces, he decidido que está bien intentarlo y permitir que la posible excepción indique exactamente los derechos del usuario son - porque la información que obtuve no me dijo 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 

End Function

Cuestiones relacionadas