Autorização com Zend Framework

Veremos aqui uma explicação detalhada dos componentes de Autorização do Zend Framework, demonstrando como restringir acesso a recursos de um sistema, baseando-se nos privilégios que um usuário autenticado possui. Para este artigo, é essencial o estudo do artigo sobre Autenticação com Zend Framework.

Conceitos

Autorização é o ato de verificar as permissões de um usuário já autenticado no sistema e, baseando-se nessas permissões, permitir ou bloquear o acesso deste usuário a determinados recursos da aplicação. Se, por exemplo, em um Sistema de Gestão de Conteúdo (CMS), o usuário logado é um escritor, ele poderia ter acesso à escrita de artigos, porém não poderia ter acesso ao cadastro de novos usuários, pode-se obter este tipo de funcionalidade por meio de um componente de autorização. No Zend Framework este componente é o Zend_Acl, que fornece a funcionalidade de Lista de Controle de Acesso (ACL) e gestão de privilégios. Em geral, uma aplicação pode usar esta funcionalidade para controlar o acesso a certos objetos protegidos, requeridos por outros objetos.

Existem alguns termos utilizados para melhor explanar as entidades envolvidas no controle de acesso, cada um deles é detalhado a seguir.

Papel (role)

Um papel corresponde a uma responsabilidade de um usuário dentro de um sistema como, por exemplo, o papel de “colunista” ou de “membro”. Isto é encapsulado através da classe Zend_Acl_Role, que é uma classe simples que apenas armazena o nome do papel. Existe também herança de papéis, onde ao se herdar de um papel, é possível fazer tudo que o papel pai faz, além das ações específicas do papel filho. Para se criar um papel e adicioná-lo  na lista de controle de acesso, o seguinte código é necessário:

$papelMembro = new Zend_Acl_Role('membro');
$acl = new Zend_Acl();
$acl->addRole($papelMembro);

Para utilizar a herança de papéis basta passar o nome do papel pai como segundo parâmetro do método addRole(). Para exemplificar isso o papel “colunista” irá herdar de “membro”, pois um colunista pode fazer tudo o que um membro pode, além de possuir permissões especiais para criar colunas. O código deste exemplo é apresentado abaixo:

$papelMembro = new Zend_Acl_Role('membro');
$papelColunista = new Zend_Acl_Role('colunista');
$acl = new Zend_Acl();
$acl->addRole($papelMembro);
$acl->addRole($papelColunista, 'membro');

Recurso (resource)

Um recurso é algo a ser protegido, o que pode ser um controlador ou uma ação. Um recurso é encapsulado pela classe Zend_Acl_Resource, que simplesmente armazena o nome do recurso que será protegido. Assim como no caso do Zend_Acl_Role, a classe Zend_Acl_Resource oferece suporte a herança, esta sendo definida no segundo parâmetro do método add() de Zend_Acl. Um exemplo de utilização de recursos é o caso de um sistema de ERP onde usuários do papel “administrador” podem ter acesso ao controlador manutencao, já usuários do papel “operador” podem ter acesso ao controlador produto. Para criar estes recursos o seguinte código é necessário:

$acl = new Zend_Acl();
$acl->addResource(new Zend_Acl_Resource('manutencao'));
$acl->addResource(new Zend_Acl_Resource('produto'));

Privilégio (privilege)

Um dado recurso pode ter diversas permissões a um determinado papel, estas permissões são, tipicamente, baseadas nas operações que serão executadas. Exemplos destas operações podem ser ações de um controlador, como, por exemplo: “adicionar” e “visualizar”. Este tipo de acesso exigido é facilmente configurado com dois métodos do Zend_Acl: allow() e deny(). Um exemplo permitindo e negando ações do controlador artigos ao papel membro é exibido a seguir:

$acl->allow('membro', 'artigos', 'visualizar');
$acl->deny('membro', 'artigos', 'adicionar');

Após permitir ou negar privilégios é possível verificar se um determinado papel tem acesso a um privilégio com o seguinte código:

if ($acl->isAllowed('member', 'artigos', 'visualizar') {
	echo "acesso permitido";
}

Estes são os principais conceitos e métodos relacionados aos componentes de autorização do Zend Framework, agora é hora de implementar um sistema de autorização integrado com o MVC do framework. Existem diversas formas de se implementar esta funcionalidade, a maneira demonstrada neste artigo é apenas uma delas.

Codificando a Autorização

Após uma introdução sobre os componentes de autorização do Zend Framework é hora de integrar as funcionalidades para aplicar a autorização ao MVC do framework. Antes de mais nada estou partindo do pressuposto de que o leitor possui todos os fontes do artigo Autenticação com Zend Framework, para reaproveitar o código desenvolvido neste artigo.

Banco de Dados e configurações iniciais

O banco de dados do artigo anterior sofreu algumas modificações, agora foi criada uma tabela para armazenar os perfis (papéis) suportados por essa aplicação. Dois perfis de exemplo são criados, o primeiro corresponde ao perfil de administrador, que pode ter acesso a todos os recursos protegidos e o segundo ao perfil de escritor, que pode apenas acessar o controlador de notícias. Um usuário para cada perfil também foi criado. O SQL do banco de dados é apresentado abaixo:

CREATE TABLE `perfil`(
 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
 nome VARCHAR(30)
)ENGINE=InnoDB;
CREATE TABLE `usuario`(
 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
 login VARCHAR(30) NOT NULL UNIQUE,
 senha VARCHAR(60) NOT NULL,
 nome_completo VARCHAR(100) NOT NULL,
 perfil_id INT UNSIGNED NOT NULL,
 FOREIGN KEY(`perfil_id`) REFERENCES `perfil`(`id`)
 ON UPDATE CASCADE
 ON DELETE CASCADE
)ENGINE=InnoDB;
INSERT INTO `perfil`(nome) VALUES ('admin'), ('writer');
INSERT INTO `usuario`(login, senha, nome_completo, perfil_id) VALUES ('admin', SHA1('admin'), 'Administrador', 1), ('escritor', SHA1('escritor'), 'Escritor', 2);

O arquivo de configurações application/configs/application.ini desenvolvido no artigo anterior não sofrerá alterações impactantes, algumas linhas são adicionadas para configurar as namespaces do autoloader do framework e incluir o plugin de autenticação, que cuidará da parte de verificar o usuário logado e suas permissões, implementado mais adiante. Estas linhas podem ser adicionadas logo acima da seção [staging : production]:

autoloaderNamespaces[] = "Aplicacao"
resources.frontController.plugins.auth = "Aplicacao_Plugin_Auth"

A próxima etapa é adicionar ao Bootstrap a inicialização da classe de ACL que irá popular todas as regras de autorização suportadas pela aplicação. Dentro do arquivo application/Bootstrap.php basta adicionar o seguinte método à classe Bootstrap:

protected function _initAcl()
{
	$aclSetup = new Aplicacao_Acl_Setup();
}

No artigo anterior foram criados os controladores: noticias e auth. Agora basta criar o controlador usuarios, que será acessível pelo papel admin, adicionar novas ações a este novo controlador e ao controlador noticias e, por último, adicionar uma nova ação ao controlador error.

zf create controller usuarios
zf create action adicionar noticias
zf create action adicionar usuarios
zf create action forbidden error

A ação forbidden ficará responsável por informar ao usuário que um determinado acesso não foi autorizado pela aplicação. Para exibir esta mensagem ao usuário basta adicionar o seguinte conteúdo ao arquivo views/scripts/error/forbidden.phtml:

<div style="color: #f00;">Voc&ecirc; n&atilde;o est&aacute; autorizado a ver esta p&aacute;gina</div>

Com isso a parte inicial da aplicação está pronta, agora é hora de implementar a classe responsável por popular o componente Zend_Acl.

Populando a ACL

A classe Bootstrap irá inicializar uma classe chamada Aplicacao_Acl_Setup, que ficará responsável por popular todos os dados relacionados a papéis, recursos e privilégios do componente Zend_Acl. Esta classe é apresentada abaixo e deve ser gravada no arquivo library/Aplicacao/Acl/Setup.php.

<?php
class Aplicacao_Acl_Setup
{
	/**
	 * @var Zend_Acl
	 */
	protected $_acl;

	public function __construct()
	{
		$this->_acl = new Zend_Acl();
		$this->_initialize();
	}

	protected function _initialize()
	{
		$this->_setupRoles();
		$this->_setupResources();
		$this->_setupPrivileges();
		$this->_saveAcl();
	}

	protected function _setupRoles()
	{
		$this->_acl->addRole( new Zend_Acl_Role('guest') );
		$this->_acl->addRole( new Zend_Acl_Role('writer'), 'guest' );
		$this->_acl->addRole( new Zend_Acl_Role('admin'), 'writer' );
	}

	protected function _setupResources()
	{
		$this->_acl->addResource( new Zend_Acl_Resource('auth') );
		$this->_acl->addResource( new Zend_Acl_Resource('error') );
		$this->_acl->addResource( new Zend_Acl_Resource('noticias') );
		$this->_acl->addResource( new Zend_Acl_Resource('usuarios') );
	}

	protected function _setupPrivileges()
	{
		$this->_acl->allow( 'guest', 'auth', array('index', 'login') )
				   ->allow( 'guest', 'error', array('error', 'forbidden') );
		$this->_acl->allow( 'writer', 'noticias', array('index', 'adicionar') )
				   ->allow( 'writer', 'auth', 'logout' );
		$this->_acl->allow( 'admin', 'usuarios', array('index', 'adicionar') );
	}

	protected function _saveAcl()
	{
		$registry = Zend_Registry::getInstance();
		$registry->set('acl', $this->_acl);
	}
}

A primeira etapa desta classe é configurar os papéis. Neste caso foram criados os papéis guest, writer e admin. O papel guest foi criado como um facilitador, já que não será utilizado. Pode ocorrer de a aplicação liberar determinados acessos a usuários não-logados, aí que entra este papel. O papel writer terá os mesmos acessos de guest, além de acessos específicos, assim como o papel admin terá os mesmos acessos que guest e writer, além de outras permissões.

Após ter os papéis definidos, são adicionados os recursos protegidos. Neste caso os recursos são os controladores auth, error, noticias e usuarios. A próxima etapa, é definir os privilégios de um determinado papel a um determinado recurso. O papel guest pode acessar ações de erro e a página de login, já o papel writer pode acessar as ações de guest, as ações do controlador de notícias e a ação de logout do controlador auth. Já o papel admin pode acessar o mesmo que os outros dois papéis, além das ações do controlador de usuários.

Por último esta ACL é adicionada ao Zend_Registry, para que em outras partes da aplicação ela esteja acessível e devidamente populada.

Verificação de permissões

Tendo a ACL devidamente configurada, agora será possível verificar as permissões do usuário. Para isso foi criado um plugin que é adicionado ao Zend_Controller_Front no arquivo de configuração definido na primeira etapa das implementações. Este plugin terá como pai a classe Zend_Controller_Plugin_Abstract que possui diversos métodos referentes a cada etapa do processo de despacho das classes do Zend Framework. O método utilizado no plugin é o preDispatch() que é chamado antes de uma ação de controlador ser despachada, o que permite re-configurar a rota caso o usuário não possa ter acesso à página em questão. Este plugin é exibido abaixo e deve estar gravado em library/Aplicacao/Plugin/Auth.php:

<?php
class Aplicacao_Plugin_Auth extends Zend_Controller_Plugin_Abstract
{
	/**
	 * @var Zend_Auth
	 */
	protected $_auth = null;
	/**
	 * @var Zend_Acl
	 */
	protected $_acl = null;
	/**
	 * @var array
	 */
	protected $_notLoggedRoute = array(
		'controller' => 'auth',
		'action'     => 'login',
		'module'     => 'default'
	);
	/**
	 * @var array
	 */
	protected $_forbiddenRoute = array(
		'controller' => 'error',
		'action'     => 'forbidden',
		'module'     => 'default'
	);

	public function __construct()
	{
		$this->_auth = Zend_Auth::getInstance();
		$this->_acl = Zend_Registry::get('acl');
	}

	public function preDispatch(Zend_Controller_Request_Abstract $request)
	{
		$controller = "";
		$action     = "";
		$module     = "";
		if ( !$this->_auth->hasIdentity() ) {
			$controller = $this->_notLoggedRoute['controller'];
			$action     = $this->_notLoggedRoute['action'];
			$module     = $this->_notLoggedRoute['module'];
		} else if ( !$this->_isAuthorized($request->getControllerName(),
					$request->getActionName()) ) {
			$controller = $this->_forbiddenRoute['controller'];
			$action     = $this->_forbiddenRoute['action'];
			$module     = $this->_forbiddenRoute['module'];
		} else {
			$controller = $request->getControllerName();
			$action     = $request->getActionName();
			$module     = $request->getModuleName();
		}
		$request->setControllerName($controller);
		$request->setActionName($action);
		$request->setModuleName($module);
	}

	protected function _isAuthorized($controller, $action)
	{
		$this->_acl = Zend_Registry::get('acl');
		$user = $this->_auth->getIdentity();
		if ( !$this->_acl->has( $controller ) || !$this->_acl->isAllowed( $user, $controller, $action ) )
			return false;
		return true;
	}
}

Primeiramente são definidas as rotas padrão para o caso do usuário não estar logado ou não estar autorizado a ver uma determinada página. Estas rotas são, consecutivamente: auth/login e error/forbidden. No construtor é recuperada a instância atual de Zend_Auth e Zend_Acl, ambas usadas no processo de verificação do usuário logado.

A primeira etapa do método preDispatch() é verificar se o usuário está logado e, caso não esteja, os parâmetros da requisição são configurados para a rota de login. A segunda etapa é o caso do usuário estar logado, nesta etapa o objeto que representa o usuário logado é recuperado e é feita uma verificação se este usuário possui o privilégio correspondente à ação do controlador requisitado. Caso não possua, ou o recurso que representa o controlador não exista, ele é redirecionado para a rota de acesso proibido. Se nenhuma destas verificações negar o acesso ao usuário significa que ele está apto a acessar a requisição em questão, portanto os parâmetros dessa requisição são mantidos e o fluxo prossegue.

Models

Tendo todo o processo de verificação pronto a próxima etapa é criar as classes com regras de negócio da parte de autenticação. Já que, diferente do artigo anterior, este artigo envolve o MVC do Zend Framework, toda a parte de regra de negócio é implementada na camada referente ao Model. A primeira classe criada é a classe que representa um usuário da aplicação. Ela implementará a interface Zend_Acl_Role_Interface, que permite que ela seja tratada como um papel do componente Zend_Acl. Esta classe exige que o método getRoleId() seja implementado e retorne o identificador correspondente ao papel em questão. Esta classe é bem simples e possui apenas métodos getters e setters. Ela deverá estar no arquivo models/Usuario.php.

<?php
class Model_Usuario implements Zend_Acl_Role_Interface
{
	private $_userName;
	private $_roleId;
	private $_fullName;

	public function getUserName()
	{
		return $this->_userName;
	}

	public function setUserName($userName)
	{
		$this->_userName = (string) $userName;
	}

	public function getFullName()
	{
		return $this->_fullName;
	}

	public function setFullName($fullName)
	{
		$this->_fullName = (string) $fullName;
	}
	/**
	 *
	 */
	public function getRoleId()
	{
		return $this->_roleId;
	}

	public function setRoleId($roleId)
	{
		$this->_roleId = (string) $roleId;
	}
}

A próxima etapa é implementar a classe que cuidará da regra de negócio de login. Esta classe terá apenas um método estático, responsável por fazer o login do usuário na aplicação. A principal diferença deste método de login com o apresentado no artigo anterior é a modificação da consulta ao banco de dados, para adicionar a tabela perfil aos dados retornados pelo Zend_Auth_Adapter_DbTable. O método join() de Zend_Db_Select, adicionará a tabela perfil com o alias “p”, onde a coluna perfil_id da tabela usuario seja igual a coluna id da tabela perfil, retornando a coluna  “nome” da tabela “perfil” com o alias “nome_perfil”. Após validar o login, o objeto retornado pelo adapter é mapeado para um objeto do tipo Model_Usuario e, por último, este é armazenado à sessão do Zend Framework. Este model ficará no arquivo models/Auth.php e deve possuir o seguinte conteúdo:

<?php
class Model_Auth
{
	public static function login($login, $senha)
	{
		$dbAdapter = Zend_Db_Table::getDefaultAdapter();
		//Inicia o adaptador Zend_Auth para banco de dados
		$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
		$authAdapter->setTableName('usuario')
					->setIdentityColumn('login')
					->setCredentialColumn('senha')
					->setCredentialTreatment('SHA1(?)');
		//Define os dados para processar o login
		$authAdapter->setIdentity($login)
					->setCredential($senha);
		//Faz inner join dos dados do perfil no SELECT do Auth_Adapter
		$select = $authAdapter->getDbSelect();
		$select->join( array('p' => 'perfil'), 'p.id = usuario.perfil_id', array('nome_perfil' => 'nome') );
		//Efetua o login
		$auth = Zend_Auth::getInstance();
		$result = $auth->authenticate($authAdapter);
		//Verifica se o login foi efetuado com sucesso
		if ( $result->isValid() ) {
			//Recupera o objeto do usuário, sem a senha
			$info = $authAdapter->getResultRowObject(null, 'senha');

			$usuario = new Model_Usuario();
			$usuario->setFullName( $info->nome_completo );
			$usuario->setUserName( $info->login );
			$usuario->setRoleId( $info->nome_perfil );

			$storage = $auth->getStorage();
			$storage->write($usuario);

			return true;
		}
		throw new Exception('Nome de usuário ou senha inválida');
	}
}

Controller de Autenticação e demais Views

Agora que toda a regra de negócio foi delegada à camada Model, o controller de autenticação sofre algumas modificações para utilizar esta classe. Neste caso ele chamará o método estático Model_Auth::login(), e, caso este lance alguma exceção, irá armazenar a mensagem ao helper FlashMessenger e exibí-la em cima do formulário de login. A classe modificada é apresentada abaixo e, logo em seguida, a view correspondente a esta ação de login é apresentada. O formulário de login é o mesmo implementado no antigo anterior.

<?php
class AuthController extends Zend_Controller_Action
{

	public function init()
	{
	}

	public function indexAction()
	{
		return $this->_helper->redirector('login');
	}

	public function loginAction()
	{
		$this->_flashMessenger = $this->_helper->getHelper('FlashMessenger');
		$this->view->messages = $this->_flashMessenger->getMessages();
		$form = new Form_Login();
		$this->view->form = $form;
		//Verifica se existem dados de POST
		if ( $this->getRequest()->isPost() ) {
			$data = $this->getRequest()->getPost();
			//Formulário corretamente preenchido?
			if ( $form->isValid($data) ) {
				$login = $form->getValue('login');
				$senha = $form->getValue('senha');

				try {
					Model_Auth::login($login, $senha);
					//Redireciona para o Controller protegido
					return $this->_helper->redirector->goToRoute( array('controller' => 'noticias'), null, true);
				} catch (Exception $e) {
					//Dados inválidos
					$this->_helper->FlashMessenger($e->getMessage());
					$this->_redirect('/auth/login');
				}
			} else {
				//Formulário preenchido de forma incorreta
				$form->populate($data);
			}
		}
	}

	public function logoutAction()
	{
		$auth = Zend_Auth::getInstance();
		$auth->clearIdentity();
		return $this->_helper->redirector('index');
	}
}
<h2>Login</h2>
<?php echo ( sizeof( $this->messages ) > 0 ) ? '<div style="color: #f00;">' . $this->messages[0] . '</div>' : ""; ?>
<?php echo $this->form; ?>

A última etapa é modificar as views dos controladores noticias e usuarios. Estas views são extremamente simples e irão conter apenas uma mensagem de boas-vindas e um link para logout. O arquivo views/scripts/noticias/index.phtml deve conter o seguinte conteúdo:

<h2>Bem-vindo Escritor</h2>
<a href="<?php echo $this->url(array('controller' => 'auth', 'action' => 'logout', 'module' => 'default'), '', true); ?>">
	Logout
</a>

E, por último, o arquivo views/scripts/usuarios/index.phtml possui o seguinte conteúdo:

<h2>Bem-vindo Admin</h2>
<a href="<?php echo $this->url(array('controller' => 'auth', 'action' => 'logout', 'module' => 'default'), '', true); ?>">
	Logout
</a>

Funcionamento

Após todas as etapas estarem concluídas a aplicação está devidamente protegida, tanto na questão de permitir apenas usuários logados, como em autorizar aos usuários apenas alguns privilégios baseados em seu perfil. Para o meu caso a URL da aplicação ficou apontada para: http://localhost/zend_acl, e qualquer tentativa de acessar controllers internos são redirecionadas à pagina de login. Após o login,  a aplicação permite ou proíbe o acesso, de acordo com o perfil logado.  Abaixo são apresentadas algumas screenshots de cada parte funcional desta aplicação:

Erro ao efetuar login

Escritor logado com sucesso, acessando o controlador noticias

Tentativa de acesso ao controlador usuarios, utilizando o perfil escritor

Acesso ao controlador usuarios, usando o perfil admin

Conclusão

Os componentes do Zend Framework relacionados à autorização apresentam uma API consistente e intuitiva de se utilizar e configurar, o que garante uma baixa curva de aprendizagem para se utilizar as funcionalidades básicas do mesmo. Estes componentes podem ser integrados a todo o processo de despacho da parte MVC do framework através de plugins, o que permite manipular as requisições livremente e, baseado em determinados critérios, redirecionar o usuário para outros pontos da aplicação para informar mensagens de erro ou exigir algum formulário para entrada de dados. O objetivo deste artigo foi demonstrar de forma prática uma maneira de implementar autorização e integrá-la ao MVC, porém, vale ressaltar, que existem diversas formas de se chegar a esta funcionalidade, para maiores informações a seção de referências logo abaixo pode ser de grande valia. No próximo artigo os componentes de Internacionalização e Localização serão abordados, até a próxima pessoal!

Faça o download do código-fonte.

Referências

45 thoughts on “Autorização com Zend Framework

  1. como seria para a aplicação recuperar as “roles”, “resources” e “privileges” do banco, para que possam ser criados e controlados, perfis pela aplicação?

    • Existem várias formas para se obter esta funcionalidade, você deve manter cada mapeamento para os recursos protegidos e privilégios no banco de dados e em um plugin da aplicação obter estes recursos para verificar se o usuário logado possui acesso. No quesito performance você pode armazenar um objeto Zend_Acl em cache para não ficar consultando o banco de dados a cada requisição que sua aplicação receber.

  2. bom dia fernando, o post esta otimo, porém com um probleminha besta, mas que preciso muito, não estou conseguindo retornar a instancia do Zend_Auth, como por exemplo o “nome_completo”, ja utilizei o codigo que vc mostrou no “Autenticação com ZF” mas não deu certo, quando aplico o metodo”Zend_Auth::getInstance()->getIdentity()” da tela branca e não aparece erro nenhum, eu sei que é besteira, mas to precisando, rsrsrs…

  3. eu consegui retornar o objeto com o “getIdentity()” so que nao consigo manioular este objeto, como, fazer comparacoes com a sessao do ususario, ou armazenar no bd o ultimo usuario que alterou “tal” registro, essas coisas de log, ja tentei converter para um array mas nao funcionou, com o “->toArray()”, me da uma luz no fim do tunel, to travado nessa parte, rsrs…

    Obrigado!!

    • Olá Rafael, não entendi direito onde está o problema mas vou tentar te ajudar.
      1 – O seu objeto retornado é uma instância de Model_Usuario?
      2 – Como deseja fazer comparações com a sessão do usuário?
      3 – Se deseja gravar no banco de dados existem algumas considerações, a primeira é verificar a possibilidade de serializar o objeto, e, em caso negativo, armazenar apenas o id do usuário em sua tabela de log.

      Att.

  4. isso, o objeto retornado é uma instancia de Model_usuario. as comparacoes seriam da seguinte forma, estou desenvolvendo um sistema de OS, e o usuario quando logar no sistema precisa visualizar suas pendencias, essas coisas. a questao do log é isso mesmo salvar data, hora e o usuario(id) que fez a alteracao. quando retornei o objeto, transformei em array, so que os indices do array vem com uns erros, como aqueles pontos de interrogação de caracteres especiais html. ja consigo visualizar o array, mas somente com o print_r na variavel do array, mas se vc puder me ajudar de outra forma nao sei uma mais simples, ou que funcione(rsrs), te agradeco muito.

    Obs.: retorno do objeto = Model_Usuario Object ( [_userName:Model_Usuario:private] => admin [_roleId:Model_Usuario:private] => admin [_fullName:Model_Usuario:private] => Administrador )

    retorno do array = Array ( [�Model_Usuario�_userName] => admin [�Model_Usuario�_roleId] => admin [�Model_Usuario�_fullName] => Administrador )

  5. vc poderia me mostrar como seria, pq eu instancio o model_usuario, e depois chamo a funcao getFullName(), por exemplo, mas quando do o print na variavel ela nao aparece nada, desculpa se isso q estou tentando fazer e besteira, mas sou estagiario, e nao tenho muita experiencia.

  6. Muito bom o artigo Fernando. Parabéns!
    Me ajudou muito! Iniciei um trabalho na faculdade de projetar um sistema completo de gerênciamento de uma biblioteca e não sabia como desenvolver as permissões no zend. Agora está muito mais claro!
    Ótimo post!
    Abraço.

  7. Fernando você tem MSN pra eu te perguntar algumas coisas? Estou meio perdido e gostaria de uma ajuda!
    Meu MSN é o que está no campo email aqui do comentário.
    Abraço.

  8. Cara muito bom. Bem bacana a explicação e o exemplo. Teria como ao inves de utilizar o DBTable do Zend, substitui-lo por um ORM como o Doctrine 1.2 e modificar somente os modelos, para a autenticação e autorização funcionar? flw e ate mais.

  9. Bem voltando ao assunto da autenticação com o Zend e Doctrine, após analizar o tutorial do Zend Cast, pesquisei mais a fundo na internet e encontrei uma implementação no ZendX que faz justamente o que eu estava precisando. Ele cria um adapter aos moldes do Zend_DB_Table, ficando muito semelhante (identico). Bom era isso ai quem quiser conferir:

    http://framework.zend.com/svn/framework/extras/incubator/library/ZendX/Doctrine/Auth/Adapter.php

    http://framework.zend.com/wiki/pages/viewpage.action?pageId=3866950

    Obrigado pela dica e me salvou a semana de novo, hehe.

  10. Amigo, não existe possibilidade de disponibilizar o tutorial. Porque da forma em que foi explicado, não consegui fazer o sistema rodar. Não tem como vc fazer um tutorial de ACL baseado no tutorial Zend_Auth do Akrabat? Ou então fazer um passo a passo.

  11. Olá Fernando,

    Show de bola seu blog, seus tutoriais tbem! Tenho algumas dúvidas:

    Quando faço o login sem definir o papel, qual papel ele pega automaticamente?

    Como faço para permitir tudo digamos ao usuário guest, menos a action Y do controller X, pq fiz o seguinte no setup.php

    $this->_acl->allow(‘guest’)
    ->deny(‘guest’, ‘X’, array(‘Y’));

    E não deu certo, segue meu setup.php http://pastebin.com/KLWu1A2i

    Outra coisa, será que realmente preciso da ACL, pois no meu caso só vou usar 2 tipos, o visitante e o membro?

    Obrigado.

    • Acredito que a forma mais elegante de você resolver isso é realmente utilizando Zend_Acl. Com relação ao papel guest, você terá que verificar no Auth.php se existe um papel e um usuário e em caso contrário assumir como o guest.
      No caso da permissão do guest era para estar funcionando dessa forma que você implementou, vou dar uma analisada melhor e te envio uma resposta.

      Att.

      • Onde dou um LIKE aqui?!?! ehhehe… Parabéns pelas aulas, mto boas e produtivas, cheias de best practices.

        Você estava falando com o Diogo, sobre o usuário guest. Tive o mesmo “problema” e resolvi assim:

        seguindo o artigo, em library/Aplicacao/Plugin/Auth.php (classe Aplicacao_Plugin_Auth) adicionei ao método construtor da classe o seguinte trecho de código:

        public function __construct()
        {
        $this->_auth = Zend_Auth::getInstance();
        $this->_acl = Zend_Registry::get(‘acl’);

        if ($this->_auth->getIdentity() == NULL){
        $auth = Zend_Auth::getInstance();
        $usuario = new Application_Model_Usuario();
        $usuario->setFullName( “Convidado” );
        $usuario->setUserName( “Convidado” );
        $usuario->setRoleId( “guest” );

        $storage = $auth->getStorage();
        $storage->write($usuario);
        }
        }

        Dessa forma se não estiver logado o usuário sempre será o guest.

        #ficaadica

        Abraços

        RMM – esperando ansiosamente pelo o próximo artigo…

  12. Opa, mais uma coisa

    Não sei se e você chegou a testar com o zf layout ativado, pois quando ativo ele não funciona perfeitamente, parece que faz 3 request, to vendo como posso consertar possívelmente o problema é no Auth.php no preDispatch, depois se vc tiver tempo da uma testada no zf layout.

    Obrigado novamente :)

  13. Olá Fernando,
    Ótimo artigo. Só estou com um problema. Meu sistema foi modelado para 1 usuário ter N roles, isso implica numa pequena mudança no código que vc fez.

    Não cheguei a testar o seu código, mas essa parte realmente funciona?

    protected function _isAuthorized($controller, $action)
    {
    $this->_acl = Zend_Registry::get(‘acl’);
    $user = $this->_auth->getIdentity();
    if ( !$this->_acl->has( $controller ) || !$this->_acl->isAllowed( $user, $controller, $action ) )
    return false;
    return true;
    }

    Acho que poderá dar um erro porque o ->isAllowed() aceita o role como string somente e o seu gera um objeto da Model_Usuario e também não pega só o Role do User e sim o objeto por completo.

    Valeu!

      • Sim.. testei aqui depois e funciona mesmo.
        Só tem um problema: Se o meu User ter N roles isso não deve funcionar né? Já que se fizer um “User implements Zend_Acl_Role_Interface” o método getRoleId() só irá retornar uma string e não um array com todas as permissões.

        Resumindo: Há uma forma de implementar a Zend_Acl num model onde o User possa ter N roles (Writer, Editor, Publisher, etc…)?

        Muito obrigado.

        • Se no seu caso de vários papéis não houver a possibilidade de se fazer uma hierarquia com papéis pais e filhos, o que facilitaria muito, você poderia experimentar o mecanismo de assertion suportado pelo Zend_Acl. Este mecanismo permite que você faça validações customizadas, então seu objeto de usuário pode retornar todos os papéis e você fazer validações no mecanismo dentro de sua classe de assertion. Para maiores informações veja: http://framework.zend.com/manual/en/zend.acl.advanced.html.

          Caso isso ainda não resolva, você precisará implementar uma customização do Zend_Acl. Att.

  14. Fernando,

    Implementei seu exemplo aqui direitinho, com os dados vindo do DB com Doctrine ainda.. funcionou belezinha.

    Agora uma pergunta não tão relacionado ao post, mas acho que o pessoal deve querer saber também:
    Qual é a melhor maneira de gerar um menu & navegação baseados nas permissoes que o usuário logado possui?
    Exemplo: Se o usuário é administrador, vê tudo, mas se for writer, por exemplo, só irá ver a parte de postagem de notícias (link de add novo post e ver posts já cadastrados).

    Teria como explicar como integrar aos links das views e ao componente Zend_Navigation?

    Valeu!

  15. Boa tarde brother, blz?

    Seguinte fiz o exemplo completo rodou uma blz, consigo logar e tal… mais quando logo com qualquer perfil, quando entro no /usuario/index, com o perfil de admin, ele não deixa… dando a msg de não autorizado:

    “Você não está autorizado a ver esta página”

    será que caguei o pau em algum lugar ai ?

    Caso queira me ajudar via gtalk: alissondeveloper@gmail.com abraços e aguardo seu favor!

  16. Muito legal seu blog, achei muito legal a forma como modulariza quando na arquitetura mvc, segui este seu exemplo (antes segui o indicado no topo deste) porem não consegui fazer funcionar direito, os problemas são: quando logo como admin cai na pagina bem vindo escritor, quando tento fazer logout ele cai no view do forbidden, creio q segui os passos corretamente se houver como posso lhe enviar a exemplo por email – estou usando xammp + mysql + php 5.3. + zend fw 1.11, desde de ja agradeço e otimo espaço de compartilhamento.

  17. Fernando,
    Muito Bom o Artigo!

    Tenho uma dúvida,
    Eu gostaria de fazer que cada usuário fosse “único”,
    setar permissões diferentes para cada usuário e não há um perfil somente, vindo do banco de dados.

    Como eu poderia resolver essa questão?

  18. Fernando,

    Estou com problema na hora de escrever no Storage.
    É como se o Zend_Auth_Storage_Session não aceitasse objeto como parametro.

    Como resolvo isso?

    Linha:
    $objAuth->getStorage()->write($user);

    Erro:
    Catchable fatal error: Object of class __PHP_Incomplete_Class could not be converted to string in C:\Program Files\Zend\ZendServer\share\ZendFramework\library\Zend\Acl\Role\Registry.php on line 124

  19. quando tento acessar a aplicação retorna o erro: “Catchable fatal error: Object of class stdClass could not be converted to string in C:\xampp\htdocs\siscot\library\Zend\Acl\Role\Registry.php on line 124″. O que você que pode estar acontecendo?

  20. Ótimos post, porém aqui ta dando o seguinte erro:
    Fatal error: Uncaught exception ‘Zend_Exception’ with message ‘No entry is registered for key ‘acl” in C:\inetpub\tecnosoft.com\library\Zend\Registry.php:147 Stack trace: #0 C:\inetpub\tecnosoft.com\library\Aplicacao\Plugin\Auth.php(31): Zend_Registry::get(‘acl’) #1

    Pelo que entendi não está sendo salvo os papéis na zend_registry, o que será que eu fiz de errado? desde já obrigado.

      • Esse problema eu resolvi acontece que eu estava chamando o _initAcl depois do autoloader, ai mudei pra chamar antes do autoloader e deu tudo certo, porém só fica exibindo a página de Login agora, deve ser por causa do Zend_layout que eu tenho ativado neh? como eu faço pra resolver isso, e outra coisa todos os _init que eu for criar no bootstrap devem ser chamados antes do autoloader ou só o acl? vlw

      • Na verdade o que está acontecendo é que só quando eu faço login que consegue acessar as páginas comuns do site, deve ser alguma coisa na permissão mas não consigo resolver vc sabe como eu faço isso fernando? e outra pergunta o Role “guest” é executado automaticamente quando não tem outro role ativo? vlw desde já

  21. Olá.. muito bom o post, já implementei e ficou bacana, porém me deparei com um “problema”, na verdade não chega a ser um problema, mas enfim, como seria a melhor forma de implementar a recuperação de senha?
    - adicionar permissão em Plugin/Auth.php parra acessar um controller pass/lost por exemplo?

Deixe um Comentário

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*


2 + = 3

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>