Después de que un usuario ha sido identificado como auténtico, una aplicación puede ocuparse de proporcionar algunos recursos útiles y deseables a un consumidor. En muchos casos, las aplicaciones podrían contener diferentes tipos de recursos, con algunos recursos que tienen reglas más estrictas respecto al acceso. Este proceso de determinar quién tiene acceso a qué recursos es el proceso de "autorización". La autorización, en su forma más simple, es la composición de estos elementos:
la identidad a la que se desea conceder acceso
el recurso al que la identidad está solicitando permiso para consumir
y opcionalmente, qué está autorizada a hacer la identidad con el recurso
En Zend Framework, el componente Zend_Acl se encarga de la tarea de
construir un árbol de roles, recursos y privilegios contra el cual gestionar y consultar
las solicitudes de autorización.
Al usar Zend_Acl, cualquier modelo puede servir como rol o recurso
simplemente implementando la interfaz adecuada. Para usarse en capacidad de rol, la clase
debe implementar Zend_Acl_Role_Interface, que solo requiere
getRoleId(). Para usarse en capacidad de recurso, una clase debe
implementar Zend_Acl_Resource_Interface, que de manera similar
requiere que la clase implemente el método getResourceId().
A continuación se muestra un modelo de usuario sencillo. Este modelo puede formar parte de nuestro
sistema ACL simplemente implementando la
Zend_Acl_Role_Interface. El método
getRoleId() devolverá el id "guest" cuando no se conozca un ID,
o devolverá el ID de rol que fue asignado a este objeto de usuario real. Este valor
puede provenir efectivamente de cualquier lugar, una definición estática o quizás dinámicamente del
propio rol de base de datos de los usuarios.
class Default_Model_User implements Zend_Acl_Role_Interface
{
protected $_aclRoleId = null;
public function getRoleId()
{
if ($this->_aclRoleId == null) {
return 'guest';
}
return $this->_aclRoleId;
}
}
Si bien el concepto de un usuario como rol es bastante sencillo, su aplicación podría optar por tener cualquier otro modelo en su sistema como un potencial "recurso" a ser consumido en este sistema ACL. Para simplificar, usaremos el ejemplo de una entrada de blog. Dado que el tipo del recurso está vinculado al tipo del objeto, esta clase solo devolverá 'blogPost' como el ID de recurso en este sistema. Naturalmente, este valor puede ser dinámico si su sistema así lo requiere.
class Default_Model_BlogPost implements Zend_Acl_Resource_Interface
{
public function getResourceId()
{
return 'blogPost';
}
}
Ahora que tenemos al menos un rol y un recurso, podemos proceder a definir las reglas del sistema ACL. Estas reglas se consultarán cuando el sistema reciba una consulta sobre qué es posible dado cierto rol, recursos, y opcionalmente un privilegio.
Supongamos las siguientes reglas:
$acl = new Zend_Acl();
// setup the various roles in our system
$acl->addRole('guest');
// owner inherits all of the rules of guest
$acl->addRole('owner', 'guest');
// add the resources
$acl->addResource('blogPost');
// add privileges to roles and resource combinations
$acl->allow('guest', 'blogPost', 'view');
$acl->allow('owner', 'blogPost', 'post');
$acl->allow('owner', 'blogPost', 'publish');
Las reglas anteriores son bastante simples: existen un rol guest y un rol owner; al igual que un recurso de tipo blogPost. Los guests tienen permitido ver entradas de blog, y los owners tienen permitido publicar y hacer públicas las entradas de blog. Para consultar este sistema, se podría hacer cualquiera de las siguientes acciones:
// assume the user model is of type guest resource
$guestUser = new Default_Model_User();
$ownerUser = new Default_Model_Owner('OwnersUsername');
$post = new Default_Model_BlogPost();
$acl->isAllowed($guestUser, $post, 'view'); // true
$acl->isAllowed($ownerUser, $post, 'view'); // true
$acl->isAllowed($guestUser, $post, 'post'); // false
$acl->isAllowed($ownerUser, $post, 'post'); // true
Como puede ver, las reglas anteriores ponen a prueba si los owners y los guests pueden ver entradas, lo cual pueden, o publicar nuevas entradas, lo que los owners pueden y los guests no. Pero como podría esperarse, este tipo de sistema podría no ser tan dinámico como desearíamos. ¿Qué pasa si queremos asegurarnos de que un owner específico realmente posee una entrada de blog muy específica antes de permitirle publicarla? En otras palabras, queremos asegurarnos de que solo los owners de las entradas tengan la capacidad de publicar sus propias entradas.
Aquí es donde entran las aserciones. Las aserciones son métodos a los que se llama cuando la comprobación de reglas estática simplemente no es suficiente. Al registrar un objeto de aserción, se consultará a este objeto para determinar, típicamente de forma dinámica, si algún rol tiene acceso a algún recurso, con algún privilegio opcional que solo puede ser respondido por la lógica dentro de la aserción. Para este ejemplo, usaremos la siguiente aserción:
class OwnerCanPublishBlogPostAssertion implements Zend_Acl_Assert_Interface
{
/**
* This assertion should receive the actual User and BlogPost objects.
*
* @param Zend_Acl $acl
* @param Zend_Acl_Role_Interface $user
* @param Zend_Acl_Resource_Interface $blogPost
* @param $privilege
* @return bool
*/
public function assert(Zend_Acl $acl,
Zend_Acl_Role_Interface $user = null,
Zend_Acl_Resource_Interface $blogPost = null,
$privilege = null)
{
if (!$user instanceof Default_Model_User) {
throw new Exception(__CLASS__
. '::'
. __METHOD__
. ' expects the role to be'
. ' an instance of User');
}
if (!$blogPost instanceof Default_Model_BlogPost) {
throw new Exception(__CLASS__
. '::'
. __METHOD__
. ' expects the resource to be'
. ' an instance of BlogPost');
}
// if role is publisher, he can always modify a post
if ($user->getRoleId() == 'publisher') {
return true;
}
// check to ensure that everyone else is only modifying their own post
if ($user->id != null && $blogPost->ownerUserId == $user->id) {
return true;
} else {
return false;
}
}
}
Para conectar esto a nuestro sistema ACL, haríamos lo siguiente:
// replace this:
// $acl->allow('owner', 'blogPost', 'publish');
// with this:
$acl->allow('owner',
'blogPost',
'publish',
new OwnerCanPublishBlogPostAssertion());
// lets also add the role of a "publisher" who has access to everything
$acl->allow('publisher', 'blogPost', 'publish');
Ahora, cada vez que se consulte al ACL sobre si un owner puede publicar una entrada de blog específica, se ejecutará esta aserción. Esta aserción se asegurará de que, a menos que el tipo de rol sea 'publisher', el rol owner esté lógicamente vinculado a la entrada de blog en cuestión. En este ejemplo, comprobamos que la propiedad ownerUserId de la entrada de blog coincida con el id del owner pasado.