Introdução

Zend_Acl fornece uma implementação de lista de controle de acesso (ACL, na sigla em inglês) leve e flexível para a gestão privilégios. Em geral, uma aplicação pode utilizar essa ACL para controlar o acesso a determinados objetos protegidos por outros objetos requerentes.

Para o propósito desta documentação:

  • Um recurso é um objeto cujo acesso é controlado

  • Um papel é um objeto que pode solicitar acesso a um Recurso.

De modo simples, papéis requisitam acesso a recursos. Por exemplo, se um atendente de estacionamento solicita acesso ao um carro, o atendente é o papel requisitante e o carro é o recurso, pois o acesso ao carro pode não ser garantido a qualquer um.

Através da especificação e uso de uma ACL, uma aplicação pode controlar como os papéis têm acesso aos recursos.

Recursos

A criação de um recurso em Zend_Acl é muito simples. Zend_Acl fornece um meio, Zend_Acl_Resource_Interface, para facilitar a criação de recursos em uma aplicação. Uma classe precisa apenas implementar esta interface, que consiste em um único método, getResourceId(), para que Zend_Acl reconheça o objeto como um recurso. Adicionalmente, Zend_Acl_Resource é fornecida por Zend_Acl como uma implementação básica de recurso para que desenvolvedores a extendam conforme necessário.

Zend_Acl fornece uma estrutura de árvore na qual múltiplos recursos podem ser adicionados. Como os recursos são armazenados em uma estrutura de árvore, eles podem ser organizados do geral (em direção à raiz da árvore) para o específico (em direção às folhas da árvore). Consultas em um recurso específico irá automaticamente pesquisar sua hierarquia por regras definidas em recursos ancenstrais, permitindo herança simplificada de regras. Por exemplo, se uma regra deve ser aplicada a cada construção em uma cidade, pode-se simplesmente aplicar a regra à cidade, ao invés de atribuir a mesma regra para cada construção. Contudo, algumas construções podem necessitar de exceções para tal regra e isto pode ser feito em Zend_Acl atribuindo tal exceção a cada construção que a necessite. Um recurso pode descender de apenas um recurso pai, embora este recurso pai pode possuir seu próprio pai, etc.

Zend_Acl também suporta privilégios em recursos (ex., "criar", "ler", "atualizar", "excluir") e o desenvolvedor pode atribuir regras que afetam todos os privilégios ou apenas privilégios específicos em um ou mais recursos.

Papéis

Assim como recursos, criar um papel é também muito simples. Todos os papéis devem implementar Zend_Acl_Role_Interface. Esta interface consiste em um único método, getRoleId(). Adicionalmente, Zend_Acl_Role é fornecida por Zend_Acl como uma implementação de papel básica da qual desenvolvedores podem extender, quando necessário.

Em Zend_Acl, um papel pode derivar de um ou mais papéis. Isto é para suportar herança de regras através de papéis. Por exemplo, um papel de usuário, tal como "sally", pode derivar de um ou mais papéis pai, como "editor" e "administrador". O desenvolvedor pode atribuir regras para "editor" e "administrador" separadamente, e "sally" herdará tais regras de ambos, sem a necessidade de atribuir regras diretamente a "sally".

Embora a abilidade de herdaer de múltiplios papéis seja muito útil, herança múltipla também introduz algum grau de complexidade. O exemplo a seguir ilustra a condição ambígua e como Zend_Acl a soluciona.

Exemplo 28. Herança múltipla através de Papéis

O código a seguir define três papéis fundamentais - "visitante", "membro" e "admin" - dos quais outros papéis irão descender. Então, um papel identificado por "algumUsuario" é estabelecido e descende dos outros três papéis. A ordem em que estes papéis aparecem no array $parents é importante. Quando necessário, Zend_Acl procura por regras de acesso definidas não somente para o papél consultado (aqui, "algumUsuario"), mas também em papéis do qual o papel consultado descende (aqui, "visitante", "membro" e "admin"):

$acl = new Zend_Acl();

$acl->addRole(new Zend_Acl_Role('visitante'))
    ->addRole(new Zend_Acl_Role('membro'))
    ->addRole(new Zend_Acl_Role('admin'));

$parents = array('visitante', 'membro', 'admin');
$acl->addRole(new Zend_Acl_Role('algumUsuario'), $parents);

$acl->add(new Zend_Acl_Resource('algumRecurso'));

$acl->deny('visitante', 'algumRecurso');
$acl->allow('membro', 'algumRecurso');

echo $acl->isAllowed('algumUsuario', 'algumRecurso') ? 'permitido' : 'negado';

Como não há regra especificamente definida para o papel "algumUsuario" e "algumRecurso", Zend_Acl deve procurar por regras que tenham sido definidas para papéis dos quais "algumUsuario" descende. Primeiro, o papel "admin" é visitado e não há regra de acesso definida para ele. Depois, o papel "membro" é visitado e Zend_Acl verifica que há uma regra especificando que "membro" tem acesso permitido a "algumRecurso".

Se Zend_Acl continuar examinando as regras definidas para outros papéis pai, contudo, ela encontrará que "visitante" tem acesso negado a "algumRecurso". Este é o fato que introduz uma ambiguidade, pois agora "algumUsuario" possui, ao mesmo tempo, acesso negado e permitido a "algumRecurso", pelo motivo de herdar regras conflitantes de diferentes papéis pai.

Zend_Acl soluciona esta ambiguidade concluindo a consulta quando ela encontra a primeira regra diretamente aplicável a consulta. Neste caso, como o papel "membro" é examinado antes do papel "visitante", o código de exemplo exibirá "permitido".


Nota

Quando especificando múltiplos pais para um papel, tenha em mente que o último pai listado será o primeiro buscado para regras aplicáveis a uma consulta de autorização.

Criando Listas de Controle de Acesso

Uma Lista de Controle de Acesso (ACL, na sigla em inglês) pode representar qualquer conjunto de objetos físicos ou virtuais que você desejar. Contudo, para o propósito de demonstração, criaremos uma ACL básica de um Sistema de Gerenciamento de Conteúdo (CMS, na sigla em inglês) que mentém diversas camadas de grupos através de uma grande variedade de áreas. Para criar um novo objeto ACL, instanciamos a ACL sem parâmetros:

$acl = new Zend_Acl();

Nota

A menos que um desenvolvedor especifique uma regra "allow" (permitir - em inglês), Zend_Acl negará acesso a todo privilégio em todo recurso para todo papel.

Registrando Papéis

O CMS irá quase sempre necessitar de uma hierarquia de permissões para determinar as capacidades de autoridade de seus usuários. Pode haver um grupo 'Visitante' para permitir acesso limitado para demonstrações, um grupo 'Equipe' para que a maioria dos usuários do CMS que executam grande parte das operações diárias, um grupo 'Editor' responsável pela publicação, revisão, arquivamento e exclusão de conteúdo, e finalmente um grupo 'Administrador' cujas tarefas podem incluir todas as de outros grupos bem como a manutenção de informações sensíveis, gerenciamento de usuários, configuração de back-end, dados de configuração, cópias de segurança e exportação. Este conjunto de permissões podem ser representadas em um registro de papéis, permitindo a cada grupo herdar privilégios de grupos 'pai', assim como fornecer privilégios distintos para grupos únicos. As permissões podem ser expressas como:

Tabela 1. Controles de Acesso para um CMS de Exemplo

Nome Permissões Únicas Permissões herdadas de
Visitante Visualizar N/D
Equipe Editar, Enviar, Revisar Visitante
Editor Publicar, Arquivar, Excluir Equipe
Administrador (Todos os acessos garantidos) N/D

Para este exemplo, Zend_Acl_Role é usado, mas qualquer objeto que implemente Zend_Acl_Role_Interface é aceitável. Estes grupos podem ser adicionados ao registro de papéis, como a seguir:

$acl = new Zend_Acl();
// Adiciona grupos ao registro de papéis usando Zend_Acl_Role
// 'Visitante' não herda controles de acesso
$roleGuest = new Zend_Acl_Role('visitante');
$acl->addRole($roleGuest);

// 'Equipe' descende de 'Visitante'
$acl->addRole(new Zend_Acl_Role('equipe'), $roleGuest);

/*
Alternativamente, o código acima poderia ser escrito como:
$acl->addRole(new Zend_Acl_Role('equipe'), 'visitante');
*/

// 'Editor' descende de 'Equipe'
$acl->addRole(new Zend_Acl_Role('editor'), 'equipe');

// 'Administrador' não herda controles de acesso
$acl->addRole(new Zend_Acl_Role('administrator'));

Definindo Controles de Acesso

Agora que a ACL contém os papéis relevantes, regras podem ser estabelecidas para definir quais recursos podem ser acessados por quais papéis. Você pode notar que não definimos nenhum recurso em particular para este exemplo, que é simplificado para ilustrar que regras se aplicam a todos os recursos. Zend_Acl fornece uma implementação onde regras necessitam apenas serem atribuidas do caso geral para o específico, minimizando o número de regras necessárias, pois recursos e papéis herdam regras definidas em seus ancestrais.

Nota

Em geral, Zend_Acl obedecerá uma regra dada se e somente se uma regra mais específica não for aplicável.

Consequentemente, podemos definir um conjunto razoavelmente complexo de regras com o mínimo de código. Para aplicar as permissões básicas, tal como definido acima:

$acl = new Zend_Acl();

$roleGuest = new Zend_Acl_Role('visitante');
$acl->addRole($roleGuest);
$acl->addRole(new Zend_Acl_Role('equipe'), $roleGuest);
$acl->addRole(new Zend_Acl_Role('editor'), 'equipe');
$acl->addRole(new Zend_Acl_Role('administrador'));

// 'Visitante' pode apenas visualizar conteúdo
$acl->allow($roleGuest, null, 'visualizar');

/*
Alternatively, the above could be written:
$acl->allow('visitante', null, 'visualizar');
//*/

// 'Equipe' herda privilégios de 'Visitante', porém precisa de
// privilégios adicionais
$acl->allow('equipe', null, array('editar', 'enviar', 'revisar'));

// 'Editor' herda os privilégios visualizar, editar, enviar, e revisar de
// 'Equipe', mas também precisa de privilégios adicionais
$acl->allow('editor', null, array('publicar', 'arquivar', 'excluir'));

// Administrador não herda nada, mas têm acesso a tudo
$acl->allow('administrador');

Os valores NULL nas chamadas allow() acima indicam que as regras para permitir acesso se aplicam a todos os recursos.

Consultando uma ACL

Temos agora uma ACL flexível que pode ser usada para determinar quais solicitantes têm permissão para executar funções através da aplicação web. Realizar consultas é bastante simples usando o método isAllowed():

echo $acl->isAllowed('visitante', null, 'visualizar') ?
     "permitido" : "negado";
// permitido

echo $acl->isAllowed('equipe', null, 'publicar') ?
     "permitido" : "negado";
// negado

echo $acl->isAllowed('equipe', null, 'revisar') ?
     "permitido" : "negado";
// permitido

echo $acl->isAllowed('editor', null, 'visualizar') ?
     "permitido" : "negado";
// 'permitido' por conta da herança de 'visitante'

echo $acl->isAllowed('editor', null, 'atualizar') ?
     "permitido" : "negado";
// 'negado' pois não há regra 'atualizar'

echo $acl->isAllowed('administrador', null, 'visualizar') ?
     "permitido" : "negado";
// permitido, pois administrador é permitido a todos os privilégios

echo $acl->isAllowed('administrador') ?
     "permitido" : "negado";
// permitido, pois administrador é permitido a todos os privilégios

echo $acl->isAllowed('administrador', null, 'atualizar') ?
     "permitido" : "negado";
// permitido, pois administrador é permitido a todos os privilégios