DBAuthorization – Parte 4 – Estrutura dos Tipos

No post anterior exibi a estrutura necessária para armazenamento das políticas no banco de dados. Da mesma forma, a aplicação também precisa definir alguns tipos (enumeradores, classes, etc.) para garantir que a programação/consumo desta API seja realizada de forma consistente.

Neste post não será discutido na íntegra cada uma das classes, pois isso será feito no momento específico e, além disso, há classes que foram criadas que são utilizadas como “helpers” e que não serão abordadas aqui. Vamos focar apenas nos tipos que representam a estrutura necessária para fazer o recurso funcionar.

Há dois enumeradores chamados DBAuthorizationRuleAction e DBAuthorizationRuleType. O primeiro fornece duas opções autoexplicativas: Deny e Allow. Já o segundo determina os tipos disponíveis, onde efetivamente a política será aplicada: Users, Roles ou HttpVerbs. A combinação destes dois valores ainda exigirá um valor adicional, que são os usuários, papéis ou verbos que poderão ou não acessar o recurso em questão.

Uma outra classe que tem o papel importante nesta funcionalidade é a DBAuthorizationRule. Esta classe representa uma política e, para isso, ela possui as seguintes propriedades (entre parênteses temos o tipo delas): Action (DBAuthorizationRuleAction), Type (DBAuthorizationRuleType), PathId (Guid), RuleId (Guid), Path (String) e Data (StringCollection). Além destas propriedades, ainda temos as propriedades Everyone (Boolean) e Anonymous (Boolean), que são somente leitura. A propriedade Everyone retorna um valor booleano indicando se o caractere “*” está definido na propriedade Data, enquanto a propriedade Anonymous também retorna um valor booleano indicando a existência do caractere “?”. Essas duas propriedades estão fortemente relacionadas ao tipo, ou seja, o caractere “?” somente faz sentido quando é usado em conjunto com o tipo DBAuthorizationRuleType.Users.

Para finalizar, ainda temos a classe DBAuthorizationRuleCollection que, como o próprio nome diz, é uma coleção de elementos do tipo DBAuthorizationRule. Para efeitos de boas práticas, esta classe herda diretamente da classe Collection<T>, apesar de não haver a necessidade de interceptar qualquer mudança na coleção. A imagem abaixo ilustra graficamente os tipos discutidos neste post

DBAuthorization – Parte 3 – Estrutura do Banco de Dados

Como falado no post anterior, vamos utilizar uma base de dados como repositório para as políticas de acesso aos arquivos ASP.NET. Vale lembrar que você pode utilizar qualquer outro tipo de repositório, como por exemplo, um arquivo Xml independente.

É importante dizer que a partir da versão 2.0 do ASP.NET, a Microsoft criou diversas APIs para gerenciamento de usuários (Membership), de papéis (Roles) e de perfis de usuários (Profile). Essas funcionalidades foram extensamente discutidas neste artigo e já assumirei que você já tenha o respectivo conhecimento.

As tabelas auxiliares necessárias serão colocadas no mesmo banco de dados que já possua a infraestrutura das funcionalidades do ASP.NET que vimos no artigo mencionado acima. Basicamente teremos duas tabelas adicionais, sendo: aspnet_Authorization_Paths e aspnet_Authorization_Rules. Como podemos ter diversas políticas para uma página específica, então a primeira tabela será responsável por armazenar paths (páginas ou diretórios) que você deseja aplicar as políticas. Além do path (que deve incluir o diretório virtual), essa tabela terá uma coluna chamada ApplicationId, que é a chave estrangeira da tabela aspnet_Applications (built-in), já que podemos ter um banco de dados do ASP.NET compartilhado para várias aplicações. A segunda tabela, aspnet_Authorization_Rules, armazenará quais são as políticas definidas para um determinado path e, com isso, além de ter uma coluna para relacionamento com a tabela de paths, temos também: Action, Type e Data. A coluna Action determina qual a ação a ser executada (Deny ou Allow); já a coluna Type determina em quem vamos aplicar a ação (Users, Roles ou Verbs) e, finalmente, a coluna Data armazenará o nome dos usuários, papéis ou verbos que farão parte da política. A imagem abaixo ilustra o relacionamento entre elas:

Verbs: Esses são os conhecidos verbos de HTTP, e que neste caso faremos o uso GET ou POST. A idéia é também permitir o acesso somente quando a requisição for realizada através de um deles.

Além das tabelas ainda temos as Stored Procedures. Essas Stored Procedures apenas definem as queries necessárias para efetuar a inserção, exclusão ou a leitura das informações referentes as políticas de acesso. Para seguir a mesma convenção estipulada pela Microsoft (não que você deva seguí-la), as Stored Procedures estão prefixadas com a palavra “aspnet_”. Para atender as tarefas que vamos desempenhar, criei quatro delas, a saber:

 

  • aspnet_Authorization_AddRule: Adiciona uma nova regra.
  • aspnet_Authorization_DeletePathById: Exclui o path e todas as regras relacionadas.
  • aspnet_Authorization_DeleteRuleById: Exclui apenas uma única regra.
  • aspnet_Authorization_ReadRules: Extrai todas as regras definidas para uma aplicação.

Para poupar espaço, não vou colocar o código referente a cada Stored Procedure aqui. No final da série publicarei o projeto todo, incluindo o backup do banco de dados que possui a estrutura necessária para fazer tudo isso funcionar.

DBAuthorization – Parte 2 – A possível solução

Para solucionar o problema descrito no post anterior, podemos utilizar o banco de dados como repositório das políticas de acesso às páginas ASP.NET. Com isso, qualquer mudança que seja necessária, você pode utilizar os comandos (queries) e classes tradicionais de acesso à dados para fazer tal manipulação.

Mover para o banco de dados nos permitirá criar uma interface amigável para que o responsável pelo sistema gerencie as políticas de acesso. Como um ponto negativo desta técnica, temos o excesso de round-trips que haverá durante a execução da aplicação. Como todas as páginas ASP.NET podem ou não estar protegidas, será necessário consultar a base de dados para certificar que há alguma política definida para esta página e, se houver, aplicá-la.

Como as políticas de acesso são comuns para qualquer usuário, ou melhor, são configurações globais da aplicação e serão aplicadas/avaliadas em todas as requisições, podemos utilizar alguma estratégia de caching para evitar o constante acesso à base de dados. Mas esta técnica exige um certo sincronismo entre a leitura e escrita e que será discutido no momento correto.