Programmer's Reference Guide
| Zend_Acl |
Введение
Zend_Acl предоставляет легковесную и гибкую реализацию списка прав доступа (ACL) и управления привилегиями. Приложение может использовать ACL для контроля доступа одних объектов к другим - защищенным.
В рамках данной документации:
-
Ресурс - объект, доступ к которому контролируется.
-
Роль - объект, который может запрашивать доступ к ресурсу.
Говоря проще, роли запрашивают доступ к ресурсам. Например, если парковщик запрашивает доступ к автомобилю, то парковщик - это роль, а автомобиль - ресурс, поскольку доступ к автомобилю не может предоставляться всем без исключения.
Благодаря спецификации и использованию списка прав доступа (ACL) приложение может контролировать предоставление ролям доступа к ресурсам.
Ресурсы
Создать ресурс в Zend_Acl очень просто. Zend_Acl предоставляет интерфейс ресурса Zend_Acl_Resource_Interface для облегчения процесса создания ресурса. Этот интерфейс содержит только один метод, getResourceId(). Классу достаточно реализовывать этот интерфейс для того, чтобы Zend_Acl рассматривал объект этого класса как ресурс. Кроме того, Zend_Acl предоставляет Zend_Acl_Resource в качестве базового класса, который разработчики могут расширять по желанию.
Zend_Acl предоставляет древовидную структуру, в которую могут добавляться различные ресурсы. В этой структуре они могут быть организованы от общих (корень дерева) к специфическим (листья дерева). При запросах к определенным ресурсам в иерархии будет выполнен автоматический поиск правил, относящихся к ресурсам-предкам, учитывающий простое наследование правил. Например, если некое общее правило должно действовать в каждом здании города, то проще прикрепить его к городу, чем крепить к каждому зданию в городе. Однако, для некоторых зданий могут потребоваться исключения из этого правила, в Zend_Acl это достигается путем закрепления исключений за каждым зданием, требующим исключений из правила. Ресурс может наследовать только от одного родительского ресурса, однако сам родительский ресурс может, в свою очередь, наследовать от другого родительского ресурса и т.д.
Zend_Acl также поддерживает права доступа к ресурсам (например, "создание", "чтение", "обновление", "удаление"), разработчик может присваивать правила, которые будут влиять на все или определенные права доступа к ресурсу.
Роли
Как и в случае с ресурсами, создавать роль тоже очень просто. Все роли должны реализовывать интерфейс Zend_Acl_Role_Interface. Этот интерфейс содержит единственный метод getRoleId(). Кроме того, Zend_Acl предоставляет Zend_Acl_Role в качестве базового класса, который разработчики могут расширять по желанию.
В Zend_Acl роль может наследовать от одной или от нескольких ролей. Это реализовано для поддержки наследования правил между ролями. Например, пользовательская роль, такая как "Салли", может принадлежать одной или нескольким родительским ролям, таким как "редактор" и "администратор". Разработчик может привязывать правила к "редактору" и "администратору" раздельно, и "Салли" будет наследовать правила обоих ролей. Нет необходимости привязывать правила непосредственно к "Салли".
Хотя множественное наследование ролей - очень полезная возможность, она также усложняет разработку. Следующий пример демонстрирует неопределенное условие и показывает, как Zend_Acl решает эту проблему.
Пример #1 Множественное наследование ролей
Следующий код определяет три базовые роли: "guest", "member" и "admin", от которых будут наследовать другие роли. Далее создается "someUser", он наследует от этих только что созданных трех ролей. Порядок, в котором эти роли появляются в массиве $parents, важен. При необходимости Zend_Acl ищет правила доступа не только для запрашиваемых ролей (в нашем случае, "someUser"), но и для ролей, от которых запрашиваемая роль унаследована (в нашем примере, "guest", "member" и "admin"):
- $acl = new Zend_Acl();
- $acl->addRole(new Zend_Acl_Role('guest'))
- ->addRole(new Zend_Acl_Role('member'))
- ->addRole(new Zend_Acl_Role('admin'));
- $acl->addRole(new Zend_Acl_Role('someUser'), $parents);
- $acl->add(new Zend_Acl_Resource('someResource'));
- $acl->deny('guest', 'someResource');
- $acl->allow('member', 'someResource');
Поскольку нет правил, определенных специально для роли "someUser" и ресурса "someResource", то Zend_Acl должен производить поиск правил, которые могут быть определены для ролей, от которых "someUser" наследуется. Сперва проверяется роль "admin", и обнаруживается, что для нее не определены правила доступа. Затем проверяется роль "member", и Zend_Acl обнаруживает, что есть правило разрешающее доступ для "member" к "someResource".
Если бы Zend_Acl продолжил поиск правил, определенных для родительских ролей, то обнаружил бы, что для "guest" запрещен доступ к "someResource". Это пример показывает противоречие, так как теперь для "someUser" доступ к "someResource" разрешен и запрещен одновременно. Конфликт произошел по причине наследования от нескольких ролей.
Zend_Acl решает эту неоднозначность, завершая запрос, как только находит первое правило, которое может быть применено к запросу. В этом случае, если роль "member" проверяется раньше, чем роль "guest", то данный пример выведет "разрешен".
Замечание: При определении нескольких родительских ролей, не забывайте, что последний указанный родитель будет первым в списке поиска правил для запроса авторизации.
Создание списка контроля доступа (ACL)
Список контроля доступа (ACL) может представлять собой любое множество физических или виртуальных объектов. В целях демонстрации, мы создадим базовый функционал ACL для системы управления содержимым (CMS), который будет поддерживать нескольких уровней групп к множеству областей. Чтобы создать новый объект ACL, производим инстанцирование ACL без параметров:
- $acl = new Zend_Acl();
Замечание: До тех пор, пока разработчик не определит какое-либо правило, разрешающее доступ, Zend_Acl отказывает всем ролям в доступе ко всем привилегиям на все ресурсы.
Регистрация ролей
CMS почти всегда нуждаются в иерархии доступа для определения авторских возможностей своих пользователей. Это может быть группа "Гость", предоставляющая ограниченный доступ для демонстрации, группа "Сотрудник" - группа большинства пользователей CMS, которые производят каждодневные операции, группа "Редактор" - для тех кто публикует и редактирует, архивирует и удаляет контент, и, наконец, группа "Администратор", участники которой могут выполнять все операции, которые доступны участникам других групп, а также управлять закрытой информацией, пользователями, конфигурацией адинистративной части, делать резервное копирование данных и их экспорт. Этот набор прав доступа может быть представлен в реестре ролей, позволяя каждой группе наследовать привилегии родительской группы, при этом имея индивидуальные права доступа. Права доступа могут быть изображены в следующем виде:
| Название | Индивидуальные права | Права, унаследованные от |
|---|---|---|
| Гость (guest) | Просмотр (view) | Не определено |
| Сотрудник (staff) | Редактирование (edit), предложение на публикацию (submit), исправление (revise) | Гость |
| Редактор (editor) | Публикация (publish), архивирование (archive), удаление (delete) | Сотрудник |
| Администратор (administrator) | (Обладает всеми правами) | Не определено |
Для этого примера мы используем Zend_Acl_Role, но можно было бы использовать любой другой класс, реализующий интерфейс Zend_Acl_Role_Interface. Эти группы могут быть добавлены в реестр ролей следующим образом:
- $acl = new Zend_Acl();
- // Добавление групп в реестр ролей с использованием Zend_Acl_Role
- // Гость не наследует управление доступом
- $roleGuest = new Zend_Acl_Role('guest');
- $acl->addRole($roleGuest);
- // Сотрудник наследует от гостя
- $acl->addRole(new Zend_Acl_Role('staff'), $roleGuest);
- /*
- Делает то же самое, что и
- $acl->addRole(new Zend_Acl_Role('staff'), 'guest');
- */
- // Редактор наследует от сотрудника
- $acl->addRole(new Zend_Acl_Role('editor'), 'staff');
- // Администатор не наследует управление доступом
- $acl->addRole(new Zend_Acl_Role('administrator'));
Определение контроля доступа
Теперь, когда ACL содержит все необходимые роли, можно определить правила, по которым роли будут иметь доступ к ресурсам. Вы, должно быть, заметили, что мы не определили ни одного отдельного ресурса для этого примера, это упрощает демонстрацию того, что правила применяются ко всем ресурсам. Zend_Acl предоставляет реализацию, посредством которой правила должны передаваться от общих к специфическим, минимизируя таким образом количество необходимых правил, так как ресурсы и роли наследуют правила, которые определены для их предков.
Замечание: В целом, Zend_Acl подчиняется правилу, только если более точное правило не применимо.
В результате мы можем определить умеренно сложный набор правил минимальным кодом. Чтобы определить базовые права доступа, описанные выше:
- <?php
- require_once 'Zend/Acl.php';
- $acl = new Zend_Acl();
- require_once 'Zend/Acl/Role.php';
- $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'));
- // Гость может только просматривать контент
- $acl->allow($roleGuest, null, 'view');
- /* другим способом, предыдущий блок кода может быть записан в таком виде:
- $acl->allow('guest', null, 'view');
- //*/
- // Сотрудник наследует привилегии просмотра у Гостя,
- // но также нуждается в дополнительных привилегиях
- // Редактор наследует привилегии просмотра, редактирования,
- // отправки и исправлений у Посетителя
- // но также нуждается в дополнительных привилегиях
- // Администратор не наследует ничего, но обладает всеми привилегиями
- $acl->allow('administrator');
Значение NULL в вызовах allow() в этом примере используется для указания того, что правила, предоставляющие доступ, действительны для всех ресурсов.
Запросы к ACL
Теперь у нас есть гибкий ACL, который может использоваться для определения того, достаточно ли прав имеет запрашивающий, чтобы производить действия в веб-приложении. Используя метод isAllowed(), производить запросы довольно просто:
- "разрешен" : "запрещен";
- // разрешен
- "разрешен" : "запрещен";
- // запрещен
- "разрешен" : "запрещен";
- // разрешен
- "разрешен" : "запрещен";
- // разрешен потому, что редактор наследует от гостя
- "разрешен" : "запрещен";
- // запрещен потому, что нет правила, разрешающего обновление (update)
- "разрешен" : "запрещен";
- // разрешен потому, что администратор обладает всеми привилегиями
- "разрешен" : "запрещен";
- // разрешен потому, что администратор обладает всеми привилегиями
- "разрешен" : "запрещен";
- // разрешен потому, что администратор обладает всеми привилегиями
| Zend_Acl |
Add A Comment
Please do not report issues via comments; use the ZF Issue Tracker.
If you have a JIRA/Crowd account, we suggest you login first before commenting.

Comments
lines 9-26 in 2nd last codeblock.
<?php
// Guest may only view content
$acl->allow($roleGuest, null, 'view');
// Staff inherits view privilege from guest, but also needs additional
// privileges
$acl->allow('staff', null, array('edit', 'submit', 'revise'));
?>
Is it simply that they are called in order? So as you add items they inherit from all previous items?
(Line 3-5)
$roleGuest = new Zend_Acl_Role('guest');
$acl->addRole($roleGuest);
$acl->addRole(new Zend_Acl_Role('staff'), $roleGuest); // A new role 'staff' is added and has inherited the 'guest' role.
[...]
We also can directly write "$acl->addRole(new Zend_Acl_Role('staff'), 'guest');".
Yeah after some sleep and rereading the code I see that you simply pass a 2nd argument to addRole(...) to indicate the inheritance.
Thanks.
Here is a stab at populating an acl class from a database:
Create a custom acl model class (e.g. class Application_Model_User extends Zend_Acl ) defined in folder application/models/User.php, that can be used/called from a custom controller (e.g. application/controllers/AuthorizationController.php)
Create a function within your custom acl model class to populate the acl from the database, for example within Application_Model_User:
public function findByName($name) {
//setup the db parameteres if necessary
$db = new Zend_Db_Adapter_Pdo_Mysql(array(
'host' => '127.0.0.1',
'username' => 'user',
'password' => 'password',
'dbname' => 'profiles'
));
// sample join of user, roles, resources by user id, role id and resource id
$sql = 'select u.id, u.username, u.password, r.name as role, rc.controller, rc.action
from profiles.roles r, profiles.resources rc, profiles.users u, profiles.roleresources rs
WHERE u.username = ? and u.roleid = r.id and r.id = rs.roleid and rs.resourceid = rc.id';
//fetch all the rows with the $name parameter
$rows = $db->fetchAll($sql, array($name));
//process each row to populate the acl roles, resources
foreach ($rows as $row) {
$this->setId($row['id']);
$this->setName($row['username']);
$this->setPassword($row['password']);
$role = new Zend_Acl_Role($row['role']);
//add a role to this acl if it does not exist
if (!$this->hasRole($role))
$this->addRole($role);
$resource = new Zend_Acl_Resource($row['controller']);
//add a resource to this acl if it does not exist
if (!$this->has($resource))
$this->add($resource);
//add the permissions for the role, resource and action
$this->allow($this->getRole(), $row['controller'], $row['action']);
} //end foreach
//store this acl object in Session or you could make this class a Singleton
$_SESSION['User'] = $this;
}
You can use your acl class within an AuthController and test for isAllowed on each action called, for example within any controller there is an indexAction:
public function indexAction()
{
//retrieve the custom User acl object that was initialized by your custom AuthController
$user = $_SESSION['User'];
if($user->isAllowed($user->getRole(), $this->_name, 'index'))
{ // if NO ROLES have access, redirect to unauthorized page
} else {
//throw new Exception($user->getRole());
$this->_helper->redirector('error', 'error');
}
}
Hope this helps get you started.
Please add some proper tutorials. You can't expect your framework to get used by people if it's so hard to get into it.
What is here is more than enough for a vaguely competent developer to get it running.
The Symfony2 documentation has a handy comment at the start of each block which tells you the file they're displaying (eg http://symfony.com/doc/current/book/controller.html). Something like that would be handy here, especially as I got to this page after clicking links for a 'getting started guide' but don't know where I'm meant to be putting the ACL to get started with it!
I'm not asking to be taught PHP, I just want to know where the framework wants me to put things - as if I already knew that I wouldn't be reading the documentation anyway ;)
Cheers :)
It also seems that I'm not the first to ask this question, perhaps it would be worth adding that to the docs ;)
As for the documentation: we all know they're not perfect, however it's up to us to change this :) Learn & Share :) That's for the forums up here are anyway. The guys at Zend are doing a bloody good work providing us with such a powerful framework for free (in Community Edition), now it's up to us to help others learning how to use it :)
Why they couldn't write a code sample that is complete and have all the classes used in it, reference the sample, instead of useless fragments and snippets, is baffling.
They should write a cookbook using the framework where there are basic examples, including setting up users & roles in a DB, accessing them through the frameworks ORM and applying the roles to different sample pages.
Then they could have each class used in the example, reference the example page.
They should take a leaf out of the jQuery docs, as these documents are as unhelpful as your post.