Introducción
Este ejemplo es de un foro (¡que original!) uso Zend_Controller con estructura modular, también tengo un pequeño manual al respecto. A continuación la estructura de directorios.
application/ <--- dir de aplicaciones bootstrap.php default/ <--- modulo que se muestra de forma predeterminada controllers/ IndexController.php <--- página de inicio PostController.php <--- restringido a usuarios models/ views/ admin/ <--- modulo de administración controllers/ IndexController.php ... models/ views/ auth/ <--- modulo del autenticación Zend_Auth controllers/ IndexController.php loginController.php logoutController.php ... models/ views/ html/ <--- directorio público, es decir el que se ve en internet index.php <--- sólo tiene una línea que llama al bootstrap.php library/ <--- Librerias, clases y esas cosas My/ Acl.php <--- lista de control de acceso AuthorizationPlugin.php <--- plugin de autorización Zend/ <-- contiene las librerias de Zend Framework
En el módulo default se muestra la página predeterminada, es decir "el index" raiz, este tiene la página de inicio y post donde los usuarios podrán enviar sus comentarios y el acceso a este requiere autorización. El módulo admin será donde se administren los comentarios, ejemplo borrar, autorizar publicación, etc. todo este módulo requiere autorización. Por ultimo el módulo auth es donde se hace la autenticación y al igual que "el index" todos tiene acceso él, en este modulo estará un sistema de login y logout, por lo menos, pero yo ahí pongo la página para registrarse como nuevo usuario y recordar contraseñas. Pero intento mantener el ejemplo lo mas simple.
Autorización
Lista de control de acceso
Primero creamos una ACL en el archivo library/My/Acl.php
addRole(new Zend_Acl_Role('guest') );
$this->addRole(new Zend_Acl_Role('user'), 'guest' );
$this->addRole(new Zend_Acl_Role('admin'));
// Recursos de lo general a lo particular
$this->add(new Zend_Acl_Resource('default'));
$this->add(new Zend_Acl_Resource('post'), 'default');
$this->add(new Zend_Acl_Resource('auth'));
$this->add(new Zend_Acl_Resource('admin'));
// Asignar permisos
// guest
$this->allow('guest', array('default', 'auth') );
$this->deny('guest', array('post', 'admin') );
// user
$this->allow('user', array('post') );
// admin
$this->allow('admin');
}
}
Líneas 11 a 13 crea los roles que se le asignan a los usuarios; guest es el invitado, cualquier persona que entra a la página; user es el usuario registrado, hereda los permisos de guest y tiene permiso de entrar a recurso post (línea 26); por último el role admin tiene permiso de entrar a toda la página (línea 28).
Para asignar recursos la documentación de Zend_Acl recomienda ir de lo general a lo particular, es decir primero los modulos, luego los controladores y por último las acciones. Esto lo hago de las líneas 16 a 19 el caso particular es en la línea 17 pues quiero restringir el acceso al controlador post del modulo default.
Los permisos se asignan en las líneas 23 a 28. Con la particularidad de que en la línea 28 se le da permiso a admin de todo.
Authorization Plugin
Quiero hacer la autorización en cada click de cada página, para eso escribo un Controllers Plugins de Zend Framework en library/My/AuthorizationPlugin.php:
_auth = $auth;
$this->_acl = $acl;
}
public function preDispatch ( Zend_Controller_Request_Abstract $request )
{
// revisa que exista una identidad
// obtengo la identidad y el "role" del usuario, sino tiene le pone 'guest'
$role = $this->_auth->hasIdentity() ? $this->_auth->getInstance()->getIdentity()->role : 'guest';
// toma el nombre del recurso actual
if( $this->_acl->has( $this->getRequest()->getActionName() ) )
$resource = $this->getRequest()->getActionName();
elseif( $this->_acl->has( $this->getRequest()->getControllerName() ) )
$resource = $this->getRequest()->getControllerName();
elseif( $this->_acl->has( $this->getRequest()->getModuleName() ) )
$resource = $this->getRequest()->getModuleName();
// Si, la persona no pasa la prueba de autorización y su "role" es 'guest'
// entonces no ha echo "login" y lo dirigo al controlador "login" del modulo "auth"
if ( !$this->_acl->isAllowed($role, $resource) && $role == 'guest' )
{
$request->setModuleName('auth');
$request->setControllerName('index');
}
// Ahora si la persona tiene un "role" distinto de 'guest' y aun así no pasa
// la prueba de identificación lo mando a una página de error.
elseif (!$this->_acl->isAllowed($role, $resource) )
{
$request->setModuleName('auth');
$request->setControllerName('error');
}
}
}
Este plugin recibe un objeto de Zend_Auth (línea 13) y uno de Zend_Acl (línea 14) que es el definido arriba. En la línea 21 le pregunto al objeto Zend_Auth el role del usuario y las líneas 24 a 29 le pregunto a Zend_Acl si el nombre del modulo, controlador o acción estan en la ACL y si lo esta lo toma como recurso.
En la línea 33 le pregunto a Zend_Acl si el role tiene acceso al recurso, si falla y ademas el role es guest lo mando a la página de login. La línea 40 es un extra, pues si falla la autorización lo manda a una página de error.
Autenticación (Zend_Auth)
La siguiente sección esta basada en el artículo Tutorial: Getting Started with Zend_Auth
Primero en mi base de datos debe haber una tabla para usuarios con al menos las siguientes caracteristicas:
> Describe useres; +-------+-------------------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------------------------+------+-----+---------+----------------+ | id | int(4) unsigned | NO | PRI | NULL | auto_increment | | lvl | enum('user','editor','admin') | NO | | 0 | | | user | varchar(60) | NO | UNI | | | | psw | varchar(60) | NO | | | | | name | varchar(60) | NO | | | | +-------+-------------------------------+------+-----+---------+----------------+
Uso MySQL pero no ha de ser muy distinto con otros servidores.
Modulo Autenticación
Antes que nada, la autenticación bien se puede programar toda en un controlador del modulo predeterminado, pero a mi me gusta separarlo del resto, porque conceptualmente hablando para mi debe ser independiente del sitio web.
Así empiezo por crear los archivos:
application/auth/controllers/IndexController.php
_redirect('/');
}
}
application/auth/controllers/loginController.php
view->title = $this->view->translate->_("Login system");
$config = new Zend_Config(require '../application/auth/configuration/authForm.php');
$this->view->form = $form;
if ( $this->_request->isPost() )
{
$data = $this->_request->getPost();
if ( $form->isValid($data) )
{
// lee la configuración de la base de datos
// iniciada en el bootstrap.php
$db = Zend_Registry::get('db');
// Le digo a Zend_Auth como leer la base de datos
$authAdapter = new Zend_Auth_Adapter_DbTable($db);
// selecciono la tabla
$authAdapter->setTableName('users');
// las identidades se leerán de la columna user
$authAdapter->setIdentityColumn('user');
// Las credenciales de la columna password
$authAdapter->setCredentialColumn('password');
// Ahora tomo los valores del formulario y los paso a Zend_Auth
$authAdapter->setIdentity( $form->getValue('username') );
$authAdapter->setCredential( md5( $form->getValue('password') ) );
// Este paso no me queda claro XD pero es
// La ¡¡verdadera Autenticación!!
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid())
{ // Si la autenticación es valida
// Leo los valores guardado en $authAdapter y
// los guardo en una variable de sesión
$data = $authAdapter->getResultRowObject();
$auth->getStorage()->write($data);
$this->_redirect('/registration');
} else {
// En caso de que no sea valido
// envío un mensaje de error.
$this->view->message = 'Login or password incorrect.';
}
} // termina: if ( $form->isValid($data) )
} // termina: if ( $this->_request->isPost() )
} // termina: indexAction()
}
¡Paren de leer aquí! lo que sigue esta mal escrito e incompleto.
Editar el bootstrap.php
En algún lugar antes de iniciar el FrontController ponemos:
...
// toma los valores de Zend_Auth, aun no vemos esto no desesperes
$auth = Zend_Auth::getInstance();
// inicia nuestra lista de control de accesos
$acl = new My_Acl();
...
// setup FrontController
$front = Zend_Controller_Front::getInstance();
...
Luego despues de inicar el FrontController y configurarlo debemos registrar nuestro plugin (AuthPlugin.php) de la siguiente forma:
...
// setup FrontController
$front = Zend_Controller_Front::getInstance();
...// por lo general aqui va mas configuración de tipo $front->
$front->registerPlugin(new My_AuthPlugin($auth, $acl));
...
En este punto todo debe funcionar! pero.. sólo que el sitio no tiene ninguna forma de iniciar sesión (login) siendo que esto es el verdadero sistema de autenticación.
Zend_Auth y la verdadera autenticación
Esta es la página de login abajo esta el código, pero pueden encontrar una muy buena explicación en http://akrabat.com/zend-auth-tutorial/
_redirect('/index');
}
function loginAction()
{
// ¡Cielos! esta parte no la explico. pero es el formulario para login
$config = new Zend_Config_Ini('../application/config/authForms.ini', 'login');
$form = new Zend_Form($config->form);
$form->setAction( $this->view->baseUrl()
. '/' . $this->getRequest()->getControllerName()
. '/' . $this->getRequest()->getActionName() );
$this->view->form = $form;
// si ya se envio el formulario por medio de post
if ( $this->_request->isPost() )
{
$data = $this->_request->getPost();
// revisa que los datos sean validos y si los son
if ( $form->isValid($data) )
{
$db = Zend_Registry::get('db'); // lee la configuracion de la base datos
// ¡¡por fin!! usamos Zend_Auth
// lo inciamos
$authAdapter = new Zend_Auth_Adapter_DbTable($db);
$authAdapter->setTableName('users'); // selecciono la tabla
$authAdapter->setIdentityColumn('user'); // columna de identidad
$authAdapter->setCredentialColumn('psw'); // columna de credenciales
// pone las credenciales tomadas del formulario
$authAdapter->setIdentity( $form->getValue('username') );
$authAdapter->setCredential( md5( $form->getValue('password') ) );
// hace la autenticacion
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid())
{
// si es valido guarda los datos
$data = $authAdapter->getResultRowObject();
$auth->getStorage()->write($data);
$this->_redirect('/registro');
} else {
// si falla no guarda nada y manda el mensaje de error
$this->view->message = 'Nombre de usuario o contraseña incorrectos.';
}
}
}
}// fin loginAction()
function logoutAction()
{
Zend_Auth::getInstance()->clearIdentity();
$this->_redirect('/index');
}
}
Hola! Felicitaciones, muy claros los ejemplos, espero que aparezcan mas articulos ya que es escasa la informacion de Zend Framework en nuestro querido idioma.
Genial, el ejemplo me sirve tanto para lo que necesito como para comenzar a aprender el funcionamiento de ZF.
Espero aprender pronto y colaborar con mis investigaciones personales 😀
Gracias 😉
Hola, hace unas horas realice un Post de Zend Mail, si les interesa:
http://matydesign.com/2009/06/30/enviar-mail-con-zend-mail/
Saludos… Maty
Hola. Felicitaciones por el ejemplo, pues me quedo muy claro esto de ZF.
Gracias
Disculpa, soy nuevo en esto, no pusiste el codigo de authForm.php?
Lo necesita en el Auth_loginController no?
Gracias
Gracias por los ejemplos, saludos.