A muito tempo não posto um bom “php” por aqui, então vamos lá… A algum tempo postei sobre Zend_Auth e fiquei devendo um post sobre Zend_Acl.
Problemas com controle de acesso viram soluções com Zend_Acl. A alguns meses precisei implementar o controle de acesso para um sistema com ZF e me encantei com a facilidade e praticidade do Acl. Primeiro, alguns conceitos básicos…
Roles: São os perfis definidos. Basicamente, quem tem ou não acesso.
Resources: São as funcionalidades a serem controladas.
Usarei como exemplo o código que implementei já citado anteriormente. Criamos uma tabela simples para as funcionalidades, uma para perfis e duas tabelas associativas: uma entre perfis e funcionalidades e outra entre usuários e funcionalidades. Para abreviar, vou mostrar somente a tabela de funcionalidades:
-
cd_funcionalidade NUMBER NOT NULL,
-
descricao VARCHAR2(150) NOT NULL,
-
funcionalidade VARCHAR2(200) NOT NULL,
-
PRIMARY KEY(cd_funcionalidade));
Nesta tabela de funcionalidades será cadastrado uma descrição e a url da funcionalidade (resource). Ex: /usuarios ou /administracao/perfil
Assim que o usuário se autentica no sistema, redireciono ele para um controller chamado “acesso” para montar o objeto Acl. Neste exemplo, o usuário pode ter as funcionalidades definidas pelo perfil ou definidas diretamente para ele. Da seguinte forma…
-
-
//Model da tabela associativa entre Usuário e Perfil
-
$objPerfilUsuario = new PerfilUsuario();
-
-
//Model da tabela de funcionalidades
-
$objFuncionalidade = new Funcionalidade();
-
-
//Dados da autenticação do usuário…
-
$nsAutenticacao = new Zend_Session_Namespace('autenticacao');
-
-
$idUser = $nsAutenticacao->id;
-
-
//Recuperando os dados do perfil do usuário
-
$arrPerfil = $objPerfilUsuario->getPerfilUsuario($idUser);
-
-
$nsAutenticacao->idPerfil = $arrPerfil['idPerfil'];
-
$nsAutenticacao->perfil = $arrPerfil['perfil'];
-
-
//Criando o objeto Acl
-
$acl = new Zend_Acl();
-
-
//Crio uma role com o nome do perfil do usuário
-
$acl->addRole(new Zend_Acl_Role($arrPerfil['perfil']));
-
-
//Funcionalidades definidas por perfil
-
-
//Recupero as funcionalidades definidas para o perfil…
-
$arrFuncionalidades = $objFuncionalidade->getFuncionalidadesAcl($arrPerfil['idPerfil']);
-
-
//Em um foreach, adiciono ao objeto Acl as funcionalidades (resources) e a permissão
-
foreach($arrFuncionalidades['allow'] as $array) {
-
$acl->add(new Zend_Acl_Resource($array));
-
$acl->allow($arrPerfil['perfil'],$array);
-
}
-
-
if(isset($arrFuncionalidades['deny'])){
-
foreach($arrFuncionalidades['deny'] as $array) {
-
$acl->add(new Zend_Acl_Resource($array));
-
$acl->deny($arrPerfil['perfil'],$array);
-
}
-
}
-
-
//Funcionalidades definidas por usuário
-
-
//Model da tabela associativa entre usuário e funcionalidade…
-
$objUserFunc = new UsuarioFuncionalidade();
-
-
//Recupero as funcionalidades definidas para o usuário
-
$arrFuncUser = $objUserFunc->getFuncionalidadesAcl($idUser);
-
-
//Como a role já está criada no objeto Acl, apenas adiciono as permissões…
-
foreach($arrFuncUser as $arr) {
-
$acl->allow($arrPerfil['perfil'],$arr);
-
}
-
-
//Crio um namespace para o objeto Acl
-
$ns = new Zend_Session_Namespace();
-
$ns->acl = $acl;
Obs: Nos métodos getFuncionalidadesAcl() das modelos utilizadas retorno um array com a seguinte estrutura: array(’idDaFuncionalidade’ => ‘Url da funcionalidade’). No caso da model Funcionalidades, existe uma particularidade: o array “deny”:
-
-
public function getFuncionalidadesAcl($idPerfil) {
-
-
$select = 'Seu sql';
-
-
$rs = $this->fetchAll($select)->toArray();
-
$arrFinal = array();
-
-
foreach($rs as $arrAllow) {
-
-
$arrFinal['allow'][$arrAllow['cd_funcionalidade']] = $arrAllow['TX_URL'];
-
-
}
-
-
unset($rs);
-
-
$rs = $this->fetchAll()->toArray();
-
-
/** Neste foreach eu verifico se existe alguma funcionalidade que está cadastrada na tabela funcionalidades
-
* mas não está definida para o perfil. Se existir, precisamos tirar a permissão de acesso a ela.
-
* Toda funcionalidade que não estiver cadastrada na tabela de funcionalidades tem seu acesso liberado.
-
*/
-
-
-
foreach($rs as $arrDeny) {
-
if((!array_key_exists($arrDeny['cd_funcionalidade'], $arrFinal['allow'])) AND ($arrDeny['funcionalidade'])) {
-
$arrFinal['deny'][$arrDeny['cd_funcionalidade']] = $arrDeny['funcionalidade'];
-
}
-
}
-
-
return $arrFinal;
-
-
}
Pronto, o objeto Acl está criado e com as permissões setadas. Agora basta fazer a verificação sempre que o usuário tentar acessar os controllers do sistema. No caso deste exemplo, temos uma classe que extende da Zend_Controller_Action, ou seja, qualquer redirecionamento do sistema passará por ela. Nesta classe, adicionei o seguinte código para realizar a verificação da permissão:
-
-
//Recupero o objeto Acl do namespace…
-
$ns = new Zend_Session_Namespace();
-
-
if(isset($ns->acl)) {
-
$acl = $ns->acl;
-
//Monto a Url que está tentando ser acessada para a verificação…
-
$url = '/'.$this->_request->getControllerName().'/'.$this->_request-getActionName();
-
-
if($acl->has($url)) {
-
-
//Recupero os dados de autenticação do usuário…
-
$nsAutenticacao = new Zend_Session_Namespace('autenticacao');
-
-
//Verifico se o perfil do usuário tem acesso à url
-
if(!$acl->isAllowed($nsAutenticacao->perfil, $url)) {
-
-
//Caso não seja, redireciono o usuário para algum lugar avisando que ele não tem acesso…
-
$this->_redirect('/index/sem-acesso');
-
}
-
}
-
}
Caso o usuário tenha o acesso, não preciso fazer nada… Apenas deixo a requisição continuar a sua execução e o usuário chegará à url.
Simples, não? Caso tenha alguma dúvida ou tenha alguma crítica ou sugestão para melhorar, deixe seu comentário ou entre em contato por email
Um abraço!




#1 by Juliano Meinen on May 19, 2010 - 5:28 pm
Boa dica, vlw!
#2 by Rogério Martins on May 26, 2010 - 11:58 am
muito bom seu post, porém como estou iniciando com o zf não abstrai como seria o controlador para fazer a verificação, ou seja, como ele seria acionado em todas as requisições??
#3 by Thiago Colares on May 26, 2010 - 1:47 pm
Rogério, ao criar uma classe extendida da Zend_Controller_Action, todas as requisições do seu sistema passarão por ela, basta você extender os seus controllers da classe criada. Sendo assim, todos os redirecionamentos do seu sistema passarão pela validação do Acl.
Um abraço!