Como você verifica as permissions para gravar em um diretório ou arquivo?

Eu tenho um programa que grava alguns dados em um arquivo usando um método como o abaixo.

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

Ao executar o programa, recebo um erro:

Exceção não tratada: System.UnauthorizedAccessException: access ao caminho ‘mypath’ é negado. em System.IO .__ Error.WinIOError (Int32 errorCode, String talvezFullPath) em System.IO.FileStream.Init (caminho String, modo FileMode, access FileAccess, direitos nt32, Boolean useRights, compartilhamento FileShare, Int32 bufferSize, opções FileOptions, SECURITY_ATTRIBUTES secAttrs , String msgPath, Boolean bFromProxy) em System.IO.FileStream..ctor (caminho String, modo FileMode, FileAccess acessa compartilhamento FileShare, Int32 bufferSize, opções FileOptions, String msgPath, Boolea bFromProxy)

Pergunta: Qual código eu preciso para capturar isso e como eu concedo o access?

ATUALIZAR:

Modificou o código com base nesta resposta para se livrar de methods obsoletos.

Você pode usar o namespace Security para verificar isso:

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

No que diz respeito a obter essa permissão, você terá que pedir ao usuário para fazer isso para você de alguma forma. Se você pudesse programaticamente fazer isso, todos nós estaríamos em apuros;)

Quando seu código faz o seguinte:

  1. Verifica se o usuário atual tem permissão para fazer alguma coisa.
  2. Realiza a ação que precisa dos direitos verificados em 1.

Você corre o risco de que as permissions mudem entre 1 e 2 porque você não pode prever o que mais estará acontecendo no sistema durante a execução. Portanto, seu código deve lidar com a situação em que uma UnauthorisedAccessException é lançada mesmo se você tiver permissions marcadas anteriormente.

Observe que a class SecurityManager é usada para verificar as permissions do CAS e, na verdade, não verifica com o SO se o usuário atual tem access de gravação ao local especificado (por meio de ACLs e ACEs). Assim, o IsGranted sempre retornará true para aplicativos em execução localmente.

Exemplo (derivado do exemplo 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. } 

É complicado e não recomendável tentar calcular programaticamente as permissions efetivas da pasta com base nas ACLs brutas (que são todas disponíveis através das classs System.Security.AccessControl ). Outras respostas no Stack Overflow e na Web mais ampla recomendam tentar realizar a ação para saber se a permissão é permitida. Este post resume o que é necessário para implementar o cálculo de permissão e deve ser o suficiente para evitar que você faça isso.

É uma versão fixa do código do 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; } 

Desculpe, mas nenhuma das soluções anteriores me ajudou. Eu preciso verificar os dois lados: SecurityManager e permissions SO. Eu aprendi muito com o código Josh e com a resposta do iain, mas eu tenho medo de usar o código Rakesh (também graças a ele). Apenas um bug: descobri que ele só verifica as permissions Permitir e não Negar. Então minha proposta é:

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

Como isso não está fechado, gostaria de enviar uma nova input para qualquer pessoa que queira ter algo funcionando corretamente para eles … usando uma fusão do que eu encontrei aqui, bem como usando DirectoryServices para depurar o próprio código e encontrar o código apropriado para usar, aqui está o que eu acho que funciona para mim em todas as situações … note que a minha solução estende o object 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; } 

Nenhum desses funcionou para mim … eles retornam como verdadeiros, mesmo quando não são. O problema é que você tem que testar a permissão disponível contra os direitos de usuário do processo atual, isso testa os direitos de criação de arquivos, basta alterar a cláusula FileSystemRights para ‘Write’ para testar o access de gravação.

 ///  /// Test a directory for create file access permissions ///  /// Full directory path /// State [bool] 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; } 

Você pode tentar seguir o bloco de código para verificar se o diretório está tendo access de gravação.

Ele verifica o 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 } 

Uau … há um monte de código de segurança de baixo nível neste segmento – a maioria dos quais não funcionou para mim também – embora eu tenha aprendido muito no processo. Uma coisa que aprendi é que a maior parte deste código não é voltada para aplicativos que buscam direitos de access por usuário – é para os Administradores que desejam alterar os direitos programaticamente, o que – como foi apontado – não é uma coisa boa. Como desenvolvedor, eu não posso usar o “caminho mais fácil” – executando como Administrador – que – eu não sou um na máquina que executa o código, nem são meus usuários – então, tão inteligente quanto essas soluções são – eles não são para a minha situação, e provavelmente não para a maioria dos desenvolvedores de rank e arquivo.

Como a maioria dos pôsteres desse tipo de pergunta – eu inicialmente achei que era “hackey” também – eu decidi que está tudo bem em tentar e deixar a possível exceção dizer exatamente quais são os direitos do usuário – porque as informações que recebi não me disseram quais eram realmente os direitos. O código abaixo – fez.

  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 

Função final