

| com exemplos em VB |
| Componente para deixar forms em Vb semelhantes às telas do winnamp |
| Componente para colocar sua aplicação VB no Systray |
| Componente para transformar sua aplicação VB em serviço |
| Ferramentas úteis para quem usa Olap Server |
| |

![]() |
||||||||
|
|
||||||||
Por Dennes
Torres dennes@bufaloinfo.com.brDennes Torres possui as certificações MCAD, MCSD,MCSE, MCDBA e MCT. Atualmente atua Como diretor da Búfalo Informática, líder do grupo de usuários DevASPNet no Rio de Janeiro e membro da liderança dos grupos getWindows e devSQL, também do Rio de Janeiro, podendo sempre ser encontrado na lista de discussão do grupo DevASPNet (devaspnet-subscribe@yahoogrupos.com.br) bem como nas reuniões do grupo. Mantém dois blogs em http://cidadaocarioca.blogspot.com e http://br.thespoke.net/blogs/dennes/default.aspx |
|
|
|
|
| Manipulando ACLs com o Framework .NET | |
|
|
|
Uma das novidades que o framework 2.0 traz em relação a segurança são novas classes que nos permitem manipular ACLs - Access Control List - através da própria class library do framework.
Um exemplo de ACL são as listas de permissão nas pastas e arquivos. Através do framework podemos agora manipular estas permissões, tanto consulta-las como altera-las.
Para demonstrar a manipulação das ACLs no framework, vamos criar uma aplicação windows simples.
Primeiro Passo : Montagem do formulário
Vamos no formulário inserir um label e uma listView. O label mostrará a pasta que estamos consultado e o listView mostrará as permissões existentes nesta pasta.
Precisaremos também de um FolderBrowserDialog, uma caixa de diálogo para perguntar ao usuário de qual pasta ele deseja visualizar as permissões. Antigamente tinhamos que disparar esse diálogo via API, agora temos um objeto para fazer isso, fica na toolbox.
Vamos chamar o label de lblPasta e o FolderBrowserDialog de FBD.

Precisaremos também configurar as coluna da listView para que sejam exibidas corretamente. Fazemos isso através da propriedade columns. As 4 colunas se chamarão "Usuário", "Herdado", "Permissão" e "Tipo".
Segundo Passo : Perguntando ao usuário qual pasta deseja visualizar
A aplicação será simples, então vamos fazer isso diretamente no load do formulário. Basta utilizar o FBD, ele fará a pergunta ao usuário e nos trará a resposta.
46 If FBD.ShowDialog = Windows.Forms.DialogResult.OK Then
47
48 lblPasta.Text = FBD.SelectedPath
49 exibirACLs()
50 Else
51 Me.Close()
52 End If
Observe que neste trecho de código fazemos uma chamada a função "exibirACLs", que irá realizar o passo seguinte, exibir a ACL da pasta.

Terceiro Passo : Exibir a ACL
Precisaremos realizar 3 imports para fazer este 3o passo :
1 Imports System.IO
2 Imports System.Security.AccessControl
3 Imports System.Security.Principal
O primeiro contem a classe DirectoryInfo, para manipularmos pastas no disco, o 2o as classes para nos permitir acessar a ACL e o 3o classes com a identificação do usuário, afinal a ACL é uma lista de quem tem ou não acesso a um recurso, ou seja, uma lista de usuários.
Nossa função, exibirAcls, irá utilizar alguns objetos que serão também necessários quando formos realizar a alteração da ACL. Dois deles são DirectoryInfo e DirectorySecurity. DirectoryInfo nos traz informações sobre uma pasta enquanto que DirectorySecurity é obtida a partir da DirectoryInfo, trazendo-nos a ACL da pasta.
Para fazermos uma consulta - exibirmos a ACL - precisamos obter a coleção de regras, permissões atribuidas. Essa coleção é do tipo AuthorizationRuleCollection. Essa coleção é o que exibiremos na listView e precisaremos dela em outros momentos, então será outra variável a ser definida a nível de form.
Veja como ficam as variáveis a nível de form :
8 Dim colecao As AuthorizationRuleCollection
9 Dim di As DirectoryInfo
10 Dim acl As DirectorySecurity
Dentro da função vamos primeiramente criar as instâncias de DI e ACl, veja :
63 Public Sub exibirACLs()
64 If di Is Nothing Then
65 di = New DirectoryInfo(lblPasta.Text)
66
67 acl = di.GetAccessControl
68 End If
69
70 ListView1.Items.Clear()
Só precisamos fazer isso uma vez, depois as instâncias serão mantidas a nível de form. Observe que também tomei o cuidado de limpar a listView, desta forma a função exibirACLs terá o efeito de um refresh.
Dando continuidade a criação da função, vamos preencher a variável coleção :
72 colecao = acl.GetAccessRules(True, True, GetType(NTAccount))
Os dois primeiros parâmetros são uma filtragem sobre o que será incluido, as permissões explicitamente definidas para o objeto ou as permissões herdadas em uma hierarquia. No nosso caso, solicitei os dois.
O 3o parâmetro trata da identidade do usuário. Os objetos dentro desta coleção (AuthorizationRuleCollection) possuem uma propriedade chamada IdentityReference. Ocorre que como as ACLs podem ser aplicadas em inúmeros locais diferentes, esta propriedade também pode devolver objetos de tipos diferentes, diferentes formas de identificar o usuário.
Assim sendo o método GetAccessRules nos permite especificar de que tipo desejamos que sejam devolvidas as identidades do usuário. Para ACLs do sistema o melhor é o NTAccount.
Após termos obtido a coleção, precisamos percorre-la para preencher a listview. Assim sendo precisamos fazer um for/each.
Oficialmente o tipo de dados devolvido pela AuthorizationRuleCollection é uma classe AuthorizationRule. Mas AuthorizationRule é na verdade uma classe que serve como base de diversas outras, mais específicas. Uma delas é a FileSystemAccessRule que utilizaremos no nosso caso, já que estamos trabalhando com permissões de pastas no disco. Veja como fica o laço :
63 For Each si As FileSystemAccessRule In colecao
64
65 Next
Vamos então criar um novo listViewItem e acrescentar o item na listView, veja como fica :
79 For Each si As FileSystemAccessRule In colecao
80
81 Dim li As New ListViewItem
82 li.Text = si.IdentityReference.Value
83 li.SubItems.Add(si.IsInherited.ToString)
84 li.SubItems.Add(si.FileSystemRights.ToString)
85 li.SubItems.Add(si.AccessControlType.ToString)
86 ListView1.Items.Add(li)
87
88 Next
Veja o significado de cada item :
IdentityReference.Value : O nome do usuário. Mas lembre-se que pedimos a classe como NTAccount, esse valor pode variar.
IsInherited : Se a permissão é herdada ou não
FileSystemRights : As permissões que foram atribuidas
AccessControlType : O tipo de atribuição, Allow ou Deny
Veja então a função completa :
68 Public Sub exibirACLs()
69 If di Is Nothing Then
70 di = New DirectoryInfo(lblPasta.Text)
71
72 acl = di.GetAccessControl
73 End If
74
75 ListView1.Items.Clear()
76
77 colecao = acl.GetAccessRules(True, True, GetType(NTAccount))
78
79 For Each si As FileSystemAccessRule In colecao
80
81 Dim li As New ListViewItem
82 li.Text = si.IdentityReference.Value
83 li.SubItems.Add(si.IsInherited.ToString)
84 li.SubItems.Add(si.FileSystemRights.ToString)
85 li.SubItems.Add(si.AccessControlType.ToString)
86 ListView1.Items.Add(li)
87
88 Next
89 End Sub
Neste ponto já podemos executar a aplicação e veremos a lista de permissões de uma pasta.

Quarto passo : Criar um formulário para a exibição de detalhes
Cada item de permissão que exibimos na listView pode na verdade se referir a várias permissões atribuidas a um mesmo usuário. Para permitir que o usuário altere as permissões vamos criar um 2o form para a exibição dos detalhes das permissões.
Neste 2o form vamos inserir um DataGridView para exibir as permissões. Precisaremos também de um botão para realizar a gravação das alterações nas permissões.

Precisamos ainda configurar as 3 colunas da DataGridView para garantir que sejam exibidas corretamente. Faremos isso através da propriedade Columns da DataGridView.
Quinto passo : Codificar a exibição do 2o formulário
Vamos fazer com que o formulário de detalhes seja exibido a partir do duplo clique na listview. O usuário estará clicando em uma FileSystemAccessRule e o objetivo do formulário de detalhes é mostrar detalhadamente as permissões contidas na FileSystemAccessRule e permitir a alteração delas.
Para isso precisaremos transmitir para o 2o form o objeto FileSystemAccessRule no qual o usuário deu duplo clique.
A alteração da ACL não pode ser feita apenas com a FileSystemAccessRule. Então vamos fazer com que o 2o formulário possa disparar a realização da alteração pelo 1o. Para fazer isso vamos utilizar delegates. O form 1 transmite para o form 2 o ponteiro para uma sub que seja responsável pela atualização.
Para que isso seja possível precisamos fazer algumas definições no 2o form, a nível de form. Veja como fica :
13 Public far As FileSystemAccessRule
14 Public Delegate Sub alteracao(ByVal Permissao As FileSystemAccessRule, ByVal Negacao As FileSystemAccessRule)
15
16
17 Public Alterar As alteracao
As variáveis FAR e ALTERAR precisarão ser preenchidas no momento da chamada ao form2. Veja como fica :
99 Private Sub ListView1_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListView1.DoubleClick
100
101
102 Dim f As New Form2
103 f.far = colecao(ListView1.SelectedItems(0).Index)
104 f.Alterar = AddressOf GravarAtualizacao
105 f.Show()
106 End Sub
Muito simples : Criamos uma instância do form, preenchemos os dois parâmetros e só depois chamamos o método show para exibição do formulário. Na variável do tipo delegate passamos o endereço de uma sub que o form 2 deverá disparar para completar a alteração da ACL.
Sexto passo : Criação de uma classe para exibição no dataGridView
Para exibir os dados no dataGridView vamos criar uma classe personalizada que represente uma permissão específica a qual o usuário tem permissão ou não. Assim poderemos fazer uma coleção desta classe e vincula-la na dataGridView.
110 Public Class ItemSeguranca
111 Dim ballow As Boolean
112 Dim bDeny As Boolean
113 Dim sPermissao As String
114
115
116 Public Sub New(ByVal Direito As String, ByVal Permite As Boolean, ByVal nega As Boolean)
117
118
119 sPermissao = Direito
120 Allow = Permite
121 Deny = nega
122 End Sub
123
124 Public ReadOnly Property ValorPermissao() As FileSystemRights
125 Get
126 Return ([Enum].Parse(GetType(FileSystemRights), sPermissao))
127
128
129 End Get
130 End Property
131
132
133 Public Property Deny() As Boolean
134 Get
135 Return (bDeny)
136 End Get
137 Set(ByVal value As Boolean)
138 bDeny = value
139 End Set
140 End Property
141
142 Public Property Allow() As Boolean
143 Get
144 Return (ballow)
145 End Get
146 Set(ByVal value As Boolean)
147 ballow = value
148 End Set
149 End Property
150 End Class
Nossa classe, ItemSeguranca, tem exatamente 3 propriedades, as 3 que desejaremos exibir na grid : A permissão e duas propriedades boolean, indicando a atribuição de Allow ou Deny para o usuário.
Sétimo Paso : Criação da coleção a ser exibida
Precisamos agora fazer uma coleção que possa receber elementos do tipo ItemSegurança. Vamos aproveitar os recursos de Generics e criar essa coleção a nível de formulário :
8 Dim ar As New Generic.List(Of ItemSeguranca)
Vamos então preencher essa coleção. A intenção é que essa coleção tenha todas as permissões que o usuário pode vir a receber, marcadas como true ou false.
No nosso caso, as permissões que o usuário pode vir a receber estão em um Enum em System.Security.AccessControl.FileSystemRights . Então podemos obter o nome dos elementos deste enum em um array e utiliza-lo para gerar a coleção.
Normalmente utilizariamos o método GetNames para obter os nomes dos elementos de um Enum, mas o enum FileSystemRights possui uma caracteristica especial. Este Enum possui vários elementos com nomes diferentes e mesmos valores. Isso porque algumas permissões são tão interligadas entre si que atribuindo uma, a outra é atribuida também. Pode-se também interpretar isso como nomes diferentes para a mesma permissão.
Cabe então a nossa aplicação cuidar em código das implicações disso. Por exemplo, Se exibirmos as duas, então quando uma for marcada a outra deve ser também. Mas quando formos gravar alterações, se somarmos o valor das duas estaremos gerando um resultado totalmente diferente, ambas precisam ser tratadas como uma só.
Para simplificar, em nosso exemplo vamos excluir as permissões com valores duplicados. Para fazer isso vamos criar uma função chamada ListaNomes. Veja como fica :
112 Private Function ListaNomes() As ArrayList
113 Dim ar As New ArrayList
114 Dim valores As FileSystemRights()
115 valores = [Enum].GetValues(GetType(System.Security.AccessControl.FileSystemRights))
116
117
118
119 For Each s As FileSystemRights In valores
120
121
122 If ar.IndexOf(s.ToString) = -1 Then
123 ar.Add(s.ToString)
124 End If
125 Next
126
127 Return (ar)
128 End Function
Utilizamos o GetValues para obter um array de valores do enum e percorremos o array de valores, adicionando-os em um arrayList. O truque é que antes de adiciona-los no arrayList testamos para ver se já existem ou não, eliminando assim os valores duplicados.
Voltando ao Load do formulário, vamos chamar esta função para obter a lista de nomes do Enum e fazer um laço através desta lista :
111 Dim nomes As ArrayList
112 nomes = ListaNomes()
113 For Each s As String In nomes
114
115 Next
O laço está sendo feito no arrayList de strings com os nomes das permissões. A partir desta string "s" precisamos recuperar de volta o valor deste elemento do enum e verificar se está contido na FileSystemAccessRule que este formulário recebeu.
113 For Each s As String In nomes
114 Dim r As FileSystemRights
115 r = [Enum].Parse(GetType(FileSystemRights), s)
116
117
118 If (far.FileSystemRights And r) = r Then
119
120 Else
121
122 End If
123 Next
Observe a variávei r definida como FileSystemRights, para recuperarmos o valor do Enum e o IF utilizando um AND para descobrir se a permissão R está ou não entre as permissões atribuidas ao usuário.
O If determinará a forma como criaremos a instância da classe ItemSeguranca. Observe que esta classe tem dois booleans, Allow e Deny, que são definidos no construtor. Um deles estará marcado caso a permissão r esteja contida nas permissões do usuário. Caso não (else) , ambos estarão como false.
113 For Each s As String In nomes
114 Dim r As FileSystemRights
115 r = [Enum].Parse(GetType(FileSystemRights), s)
116
117
118 If (far.FileSystemRights And r) = r Then
119
120 Else
121 ar.Add(New ItemSeguranca(s, False, False))
122
123
124 End If
125 Next
No caso do r estar contido nas permissões do usuário, então precisamos descobrir se definiremos Allow como true ou Deny como true. Isso depende das permissões do usuário que foram passadas para este formulário, se são uma atribuição (Allow) ou uma negação (Deny).
113 For Each s As String In nomes
114 Dim r As FileSystemRights
115 r = [Enum].Parse(GetType(FileSystemRights), s)
116
117
118 If (far.FileSystemRights And r) = r Then
119
120
121 If far.AccessControlType = AccessControlType.Allow Then
122
123
124 ar.Add(New ItemSeguranca(s, True, False))
125
126 Else
127 ar.Add(New ItemSeguranca(s, False, True))
128
129 End If
130 Else
131 ar.Add(New ItemSeguranca(s, False, False))
132
133 End If
134 Next
Após o laço temos nossa coleção montada, então podemos vincula-la na dataGridView. Veja como fica a função completa :
158 Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
159
160
161 Dim nomes As ArrayList
162 nomes = ListaNomes()
163
164 For Each s As String In nomes
165 Dim r As FileSystemRights
166 r = [Enum].Parse(GetType(FileSystemRights), s)
167
168
169 If (far.FileSystemRights And r) = r Then
170
171
172 If far.AccessControlType = AccessControlType.Allow Then
173
174
175 ar.Add(New ItemSeguranca(s, True, False))
176
177 Else
178 ar.Add(New ItemSeguranca(s, False, True))
179
180 End If
181 Else
182 ar.Add(New ItemSeguranca(s, False, False))
183
184 End If
185 Next
186 DataGridView1.DataSource = ar
187 End Sub
Neste ponto o formulário já pode ser exibido. Rodando a aplicação e dando um duplo clique na listview veremos os detalhes do item em questão
Oitavo Passo : Criando a gravação
Agora que fizemos a exibição, vamos criar uma gravação dos dados. Alterar, o usuário já pode alterar normalmente, a DataGridView permite, mas precisamos criar um botão para gravação, para que os dados alterados na DataGridView sejam persistidos
Na DataGridView fizemos a exibição de instâncias da classe ItemSeguranca, são nessas instâncias que o usuário realiza suas alterações.
A alteração de dados, porém, precisa de classes FileSystemAccessRule para ser realizada. Então precisaremos de um pequeno algorítimo para transformar os dados alterados pelo usuário novamente em instâncias da classe FileSystemAccessRule.
Mas não iremos gerar uma única instância, mas sim 2. A classe FileSystemAccessRule possui um tipo de permissão específica, ou de atribuição (Allow) ou de negação (Deny). Na nossa DataGridView exibimos duas colunas, uma de negação outra de atribuição. Assim sendo o conteúdo de nossa DataGridView precisará ser transformado em 2 instâncias da classe FileSystemAccessRule.
O ponto principal é que precisamos fazer a montagem de 2 valores do tipo FileSystemRights, um de atribuição outro de negação, conforme o que o usuário houver marcado na DataGridView.
Veja como fica essa montagem :
190 Private Sub cmdGravar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdGravar.Click
191
192
193 Dim r As FileSystemRights
194 Dim r2 As FileSystemRights
195
196 For Each IP As ItemSeguranca In ar
197 If IP.Allow Then
198 If (r And IP.ValorPermissao) <> IP.ValorPermissao Then
199
200
201 r += IP.ValorPermissao
202 End If
203
204 ElseIf IP.Deny Then
205 If (r2 And IP.ValorPermissao) <> IP.ValorPermissao Then
206
207
208 r2 += IP.ValorPermissao
209 End If
210 End If
211 Next
212
213 End Sub
Definimos duas variáveis, r e r2, para conter nossas montagens dos valores de FileSystemRights.
Realizamos então um laço através da coleção que preencheu a DataGridView. Sempre que desejamos fazer algorítimos assim devem ser feitos sobre a coleção que serviu de origem de dados para a DataGridView. Não se preocupe com as alterações do usuário, a DataGridView tomou o cuidado de persisti-las nesta coleção automaticamente.
Para cada item, se existir permissão "Allow", somamos em r, se existir permissão "Deny" somamos em r2. Não considerei a existência das duas juntas, não faria sentido, fica faltando um tratamento na interface para isso.
Observe que as somas estão cercadas com um IF, testando se por acaso o valor que iremos somar já está contido dentro da variável. Se estivesse o resultado desta operação seria o próprio valor que estamos somando, caso contrário será algo diferente.
Esse IF poderia parecer um pouco redundante, afinal que sentido tem testar se o que ainda não somamos já está incluido na soma ? Mas para compreender este IF é necessário lembrar que estamos trabalhando com Enums, flags que podem ser combinados.
A questão é que a combinação de várias permissões mais simples podem resultar em uma permissão maior. Por exemplo, Read é uma permissão resultante da combinação de várias permissões menores. Se somassemos os valores das várias permissões menores e depois somassemos novamente o valor da permissão maior obteríamos um resultado totalmente diferente. Desta forma os IFs estrategicamente colocados são essenciais para a geração do resultado.
Observe também que poderiamos melhorar a interface, criando marcações em conjunto. Por exemplo, quando a permissão maior for marcada as menores também são, e vice-versa.
O próximo passo é a partir dos valores gerados criar duas instâncias da classe FileSystemAccessRule para realizarmos a atualização na pasta. Veja como fica :
213 Dim novaregra As FileSystemAccessRule = Nothing
214 Dim novaregra2 As FileSystemAccessRule = Nothing
215
216 If r <> 0 Then
217 novaregra = New FileSystemAccessRule(far.IdentityReference, r, InheritanceFlags.ContainerInherit Or InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow)
218
219
220 End If
221 If r2 <> 0 Then
222 novaregra2 = New FileSystemAccessRule(far.IdentityReference, r2, InheritanceFlags.ContainerInherit Or InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Deny)
223
224
225 End If
Observe que a criação das instâncias está cercada por IFs,
Veja o significado dos parâmetros :
1- A identidade do usuário
2- As permissões atribuidas ao usuário
3- Os objetos que as permissões afetarão, pastas arquivos que estiverem dentro da pasta sendo permissionada.
4- A forma de propagação para outros objetos na árvore de objetos.
5- O tipo de controle de acesso, Allow ou Deny.
Com isso encerra-se a responsabilidade do form 2, quem vai realmente realizar a alteração dos parâmetros é o form 1. O que o form2 precisa fazer é disparar o delegate para que a alteração seja realizada no form1. Veja como fica a rotina completa do botão de gravação :
190 Private Sub cmdGravar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdGravar.Click
191
192
193 Dim r As FileSystemRights
194 Dim r2 As FileSystemRights
195
196 For Each IP As ItemSeguranca In ar
197 If IP.Allow Then
198 If (r And IP.ValorPermissao) <> IP.ValorPermissao Then
199
200
201 r += IP.ValorPermissao
202 End If
203
204 ElseIf IP.Deny Then
205 If (r2 And IP.ValorPermissao) <> IP.ValorPermissao Then
206
207
208 r2 += IP.ValorPermissao
209 End If
210 End If
211 Next
212
213 Dim novaregra As FileSystemAccessRule = Nothing
214 Dim novaregra2 As FileSystemAccessRule = Nothing
215
216 If r <> 0 Then
217 novaregra = New FileSystemAccessRule(far.IdentityReference, r, InheritanceFlags.ContainerInherit Or InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow)
218
219
220 End If
221 If r2 <> 0 Then
222 novaregra2 = New FileSystemAccessRule(far.IdentityReference, r2, InheritanceFlags.ContainerInherit Or InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Deny)
223
224
225 End If
226
227 Alterar(novaregra, novaregra2)
228
229 End Sub
Agora fica mais claro o conjunto de parâmetros que inserimos no Delegate, dois parâmetros do tipo FileSystemAccessRule, um de aceitação e outro de negação, mas na verdade a ordem dois dois não importará absolutamente nada para o processamento.
Para completar, vejamos como fica a sub GravarAlteracoes no form 1 :
231 Public Sub GravarAtualizacao(ByVal Permissao As FileSystemAccessRule, ByVal Negacao As FileSystemAccessRule)
232
233
234 If Not Permissao Is Nothing Then
235 acl.SetAccessRule(Permissao)
236 End If
237 If Not Negacao Is Nothing Then
238 acl.SetAccessRule(Negacao)
239 End If
240
241 di.SetAccessControl(acl)
242
243 exibirACLs()
244 End Sub
Observe os IFs cercando as instruções SetAccessRule. Qualquer dos dois parâmetros poderia estar em branco, Nothing. Os IFs são uma segurança contra isso. Observe que o SetAccessRule é aplicado sobre a classe DirectorySecurity, enquanto que o SetAccessControl é aplicado sobre a classe DirectoryInfo.
Com esta aplicação temos então um exemplo de manipulação da ACL com o sistema de arquivos. Observe os seguintes pontos :
1) Fizemos uma exibição detalhada das permissões, mas poderiamos ter feito diferente. Observe o sistema operacional, temos nele 2 exibições, básica e avançada. O que construimos se assemelha a opção avançada, na opção básica as permissões são agregadas em itens mais simples, mais compreensíveis ao usuário, é necessário um algorítimo para essa agregação.
2) Não lidei com a parte de propagação das permissões, utilizei valores fixos. É possível também manipular a auditoria.
3) Não inseri na aplicação a possibilidade de adicionar permissões a outros usuá