Глава 2. Zend_Acl

Содержание

2.1. Введение
2.1.1. О Ресурсах
2.1.2. О Ролях
2.1.3. Создание списка контроля доступа (ACL)
2.1.4. Регистрация Ролей
2.1.5. Определение Контроля Доступа
2.1.6. Запрос ACL
2.2. Усовершенствования Управления Доступом
2.2.1. Точное Управление Доступом
2.2.2. Удаление Контроля Доступа
2.3. Advanced Use
2.3.1. Storing ACL Data for Persistence
2.3.2. Writing Conditional ACL Rules with Assertions

2.1. Введение

Zend_Acl предоставляет легковесный и гибкий набор функциональных возможностей списка прав доступа (ACL) и управления привилегиями. Приложение может использовать этот функционал для контроля доступа одних объектов к другим - защищенным.

В рамках данной документации,

  • Ресурс - объект, доступ к которому контролируется.

  • Роль - объект, который может запросить доступ к Ресурсу.

Говоря проще, Роли запрашивают доступ к Ресурсу. Например, если человек запрашивает доступ к автомобилю, тогда человек - это Роль, а автомобиль - Ресурс, поскольку доступ к автомобилю находится под контролем

Благодаря спецификации и использованию списка прав доступа(ACL), приложение получает контроль над тем, как запрашивающие объекты (Роли) получают доступ к защищенным объектам (Ресурсам).

2.1.1. О Ресурсах

В Zend_Acl создавать Ресурс очень просто. Zend_Acl предоставляет Zend_Acl_Resource_Interface, чтобы облегчить разработчикам процесс создания Ресурса. Класс должен только реализовать этот интерфейс, который состоит из одного метода, getResourceId(), для того, чтобы Zend_Acl рассматривал объект как Ресурс. Дополнительно, Zend_Acl также содержит Zend_Acl_Resource, как базовый класс, который разработчики могут расширять по желанию.

Zend_Acl предусматривает структуру в виде дерева, в которое могут добавляться многочисленные Ресурсы (или "подконтрольные зоны"). Так как Ресурсы добавлены в такую структуру, они могут быть организованы от общих (в направлении корня дерева) к специфическим (в направлении листьев дерева). При запросах к определенным ресурсам, в иерархии будет выполнен автоматический поиск правил, относящихся к Ресурсам-предкам, учитывающий простое наследование правил. Например, если некое общее правило должно действовать в каждом здании города, то проще прикрепить его к городу, нежели крепить к каждому зданию в городе. Однако, для некоторых зданий потребуются исключения из этого правила, в Zend_Acl это достигается путем закрепления исключений из правила за каждым зданием, которое нуждается в этом. Ресурс может наследоваться только от одного родительского Ресурса, однако сам родительский Ресурс может, в свою очередь, наследоваться от своего родительского Ресурса и т.д.

Zend_Acl также поддерживает права доступа к Ресурсам (например, "создание", "чтение", "обновление", "удаление"), и разработчик может закреплять правила, которые будут влиять на все или определенные права доступа к Ресурсу.

2.1.2. О Ролях

Как и в случае с Ресурсами, создавать Роль также очень просто. Zend_Acl предоставляет Zend_Acl_Role_Interface чтобы облегчить разработчикам процесс создания Ролей. Класс должен только реализовать этот интерфейс, который состоит из одного метода, getRoleId(), для того, чтобы Zend_Acl рассматривал объект как Роль. Дополнительно, Zend_Acl также содержит Zend_Acl_Role, как базовый класс, который разработчики могут расширять по желанию.

В Zend_Acl Роль может наследоваться от одной или от нескольких Ролей. Это реализовано для поддержки наследования правил между Ролями. Например, пользовательская Роль, такая как "салли", может принадлежать одной или нескольким родительским Ролям, таким как "редактор" и "администратор". Разработчик может привязывать правила к "редактору" и администратору раздельно, и "салли" будет наследовать правила обоих Ролей. Нет необходимости привязывать правила непосредственно к "салли".

Хотя наследование от множества Ролей - очень полезная возможность, она также усложняет разработку. Следующий пример демонстрирует неопределенное условие и показывает как Zend_Acl решает эту проблему.

Пример 2.1. Множественное наследование Ролей

Следующий код определяет три базовые Роли - "guest", "member", и "admin" - от которых другие роли будут наследоваться. Затем, Роль "someUser" создается и наследуется от трех других Ролей. Порядок, в котором эти роли появляются в массиве $parents, важен. При необходимости, Zend_Acl ищет правила доступа не только для запрашиваемых Ролей (в нашем случае, "someUser"), но также и для ролей, от которых запрашиваемая Роль унаследована (в нашем примере, "guest", "member", и "admin"):

<?php
require_once 'Zend/Acl.php';
$acl = new Zend_Acl();

require_once 'Zend/Acl/Role.php';
$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);

require_once 'Zend/Acl/Resource.php';
$acl->add(new Zend_Acl_Resource('someResource'));

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

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

Поскольку нет правил, определенных специально для Роли "someUser" и Ресурса "someResource", Zend_Acl должен производить поиск правил, которые могут быть определены для Ролей, от которых "someUser" наследуется. Сперва, проверяется Роль "admin", и для нее не определены правила доступа. Затем, проверяется Роль "member", и Zend_Acl находит, что есть правило разрешающее доступ для "member" к "someResource".

Если бы Zend_Acl продолжил поиск правил, определенных для родительских Ролей, то он бы обнаружил, что для "guest" запрещен доступ к "someResource". Этот факт показывает противоречие, так как теперь для "someUser" доступ к "someResource" разрешен и запрещен одновременно. Конфликт произошел по причине наследования от нескольких Ролей.

Zend_Acl решает эту неоднозначность завершая запрос как только находит первое правило, которое может быть применено к запросу. В этом случае, если Роль "member" проверяется раньше, чем Роль "guest", то приведенный пример выведет "allowed".

[Замечание] Замечание

Когда определяем нескольких родительских Ролей, не забывайте, что последний указанный родитель, является первым в списке поиска правил для запроса авторизации.

2.1.3. Создание списка контроля доступа (ACL)

ACL может представлять любое множество физических либо виртуальных объектов. В целях демонстрации, мы создадим базовую функциональность ACL для Системы Управления Контентом (CMS), которая будет поддерживать нескольких уровней групп к множеству областей. Чтобы создать новый объект ACL, производим инстанцирование без параметров:

<?php
require_once 'Zend/Acl.php';

$acl = new Zend_Acl();
[Замечание] Замечание

По умолчанию Zend_Acl реализует "белый список", это значит, что пока обратное не указано разработчиком, Zend_Acl запрещает доступ ко всем Ресурсам для всех Ролей

2.1.4. Регистрация Ролей

Система Управления Контентом почти всегда нуждается в иерархии доступа для определения авторских возможностей своих пользователей. Это может быть группа "Гость", предоставляющая ограниченный доступ для демонстрации, группа "Сотрудник" - группа большинства пользователей CMS, которые производят каждодневные операции, группа "Редактор" - для тех кто публикует и редактирует, архивирует и удаляет контент, и, наконец, группа "Администратор", участники группы могут выполнять все те же операции, что и участники других групп, а также управлять другой, специфической, информацией, пользователями, конфигурацией адинистративной части, а также делать резервное копирование/восстановление данных. Этот набор прав доступа может быть представлен в реестре Ролей, позволяя каждой группе наследовать привилегии родительской группы, при этом имея индивидуальные права доступа. Права доступа могут быть представлены в таком виде:

Таблица 2.1. Контроль доступа для демонстрационной CMS

Название Индивидуальные права Права унаследованные от
Гость Просмотр Не определено
Сотрудник Редактирование, предложение на публикацию, исправление Гость
Редактор Публикация, архивирование, удаление Сотрудник
Администратор (Обладает всеми правами) Не определено

Для этого примера мы используем Zend_Acl_Role, но можно было бы использовать любой класс, который реализует интерфейс Zend_Acl_Role_Interface. Эти группы могут быть добавлены в реестр Ролей таким образом:

<?php
require_once 'Zend/Acl.php';

$acl = new Zend_Acl();

//Добавить группы в реестр Ролей используя Zend_Acl_Role
require_once 'Zend/Acl/Role.php';

// Гость не наследует управление доступом
$roleGuest = new Zend_Acl_Role('guest');
$acl->addRole($roleGuest);

//Сотрудник наследуется от Гостя
$acl->addRole(new Zend_Acl_Role('staff'), $roleGuest);

/*
Альтернатива тому, что написано выше:
$roleGuest = $acl->addRole(new Zend_Acl_Role('staff'), 'guest');
//*/

//Редактор наледуется от Посетителя
$acl->addRole(new Zend_Acl_Role('editor'), 'staff');

//Администатор не наследует управление доступом
$acl->addRole(new Zend_Acl_Role('administrator'));

2.1.5. Определение Контроля Доступа

Теперь, когда 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('staff', null, array('edit', 'submit', 'revise'));

// Редактор наследует привилегии просмотра, редактирования, отправки и исправлений у Посетителя
// но также нуждается в дополнительных привилегиях
$acl->allow('editor', null, array('publish', 'archive', 'delete'));

// Администратор не наследует ничего, но обладает всеми привилегиями
$acl->allow('administrator');

Значение null в вызовах функции allow()используются для того чтобы показать, что правила предоставляющие доступ применяются ко всем ресурсам.

2.1.6. Запрос ACL

Теперь у нас есть гибкий ACL, который может использоваться для того чтобы определить, достаточно ли прав имеет запрашивающий, чтобы выполнить функции в веб-приложении. Выполнять запросы достаточно просто, используя метод isAllowed():

<?php
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 из-за наследования от Гостя

echo $acl->isAllowed('editor', null, 'update') ?
     "allowed" : "denied"; // denied, так как нет разрешающего правила для 'обновления'

echo $acl->isAllowed('administrator', null, 'view') ?
     "allowed" : "denied"; // allowed, т.к. администратор обладает всеми привилегиями

echo $acl->isAllowed('administrator') ?
     "allowed" : "denied"; // allowed, т.к. администратор обладает всеми привилегиями

echo $acl->isAllowed('administrator', null, 'update') ?
     "allowed" : "denied"; // allowed, т.к. администратор обладает всеми привилегиями