TigerZF
🌐Español

Capítulo 12. Zend_Acl

12.1. Introducción

Zend_Acl proporciona una implementación ligera y flexible de listas de control de acceso (ACL) para la gestión de privilegios. En general, una aplicación puede utilizar dichas ACL para controlar el acceso a determinados objetos protegidos por parte de otros objetos solicitantes.

Para efectos de esta documentación:

  • un recurso es un objeto al que se controla el acceso.

  • un rol es un objeto que puede solicitar acceso a un recurso.

Dicho de manera sencilla, los roles solicitan acceso a los recursos. Por ejemplo, si un encargado de estacionamiento solicita acceso a un automóvil, entonces el encargado de estacionamiento es el rol solicitante, y el automóvil es el recurso, ya que no a todos se les puede conceder acceso al automóvil.

Mediante la especificación y el uso de una ACL, una aplicación puede controlar cómo se conceden a los roles el acceso a los recursos.

12.1.1. Recursos

Crear un recurso en Zend_Acl es muy sencillo. Zend_Acl proporciona el recurso Zend_Acl_Resource_Interface para facilitar la creación de recursos en una aplicación. Una clase solo necesita implementar esta interfaz, que consta de un único método, getResourceId(), para que Zend_Acl reconozca al objeto como un recurso. Adicionalmente, Zend_Acl_Resource es proporcionado por Zend_Acl como una implementación básica de recurso para que los desarrolladores la extiendan según lo necesiten.

Zend_Acl proporciona una estructura de árbol a la que se pueden añadir múltiples recursos. Dado que los recursos se almacenan en dicha estructura de árbol, pueden organizarse desde lo general (hacia la raíz del árbol) hasta lo específico (hacia las hojas del árbol). Las consultas sobre un recurso específico buscarán automáticamente en la jerarquía del recurso las reglas asignadas a los recursos ancestros, permitiendo una sencilla herencia de reglas. Por ejemplo, si se desea aplicar una regla predeterminada a cada edificio de una ciudad, bastaría con asignar la regla a la ciudad, en lugar de asignar la misma regla a cada edificio. Sin embargo, algunos edificios pueden requerir excepciones a dicha regla, y esto puede lograrse en Zend_Acl asignando dichas reglas de excepción a cada edificio que las requiera. Un recurso puede heredar de un único recurso padre, aunque este recurso padre puede tener a su vez su propio recurso padre, etc.

Zend_Acl también admite privilegios sobre los recursos (por ejemplo, "create", "read", "update", "delete"), de modo que el desarrollador puede asignar reglas que afecten a todos los privilegios o a privilegios específicos sobre uno o más recursos.

12.1.2. Roles

Al igual que con los recursos, crear un rol también es muy sencillo. Todos los roles deben implementar Zend_Acl_Role_Interface. Esta interfaz consta de un único método, getRoleId(). Adicionalmente, Zend_Acl_Role es proporcionado por Zend_Acl como una implementación básica de rol para que los desarrolladores la extiendan según lo necesiten.

En Zend_Acl, un rol puede heredar de uno o más roles. Esto es para admitir la herencia de reglas entre roles. Por ejemplo, un rol de usuario, como "sally", puede pertenecer a uno o más roles padres, como "editor" y "administrator". El desarrollador puede asignar reglas a "editor" y "administrator" por separado, y "sally" heredaría dichas reglas de ambos, sin necesidad de asignar reglas directamente a "sally".

Aunque la capacidad de heredar de múltiples roles es muy útil, la herencia múltiple también introduce cierto grado de complejidad. El siguiente ejemplo ilustra la condición de ambigüedad y cómo Zend_Acl la resuelve.

Ejemplo 12.1. Herencia múltiple entre roles

El siguiente código define tres roles base - "guest", "member" y "admin" - de los cuales otros roles pueden heredar. Luego, se establece un rol identificado como "someUser" que hereda de los otros tres roles. El orden en que estos roles aparecen en el array $parents es importante. Cuando sea necesario, Zend_Acl busca reglas de acceso definidas no solo para el rol consultado (en este caso, "someUser"), sino también para los roles de los que el rol consultado hereda (en este caso, "guest", "member" y "admin"):

$acl = new Zend_Acl();

$acl->addRole(new Zend_Acl_Role('guest'))
    ->addRole(new Zend_Acl_Role('member'))
    ->addRole(new Zend_Acl_Role('admin'));

$parents = array('guest', 'member', 'admin');
$acl->addRole(new Zend_Acl_Role('someUser'), $parents);

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

$acl->deny('guest', 'someResource');
$acl->allow('member', 'someResource');

echo $acl->isAllowed('someUser', 'someResource') ? 'allowed' : 'denied';

Dado que no hay ninguna regla definida específicamente para el rol "someUser" y "someResource", Zend_Acl debe buscar reglas que puedan estar definidas para los roles de los que "someUser" hereda. Primero se visita el rol "admin", y no hay ninguna regla de acceso definida para él. A continuación se visita el rol "member", y Zend_Acl encuentra que hay una regla que especifica que a "member" se le permite el acceso a "someResource".

Si Zend_Acl continuara examinando las reglas definidas para otros roles padres, sin embargo, encontraría que a "guest" se le deniega el acceso a "someResource". Este hecho introduce una ambigüedad porque ahora a "someUser" se le deniega y se le permite el acceso a "someResource" a la vez, por haber heredado reglas conflictivas de diferentes roles padres.

Zend_Acl resuelve esta ambigüedad completando una consulta cuando encuentra la primera regla que es directamente aplicable a la consulta. En este caso, dado que el rol "member" se examina antes que el rol "guest", el código de ejemplo imprimiría "allowed".


[Note] Nota

Al especificar múltiples padres para un rol, tenga en cuenta que el último padre indicado es el primero en ser buscado para las reglas aplicables a una consulta de autorización.

12.1.3. Creación de la lista de control de acceso

Una lista de control de acceso (ACL) puede representar cualquier conjunto de objetos físicos o virtuales que se desee. Sin embargo, para efectos de esta demostración, crearemos una ACL básica para un sistema de gestión de contenidos (CMS) que mantiene varios niveles de grupos sobre una amplia variedad de áreas. Para crear un nuevo objeto ACL, instanciamos la ACL sin parámetros:

$acl = new Zend_Acl();
[Note] Nota

Hasta que un desarrollador especifica una regla "allow", Zend_Acl deniega el acceso a todos los privilegios sobre todos los recursos por parte de todos los roles.

12.1.4. Registro de roles

Los CMS casi siempre requerirán una jerarquía de permisos para determinar las capacidades de autoría de sus usuarios. Puede haber un grupo 'Guest' que permita un acceso limitado para demostraciones, un grupo 'Staff' para la mayoría de los usuarios del CMS que realizan la mayor parte de las operaciones cotidianas, un grupo 'Editor' para los responsables de publicar, revisar, archivar y eliminar contenido, y finalmente un grupo 'Administrator' cuyas tareas pueden incluir todas las de los demás grupos además del mantenimiento de información sensible, la gestión de usuarios, los datos de configuración internos, las copias de seguridad y la exportación. Este conjunto de permisos puede representarse en un registro de roles, permitiendo que cada grupo herede privilegios de los grupos 'padres', así como proporcionando privilegios distintos exclusivos para su propio grupo. Los permisos pueden expresarse de la siguiente manera:

Tabla 12.1. Controles de acceso para un CMS de ejemplo

Nombre Permisos únicos Hereda permisos de
Guest View N/A
Staff Edit, Submit, Revise Guest
Editor Publish, Archive, Delete Staff
Administrator (Se conceden todos los accesos) N/A

Para este ejemplo, se utiliza Zend_Acl_Role, pero cualquier objeto que implemente Zend_Acl_Role_Interface es aceptable. Estos grupos pueden añadirse al registro de roles de la siguiente manera:

$acl = new Zend_Acl();

// Add groups to the Role registry using Zend_Acl_Role
// Guest does not inherit access controls
$roleGuest = new Zend_Acl_Role('guest');
$acl->addRole($roleGuest);

// Staff inherits from guest
$acl->addRole(new Zend_Acl_Role('staff'), $roleGuest);

/*
Alternatively, the above could be written:
$acl->addRole(new Zend_Acl_Role('staff'), 'guest');
*/

// Editor inherits from staff
$acl->addRole(new Zend_Acl_Role('editor'), 'staff');

// Administrator does not inherit access controls
$acl->addRole(new Zend_Acl_Role('administrator'));

12.1.5. Definición de controles de acceso

Ahora que la ACL contiene los roles relevantes, se pueden establecer reglas que definan cómo pueden acceder los roles a los recursos. Puede que haya notado que no hemos definido ningún recurso en particular para este ejemplo, lo cual se ha simplificado para ilustrar que las reglas se aplican a todos los recursos. Zend_Acl proporciona una implementación mediante la cual las reglas solo necesitan asignarse de lo general a lo específico, minimizando el número de reglas necesarias, ya que los recursos y los roles heredan las reglas definidas en sus ancestros.

[Note] Nota

En general, Zend_Acl obedece una regla dada si y solo si no se aplica una regla más específica.

En consecuencia, podemos definir un conjunto razonablemente complejo de reglas con una cantidad mínima de código. Para aplicar los permisos base definidos anteriormente:

$acl = new Zend_Acl();

$roleGuest = new Zend_Acl_Role('guest');
$acl->addRole($roleGuest);
$acl->addRole(new Zend_Acl_Role('staff'), $roleGuest);
$acl->addRole(new Zend_Acl_Role('editor'), 'staff');
$acl->addRole(new Zend_Acl_Role('administrator'));

// Guest may only view content
$acl->allow($roleGuest, null, 'view');

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

// Staff inherits view privilege from guest, but also needs additional
// privileges
$acl->allow('staff', null, array('edit', 'submit', 'revise'));

// Editor inherits view, edit, submit, and revise privileges from
// staff, but also needs additional privileges
$acl->allow('editor', null, array('publish', 'archive', 'delete'));

// Administrator inherits nothing, but is allowed all privileges
$acl->allow('administrator');

Los valores NULL en las llamadas a allow() anteriores se utilizan para indicar que las reglas de permiso se aplican a todos los recursos.

12.1.6. Consultar una ACL

Ahora tenemos una ACL flexible que puede utilizarse para determinar si los solicitantes tienen permiso para realizar funciones en toda la aplicación web. Realizar consultas es bastante sencillo utilizando el método isAllowed():

echo $acl->isAllowed('guest', null, 'view') ?
     "allowed" : "denied";
// allowed

echo $acl->isAllowed('staff', null, 'publish') ?
     "allowed" : "denied";
// denied

echo $acl->isAllowed('staff', null, 'revise') ?
     "allowed" : "denied";
// allowed

echo $acl->isAllowed('editor', null, 'view') ?
     "allowed" : "denied";
// allowed because of inheritance from guest

echo $acl->isAllowed('editor', null, 'update') ?
     "allowed" : "denied";
// denied because no allow rule for 'update'

echo $acl->isAllowed('administrator', null, 'view') ?
     "allowed" : "denied";
// allowed because administrator is allowed all privileges

echo $acl->isAllowed('administrator') ?
     "allowed" : "denied";
// allowed because administrator is allowed all privileges

echo $acl->isAllowed('administrator', null, 'update') ?
     "allowed" : "denied";
// allowed because administrator is allowed all privileges