quarta-feira, janeiro 03, 2007

Magoo, uma pequena peça para uma grande solução

Começo esse post com um pequeno fragmento de código e peço que pensem no que ele pode fazer:

addPropertyChangeListener(property, new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
//codigo maravilhoso
}
);


Pense.

Pense.

Pense mais um pouco.

Descobriu?

Não?

Pois é. Esse pequeno listener encerra uma solução que me incomodava há tempos: como implementar o mecanismo de permissões não intrusivo do Magoo. Magoo? O que é o Magoo?

O Magoo
O Magoo é um mecanismo de gerência de permissões não intrusivo para aplicações Java web e desktop. Idealizei ele há uns dois anos e está em banho-maria durante esse tempo porque o Merlin sempre está à frente (e parece não querer sair).
Embora o mercado ofereça várias soluções para gerenciamento de permissões, como o Acegi, não encontrei nelas a transparência, a facilidade de uso, a escalabilidade e a integração necessária com as aplicações novas e, principalmente, com as já em produção. Em outras palavras, eu queria um controle de acesso totalmente integrável ao meu sistema, mesmo que ele já estivesse pronto. Não obstante, eu queria poder construir novos sistemas sem se preocupar com permissões. Da mesma forma, também queria poder aumentar ou diminuir meu sistema sem pensar em impactos no gerenciamento de permissões. Criar usuários, perfis, grupos, subgrupos, atribuir permissões específicas por usuário, por tela, por controle, com base em valor de dado e outras coisas. Tudo isso eu queria fazer sem ter impacto no código da aplicação. Em suma, eu queria que algo como isso nunca existisse em um sistema:

//No carregamento do formulário...
if (usuario.getPermissions().hasValues("ADMIN","GERENTE","DIRETOR") {
btnExportarDados.setEnable(true);
} else {
btnExportarDados.setEnable(false);
}

Deu para entender? Bem, esse tipo de código é sempre uma dor de cabeça e, em qualquer solução de mercado, ele vai existir.
Esse tipo de controle é chamado (grosso modo) Permissão Por Controle (PPC). Devido sua granularidade elevada, aplicações grandes podem facilmente implodir devido a gama de possibilidades que podem ocorrer. Permissões Por Tela (PPT) são mais fáceis de solucionar. Em aplicações web, é relativamente fácil para um servlet controlador dirimir sobre as permissões (baseado em expressões regulares que definem URLs de páginas) e habilitar ou não o acesso para o usuário. Permissões por Sistema (PPS) são ainda mais simples. Uma página de logon resolve o problema. Por outro lado, Permissões Por Valor (PPV) são mais problemáticas que as permissões por controle. Dado o conteúdo de um objeto, o usuário terá ou não acesso à informação. Como exemplo, poderíamos dizer que Administradores e o João (sempre ele) têm acesso aos dados de número 1, 2 e 3. Todos os outros integrantes do controle acesso, exceto os Estagiários do Departamento de Tecnografica (o que é isso?) podem ver os valores maiores que 4. Os Estagiários do depto. acima têm permissão padrão sobre dado (que permissão é essa?)
Bem, o parágrafo acima ilustra um cenário bastante simplista, mas real de gerenciamento de permissões. Nem cheguei a falar de GRANTs, REVOKEs, inversão de permissões ou integração com mecanismos de autenticação e Single Sign On (...)
Imaginar um controle de acesso que faça tudo isso e sem mexer em uma linha do código da aplicação é um dos meus sonhos (além do Merlin, do Melvin, do Jestor....).
Bem, o fato é que nesses dois anos de elucubrações, percebi que é possível sim conseguir isso. E a resposta é extremamente simples (ou quase). As peças para ela são: reflexão (adoro ela), instrumentação de código e uma estrutura de permissões escalável, que permita tanto evoluir os objetos da aplicação quanto as ações que podem ser realizadas no sistema e os integrantes que o compõem.
Na prática, o Magoo é o projeto que reúne essas características que considero essenciais para um controle de permissões.

O Estalo
Nessa semana, enquanto o Julio cuidava da integração do sistema em um cliente, eu peguei o note dele e coloquei em prática algo que até então parecia difícilimo: como colocar tudo isso de forma não intrusiva em um sistema?
Embora eu conheça (e reclame sempre) da arquitetura web, meu chão de fábrica é o Swing. Gosto dele porque tem ferramentas visuais (embora ainda toscas) para trabalhar e é uma API MVC por natureza. E estruturas MVC têm vantagens notórias.
Uma delas, é a separação clara dos trabalhos. Temos o Modelo (de dados), empacotado em classes ou interfaces simples. Temos o Controle, que trata (ou delega) as ações do usuário. Temos também a View, que mostra para o usuário aquilo que o modelo contém (adoro o pattern Observer) e o que o Controle operou como regra de negócio.
Pois bem. Um dos controles que pode ser feito no Swing é adicionar listeners para eventos, tais como o ActionListener de um botão. Mas aqui ele não interessa. O que interessa é o listener do primeiro código do post: o PropertyChangeListener.
Esse listener é acionado toda vez que uma propriedade muda no controle de tela. Uma operação como setEnable, setVisible ou setColor dispara esse listener. Assim, você pode ouvir essas operações e tomar atitudes. Já sacou? Ainda não? Continuemos...

Listeners sr. Watson! Listeners!
Podemos criar um listener de modificação de propriedade bem simples, que faz o seguinte: Quando uma regra de negócio modificar sua propriedade para enabled=true, o listener de modificação é disparado. Ele vai em uma base de permissões e verifica se o usuário atual tem permissões para isso. Se tem, nada acontece. Se não tem, o listener aplica enabled=false de novo e deixa vir a renderização do controle. Obviamente, o controle não é habilitado, mesmo que a regra de negócio tenha tentado. Em outras palavras, nosso listener suprimiu a regra de negócio e manteve o status desabilitado no botão para o João.

Instrumentação de código
Obviamente, não é interessante criar listener para todas as telas e controles do sistema. Isso seria algo extremamente demorado e em nada escalável. Precisamos de algo mais simples.
Uma alternativa tosca é usar AOP e adicionar os listeners na mão para os controles. O grande inconveniente é termos que apontar cada elemento da aplicação manualmente e decorá-lo com o listener em questão.
Uma alternativa bem melhor seria deixar a reflexão trabalhar em conjunto com a instrumentação de código (auauauau). Explico.
Através da reflexão podemos varrer todo o grafo de objetos do sistema (qualquer objeto acessível através do bootstrap da aplicação) e verficar seu conteúdo, tipo, etc. Assim, poderíamos extrair os objetos que são telas, os que são controles (etc.) e montar uma árvore ou grafo de objetos. Esses objetos seriam comparados aos objetos gerenciados pela permissão e, quando fossem iguais, o listener seria adicionado via reflexão. E a instrumentação de código?
A instrumentação de código vem para resolver a seguinte questão: usar AOP ou reflexão dá na mesma, pois precisamos rodar um código dentro da aplicação cliente. Instrumentar a aplicação permite que nossos códigos de reflexão sejam carregados no boot da aplicação, mas sem o inconveniente de ter que alterar o código do sistema cliente.
Já testei isso no Merlin e funcionou muito bem para o reloading de classes. Para o Magoo, o conceito é o mesmo.

Usuários e ações
Bem, dito o que fazer para resolver o problema (usar listeners) e como fazer para evitar alterações de código (reflexão + instrumentação) a perguna que fica é: Como gerenciar a complexidade de usuários e objetos gerenciados?
Minha resposta é simples: Utilizar o conceito de ações (baseei isso no conceito - nada análogo - de ações em Casos de Uso) de usuário. Enquanto os outros controles de acesso tem a visão de perfil > operação; o Magoo tem o conceito de perfil > ação. Em palavras simples, enquanto uma operação define uma função F:pode(permissao,objeto); uma ação define a função F:pode(açao,permissões). Que loucura! Espera.
Uma ação nada mais é do que um conjunto de permissões sobre um objeto. E um objeto pode ser qualquer coisa tangível na interface do usuário: um sistema, um módulo, uma tela ou um controle. As permissões podem ser Ver e Habilitar (sendo que isso são abstrações para coisas como Ver e Clicar e coisas do gênero). Ações podem ser agrupadas. Integrantes podem executar ações. Integrantes podem ser Usuários ou Grupos. Grupos são compostos de integrantes, sucessivamente. Permissões podem ser diretas, baseadas em expressões regulares, regras de negócio ou scripts (ou qualquer outra coisa). E tudo isso fica em cache. E pronto. E estou com sono.

Bem, isso foi um preview do Magoo e das coisas que ele vai implementar. Não tenho previsão para ele, mas digo que os contatos que estou tendo, parecem mostrar-me que ele é algo bem mais simples de implementar que o Merlin e possui um mercado mais amplo e imediato.
Talvez (eu disse talvez) acabo fazendo ele antes do Merlin.

0 comentários: