MZZ.Framework 0.3.x: Документация
Разделы

3.7 ACL

1. Обзор
2. Хранение прав в БД
3. Наложение прав
4. Владельцы объектов
5. Работа с ACL
6. Запуск модулей из шаблонов
7. obj_id
8. Метод convertArgsToObj()
9. Метод getAcl()
1. Обзор

Одной из основных идей mzz является: "Всё на сайте является объектом". Т.е. новость это объект класса news, фотография в галерее - объект класса photo, пользователь - объект класса user. Каждый из объектов обладает набором действий, которые можно над ним произвести. Например "удалить", "создать", "изменить", "переместить" и т.д. Этот набор не является жёстко фиксированным и создаётся программистом в процессе разработки приложения. Система прав mzz позволяет разграничивать права на выполнение определённых действий на уровне конкретных объектов, для пользователей и групп. Т.е. вы можете разрешить (или наоборот, запретить) выполнение "редактирования" новости определённым пользователем (или группой пользователей). При этом проверка прав на действия - производится автоматически. Писать код, работающий с правами, вам придётся лишь в том случае, если будет необходимо реализовать нетривиальную схему авторизации, не вписывающуюся в имеющуюся в mzz. Обо всём по порядку.

2. Хранение прав в БД

Данные о правах хранятся в группе таблиц в БД mzz.

Имя таблицы Описание
sys_access Главная таблица ACL. Непосредственно в ней хранятся все права на все объекты в mzz.
sys_access_registry Реестр объектов. Для того, чтобы объекту можно было назначать права, он должен быть зарегистрирован в ACL. Эта таблица хранит в себе список объектов и ссылку на тип объекта и раздел, в контектсе которого этот объект существует.
sys_actions Справочник действий. Таблица, содержащая список всех действий, которые могут быть выполнены с объектами системы.
sys_classes Справочник классов. Таблица, содержащая список всех типов объектов, доступных в системе.
sys_classes_actions Таблица, связывающая между собой конкретный класс и действия. Именно на основании данных этой таблицы определяется - какие действия возможно производить над объектом.
sys_classes_sections Таблица, связывающая между собой конкретный класс и секцию, в контексте которой хранится объект этого класса.
sys_modules Справочник модулей. Таблица, содержащая список всех типов модулей, доступных в системе.
sys_obj_id Таблица, выполняющая роль секвенции. Используется для определения следующего значения obj_id (уникального в пределах системы идентификатора объекта).
sys_obj_id_named Таблица, содержащая набор "именованных" объектов. Они используются в тех случаях, когда реального объекта не существует, но он необходим идеологически.
sys_sections Справочник разделов. Таблица, содержащая список всех типов разделов, в контексте которых могут работать модули.
3. Наложение прав

Права на объекты "наследуются" в случае, если на одного и того же пользователя действует несколько разрешений на действие (т.е. право дано конкретному пользователю и группам, в которых он состоит). Наследование прав состоит из правил, определённых для конкретного пользователя, группы пользователей и дефолтных прав на тип объектов. Вся система прав mzz может быть вкратце описана в трёх тезисах:

Как уже было сказано - права на конкретный объект можно выставлять как конкретному пользователю, так и группе пользователей. Также существует набор дефолтных прав, которые действуют на всю коллекцию объектов определённого типа. Разберём процесс авторизации на нескольких примерах.

Пусть имеется в наличии объект класса news с уникальным идентификатором obj_id = 100. Для простоты представим, что с объектом класса news можно производить 2 действия: просмотр и редактирование. Также зарегистрировано 3 пользователя с именами visitor, editor и hacker, которые в примерах будут фигурировать, как рядовой посетитель, контент-менеджер системы и злоумышленник соответственно. Также в системе имеются 3 группы: viewers (посетители), managers (контент-менеджеры системы), banned (забаненные пользователи). Пользователь editor состоит в группах viewers и managers, пользователь visitor - только в группе viewers, а пользователь hacker - в группе banned.

Пользователи

Ид Имя
1 visitor
2 editor
3 hacker

Группы

Ид Имя
1 viewers
2 managers
3 banned

В следующих примерах будет использоваться упрощённая структура таблиц, облегчающая понимание процесса проверки прав, но при этом достаточно адекватная по отношению к существующей структуре.

Ситуация 1. Права по умолчанию на все объекты класса news.

Действие Пользователь Группа Правило
Просмотр 1 разрешено
Редактирование 2 разрешено
Просмотр 3 запрещено

Из этой таблицы видно, что по умолчанию всем пользователям из группы viewers разрешён просмотр новостей, группе managers разрешено редактирование всех новостей, а пользователям, включённым в группу banned доступ на просмотр всех новостей закрыт. Применительно к пользователям: visitor и editor получат права на просмотр, причём у editor'а также будет право и на редактирование всех новостей. А у hacker доступ ко всем действиям всех объектов класса news - закрыт.

Ситуация 2. Права на конкретный объект news (obj_id = 100).

Теперь давайте добавим несколько частных прав на конкретную новость (у которой obj_id = 100).

Действие Пользователь Группа Правило
Редактирование 1 разрешено
Редактирование 2 запрещено
Просмотр 3 разрешено

Разберёмся с этими установленными правами:

1 строка: в ней мы разрешаем доступ для пользователя visitor на редактирование. Ранее у этого пользователя на редактирование права выставлены не были, сейчас правило - разрешающее. Вердикт: доступ разрешён (См. тезис 2).

2 строка: в ней мы запрещаем доступ для редактирования для всей группы managers. До этого группе было выставлено разрешающее правило. Вердикт: доступ запрещён (См. тезис 3).

3 строка: в ней мы разрешаем доступ на просмотр конкретной новости пользователю hacker. Для этого пользователя это единственное правило. Но он состоит в группе, на которую уже было установлено запрещающее. доступ запрещён (См. тезис 3).

4. Владельцы объектов

В системе управления правами mzz также существует и такое понятие, как владелец. Это тот пользователь, от чьего имени был создан объект. Для владельцев можно установить специальный набор прав, разрешающий или запрещающий ряд действий, производимых над вновь созданным объектом. Номинально - при создании и регистрации объекта набор "прав для владельца" копируется для конкретного объекта и конкретного пользователя, т.е. правила фактически становятся конкретными правилами для объекта. Из этого следует, что если вы измените "права для владельца" объекта, то новый набор прав будет применён лишь к создаваемым после этого события объектам. На права, выставленные на объекты, созданные до изменения набора "прав для владельца", это никак не повлияет.

5. Работа с ACL

ACL предоставляет интерфейс, позволяющий производить необходимые действия по определению и установке прав на объекты.

Перед описанием принципов работы с ACL, приведём прототип конструктора класса acl:

public function __construct($user = null, $object_id = 0, $class = '', $section = '')

Получение прав (в качестве примера возьмём все данные из примера в прошлом пункте):

<?php
 
    $user = $userMapper->searchByLogin('editor');       // получаем пользователя 'editor'
    $news = $newsMapper->searchById(100);               // получаем новость с id = 100 (obj_id этой новости примем также равным 100)
 
    $acl = new acl($user, $news->getObjId());
 
    $access = $acl->get();                              // будет возвращён массив array('edit' => true, 'view' => true);
    $access = $acl->get('view');                        // true
 
    $user2 = $userMapper->searchByLogin('visitor');     // получаем пользователя 'visitor'
    $acl = new acl($user2, $news->getObjId());
 
    $access = $acl->get();                              // будет возвращён массив array('edit' => false, 'view' => true);
    $access = $acl->get('view');                        // true
    $access = $acl->get('edit');                        // false
 
    $user3 = $userMapper->searchByLogin('hacker');      // получаем пользователя 'hacker'
    $acl = new acl($user3, $news->getObjId());
 
    $access = $acl->get();                              // будет возвращён массив array('edit' => false, 'view' => false);
    $access = $acl->get('view');                        // false
    $access = $acl->get('edit');                        // false
 
?>

Модификация прав:

<?php
 
    $user = $userMapper->searchByLogin('editor');       // получаем пользователя 'editor'
    $news = $newsMapper->searchById(100);               // получаем новость с id = 100 (obj_id этой новости примем также равным 100)
 
    $acl = new acl($user, $news->getObjId());
    $access = array('edit' => false, 'view' => false);
    $acl->set($access);                                 // будут установлены запрещающие права для пользователя 'editor'
    $acl->set('view', true);                            // будет разрешён просмотр новости
 
    $group = $groupMapper->searchByName('banned');
    $acl->set('view', true, $group->getId());           // для группы 'banned' будет разрешён просмотр новости
                                                        //(однако в результате всё равно никто из этой группы просмотреть эту новость не сможет, см. тезис 3)
 
?>
6. Запуск модулей из шаблонов

ACL автоматически проверяет на возможность запуска действия модуля из шаблона. В случае, если права на выполнение действия есть - происходит запуск модуля, если прав нет - показывается страница 403 с соответствующим сообщением для пользователя (либо произвольная, определённая программистом страница). Для управления поведением системы проверки прав используется параметр 403handle. Он может принимать 3 значения:

Вторым параметром, имеющим отношение к системе проверки прав, является 403tpl. В нём указывается путь до шаблона, который будет отображён, в случае если на запуск метода у текущего пользователя не хватает прав.

Пример запуска модулей:

{load module="news" action="list" section="news" 403handle="manual"}
{load module="news" action="list" section="news" 403tpl="news/deny.tpl"}

В случае, если значение 403handle не было изменено, и 403tpl также не был указан - вызывается контроллер simple403Controller, реализация которого и определяет, что именно будет показано в случае, когда у пользователя не хватает прав на запуск действия. Реализация по умолчанию, идущая в фреймворке, представляет собой отображение страницы с именем 403 модуля page, секции page. В случае, если вам нужно переопределить логику работы этого контроллера - воспользуйтесь предусмотренным для этого механизмом переопределения классов, используя резолверы.

7. obj_id

obj_id это служебное поле, используемое в системе проверки прав доступа, а также в некоторых служебных целях в ORM. Это поле должно присутствовать во всех таблицах, которые находятся под управлением ACL и ORM, и иметь тип integer. obj_id является уникальным в пределах проекта, т.е. не существует двух объектов с одинаковым значением этого параметра. Доступ к значению obj_id производится с помощью метода ДО getObjId(). Это достигается с помощью таблицы sys_obj_id.

Бывают ситуации, когда необходимо зарегистрировать объект в ACL и дать ему некоторое значение obj_id, но создавать целую сущность ради одной записи в таблице - не оправданно. Тогда следует прибегнуть к механизму так называемых "фейковых" объектов. В терминологии мзз - это такие объекты, которые фактически не существуют в БД, однако зарегистрированы в ACL и имеют свои значения obj_id. Это реализуется с помощью метода getObjectId() тулкита. "Фейковые" объекты идентифицируются по имени. Пример получения obj_id для "фейкового" объекта:

$obj_id = $this->toolkit->getObjectId('sample_fake_object_name');

В результате - если "фейковый" объект с именем sample_fake_object_name не существовал, то он будет создан и возвращено значение его obj_id, если уже существовал - тогда просто возвращён obj_id.

Также бывают ситуации, когда использование obj_id нецелесообразно. В число таких ситуаций можно отнести случаи, когда объекты не участвуют в ACL и когда использование лишнего поля - просто не имеет смысла. В число демо-приложения входят как минимум 2 класса, для которых obj_id не нужно - это userAuth и userOnline. Эти сущности предназначены для реализации "запоминания" аутентификации и хранения пользователей онлайн соответственно. "Отключение" использования obj_id для такого рода классов осуществляется следующим образом:

<?php
class userAuthMapper extends simpleMapper
{
        protected $obj_id_field = null;
        [...]
}
?>

Т.е. достаточно защищённое свойство $obj_id_field установить в null.

8. Метод convertArgsToObj()

Метод предназначен для поиска и создания доменного объекта по параметрам запроса, что необходимо для разграничения прав доступа к действию над ним. В качестве аргумента в него передаются параметры запроса в виде массива, а в качестве результата должен возвращаться искомый объект, либо бросаться исключение - если объект не найден. Пример реализации этого метода:

<?php
 
class newsMapper extends simpleMapper
{
    [...]
 
    public function convertArgsToObj($args)
    {
        $news = $this->searchOneByField('id', $args['id']);
 
        if ($news) {
            return $news;
        }
 
        throw new mzzDONotFoundException();
    }
}
 
?>
9. Метод getAcl()

Метод getAcl класса simple предназначен для усложнения системы прав, в тех случаях, когда штатных средств ACL становится недостаточно. Этот метод принимает аргументом имя экшна, и должен возвращать булево значение, true - в случае если права есть и false в противном случае.

<?php
 
class photo extends simple
{
    [...]
 
    public function getAcl($name = null)
    {
        $access = parent::getAcl($name);
 
        if (in_array($name, array('viewPhoto', 'viewThumbnail', 'view')) && $access) {
            $access = $this->getAlbum()->getAcl('viewAlbum');
        }
 
        return $access;
    }
}
 
?>

В этом примере происходит уточнение прав на некоторые экшны доменного объекта photo. Из кода видно, что доступ к действиям view, viewPhoto и viewThumbnail есть лишь в том случае, если есть доступ к экшну viewAlbum просматриваемого альбома.