Archive for maio de 2010

Controle de acesso com Zend_Acl

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:

CREATE TABLE funcionalidade (
  1. cd_funcionalidade NUMBER NOT NULL,
  2. descricao VARCHAR2(150) NOT NULL,
  3. funcionalidade VARCHAR2(200) NOT NULL,
  4. 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…

  1.  
  2. //Model da tabela associativa entre Usuário e Perfil
  3. $objPerfilUsuario = new PerfilUsuario();
  4.  
  5. //Model da tabela de funcionalidades
  6. $objFuncionalidade = new Funcionalidade();
  7.  
  8. //Dados da autenticação do usuário…
  9. $nsAutenticacao = new Zend_Session_Namespace('autenticacao');
  10.  
  11. $idUser = $nsAutenticacao->id;
  12.  
  13. //Recuperando os dados do perfil do usuário
  14. $arrPerfil = $objPerfilUsuario->getPerfilUsuario($idUser);
  15.  
  16. $nsAutenticacao->idPerfil  = $arrPerfil['idPerfil'];
  17. $nsAutenticacao->perfil  = $arrPerfil['perfil'];
  18.  
  19. //Criando o objeto Acl
  20. $acl = new Zend_Acl();
  21.  
  22. //Crio uma role com o nome do perfil do usuário
  23. $acl->addRole(new Zend_Acl_Role($arrPerfil['perfil']));
  24.  
  25. //Funcionalidades definidas por perfil
  26.  
  27. //Recupero as funcionalidades definidas para o perfil…
  28. $arrFuncionalidades = $objFuncionalidade->getFuncionalidadesAcl($arrPerfil['idPerfil']);
  29.  
  30. //Em um foreach, adiciono ao objeto Acl as funcionalidades (resources) e a permissão
  31. foreach($arrFuncionalidades['allow'] as $array) {
  32.  $acl->add(new Zend_Acl_Resource($array));
  33.  $acl->allow($arrPerfil['perfil'],$array);
  34. }
  35.  
  36. if(isset($arrFuncionalidades['deny'])){
  37.  foreach($arrFuncionalidades['deny'] as $array) {
  38.   $acl->add(new Zend_Acl_Resource($array));
  39.   $acl->deny($arrPerfil['perfil'],$array);
  40.  }
  41. }
  42.  
  43. //Funcionalidades definidas por usuário
  44.  
  45. //Model da tabela associativa entre usuário e funcionalidade…
  46. $objUserFunc = new UsuarioFuncionalidade();
  47.  
  48. //Recupero as funcionalidades definidas para o usuário
  49. $arrFuncUser = $objUserFunc->getFuncionalidadesAcl($idUser);
  50.  
  51. //Como a role já está criada no objeto Acl, apenas adiciono as permissões…
  52. foreach($arrFuncUser as $arr) {
  53.  $acl->allow($arrPerfil['perfil'],$arr);
  54. }
  55.  
  56. //Crio um namespace para o objeto Acl
  57. $ns = new Zend_Session_Namespace();
  58. $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”:

  1.  
  2.  public function getFuncionalidadesAcl($idPerfil) {
  3.  
  4.   $select = 'Seu sql';
  5.  
  6.   $rs = $this->fetchAll($select)->toArray();
  7.   $arrFinal = array();
  8.  
  9.   foreach($rs as $arrAllow) {
  10.  
  11.    $arrFinal['allow'][$arrAllow['cd_funcionalidade']] = $arrAllow['TX_URL'];
  12.  
  13.   }
  14.  
  15.   unset($rs);
  16.  
  17.   $rs = $this->fetchAll()->toArray();
  18.  
  19.   /** Neste foreach eu verifico se existe alguma funcionalidade que está cadastrada na tabela funcionalidades
  20.   * mas não está definida para o perfil. Se existir, precisamos tirar a permissão de acesso a ela.
  21.   * Toda funcionalidade que não estiver cadastrada na tabela de funcionalidades tem seu acesso liberado.
  22.   */
  23.  
  24.  
  25.   foreach($rs as $arrDeny) {
  26.    if((!array_key_exists($arrDeny['cd_funcionalidade'], $arrFinal['allow'])) AND ($arrDeny['funcionalidade'])) {
  27.     $arrFinal['deny'][$arrDeny['cd_funcionalidade']] = $arrDeny['funcionalidade'];
  28.    }
  29.   }
  30.  
  31.   return $arrFinal;
  32.  
  33.  }

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:

  1.  
  2. //Recupero o objeto Acl do namespace…
  3. $ns = new Zend_Session_Namespace();
  4.  
  5. if(isset($ns->acl)) {
  6.  $acl = $ns->acl;
  7.         //Monto a Url que está tentando ser acessada para a verificação…
  8.  $url = '/'.$this->_request->getControllerName().'/'.$this->_requestgetActionName();
  9.  
  10.  if($acl->has($url)) {
  11.  
  12.   //Recupero os dados de autenticação do usuário…
  13.   $nsAutenticacao = new Zend_Session_Namespace('autenticacao');
  14.  
  15.                 //Verifico se o perfil do usuário tem acesso à url
  16.   if(!$acl->isAllowed($nsAutenticacao->perfil, $url)) {
  17.  
  18.    //Caso não seja, redireciono o usuário para algum lugar avisando que ele não tem acesso…
  19.    $this->_redirect('/index/sem-acesso');
  20.   }
  21.  }
  22. }

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!

, , ,

5 Comentários