Olá meu querido! Os posts por aqui estão escassos, sei. Mas, agora que tal uma coisa interessante do ponto de vista técnico, hein!?

Para fazer um controle de permissões básico com as quatro permissões fundamentais em um sistema (Visualização, Inclusão, Alteração e Exclusão) quantos campos são necessários em uma tabela de banco de dados? No mínimo quatro, você poderia dizer.

Vamos fazer uma modelagem de uma tabela que armazena a relação entre perfis de usuários e módulos de um sistema:

image

Basicamente criamos um campo booleano para cada permissão básica. Para verificar se um usuário de determinado perfil tem permissão em um módulo, basta puxarmos o ID do módulo, o ID do perfil do usuário, fazer uma consulta à base de dados (ou em algum local onde estas permissões são guardadas em cache - o que melhora o desempenho) e verificarmos se a permissão adequada tem valor Verdadeiro ou Falso.

Agora pensa uma coisa comigo: Não é fácil para um usuário qualquer entrar no banco de dados e alterar a permissão? Sim sim, isso é possível e existe uma forma de “ofuscar” as permissões armazenadas em banco de dados: gravar TODO o conjunto de permissões como um único campo do tipo Integer e extrair dele as permissões utilizando operações bit a bit dentro da aplicação. Como faz, com mágica?

Mágica não, meu querido! Faz-se com Matemática! Que tal pegarmos aquelas aulinhas no começo da faculdade sobre operações booleanas e aplicá-las em algo mais “palpável”?

Primeiro vamos remodelar a nossa base de dados, onde no lugar de quatro campos booleanos de permissões teremos apenas um inteiro que gravará todo o conjunto (vamos utilizar apenas as quatro permissões fundamentais para ficar mais didático ;) ).

export

Ah, já sei! Vamos atribuir “1” para Visualização, “2” para Inclusão, “3” para Alteração e “4” para Exclusão, né safadinho! Na na ni nã NÃO! Isto é muito feio! hehe.

Não vamos pensar no campo ConjuntoPermissões como um mero inteiro. Dependendo do banco de dados (e da linguagem que iremos utilizar, que no nosso caso é - adivinha - o C#), o tamanho físico de um campo do tipo Integer é de 4 bytes, 32 bits. Com isso podemos guardar até 32 permissões em um único campo!

Vamos ver a matemática da coisa agora:

Iremos utilizar 4 bits para guardar o nosso conjunto de permissões. Para ficar mais fácil de entender, vamos desenhar:

image

No esquema acima temos o número 15 representado no sistema binário. Cada bit representa uma permissão. Um bit ligado significa permissão concedida. Para revogá-la, basta desligarmos o bit correspondente. Note que cada bit tem um “peso”, e que da direita para a esquerda esse peso corresponde a uma potência de 2. Dois elevado a zero até dois elevado a três.

Vamos representar neste esquema um usuário apenas com permissões de Inclusão e Alteração:

image

Neste conjunto de permissões apenas o segundo e o terceiro bit (contando da direita para a esquerda) estão ligados. Vamos saber agora como extrair as permissões de um usuário neste esquema.

Como eu disse acima, cada bit tem o seu peso representado por uma potência de 2. Vamos representar cada uma das permissões seguido o mesmo modelo que fizemos acima:

image

Para descobrir se um usuário tem determinada permissão, fazemos uma operação “AND” bit a bit entre o conjunto de permissões e o valor da permissão desejada e verificar se o resultado desta operação é MAIOR que zero. Vamos verificar a permissão de Alteração fazendo um “AND” bit a bit utilizando os dois esquemas acima:

image

Fazendo essa operação (o “AND” equivale a uma multiplicação, mas fique atento que fizemos a multiplicação entre os bits e não com o inteiro) vemos que o resultado é exatamente aquele bit que coincide com o que queremos procurar, e que o resultado final é maior do que zero. Com isso verificamos que o usuário tem a permissão adequada. Que tal vermos a permissão de Exclusão?

image

Como vimos no esquema, o resultado do “AND” bit a bit deu zero, ou seja, o usuário NÃO tem a permissão adequada.

Beleza, agora sei como extrair as permissões. E agora, como faço para montar o conjunto de permissões? Essa parte é bem simples: basta fazermos uma operação “OR” bit a bit entre as permissões que você quer dar para o usuário. Vamos dar as permissões de visualização, inclusão e alteração?

image

A operação OR é equivalente a uma soma. Note que embora o resultado no sistema decimal seja equivalente (o que não é no caso do AND) fizemos esta conta bit a bit.

Depois dessa demonstração matemática, que tal aplicarmos este conceito em um programa C#? Serve qualquer banco de dados, então os detalhes de conexão fogem do escopo deste artigo.

Para representar as permissões você pode pensar em atribuir  números igual ao que fizemos na demonstração matemática. 1 para Visualização, 2 para Inclusão e assim por diante, sempre crescendo em potência de 2. Vamos fazer isso, porém iremos representar as permissões através de uma enumeração. Além de ser uma boa prática de programação, o código ficará muito mais legível para os mantenedores. Assim:

public enum PermissoesAcesso
{
	Visualizar = 1,
	Inserir = 2,
	Alterar = 4,
	Excluir = 8
}

Note que encapsulamos os valores inteiros (números “mágicos”, em linguagem mais acadêmica) na enumeração PermissoesAcesso, muito mais legível.

Vamos criar um método que verifica se um determinado perfil passado como parâmetro tem a permissão que iremos indicar em outro parâmetro, para um módulo indicado em um terceiro parâmetro:

public bool VerificaPermissao(int IdPerfil, PermissoesAcesso Permissao, int IdModulo)
{
	//...conecte com o BD aqui e crie os parâmetros...
	int ConjuntoPermissoes = (int)MeuCommand.getScalar("select ConjuntoPermissoes where IdPerfil = @PERFIL and IdModulo = @MODULO");
	//...feche a conexão...
	return (ConjuntoPermissoes & (int)Permissao) > 0;
}

Para podermos fazer o “AND” bit a bit do conjunto de permissões (que é um Integer) com o valor inteiro da enumeração PermissoesAcesso, devemos fazer um cast da variável Permissao para inteiro.

Agora vamos criar um outro método, que recebe um array com as permissões que serão atribuidas a um perfil, o perfil e o módulo, monta o inteiro correspondente ao conjunto de permissões e salva na base de dados:

public void SalvaPermissoes(int IdPerfil, int IdModulo, PermissoesAcesso[] Permissoes)
{
	int ConjuntoPermissoes = 0;
	foreach(PermissoesAcesso perm in Permissoes)
	{
		ConjuntoPermissoes |= (int)perm;
	}
	StringBuilder sql = new StringBuilder("update Permissoes set ConjuntoPermissoes = @PERM where IdPerfil = @PERFIL and IdModulo = @MODULO");
	//...Crie os parâmetros e execute o SQL.
}

O laço foreach do código acima vai somando (vale a pena lembrar que um OR bit a bit é uma operação de soma), bit a bit, cada permissão do array e acumulando na variável ConjuntoPermissoes. Para fazer o OR bit a bit de um inteiro (variável ConjuntoPermissoes) com o inteiro da enumeração Permissoes, precisamos fazer um cast da segunda para inteiro. Exatamente como fizemos na demonstração matemática.

Para atribuir duas (ou mais) permissões de forma isolada, basta fazer o OR bit a bit entre elas:

int Permissoes = (int)PermissoesAcesso.Visualizar | (int)PermissoesAcesso.Inserir;

Com isso, vimos como melhorar o nosso sistema de armazenamento de permissões de forma eficiente e eficaz. Este recurso de operação bit a bit é utilizado, por exemplo, na API do Windows, ou até mesmo no C#, onde temos funções em que um parâmetro admite vários flags (representado por enumerações ou até mesmo constantes inteiras) concatenados. E essa concatenação dos flags é feita justamente com o operador OR bit a bit.

Um abraço!