Класс Permission

В классе Permission даны методы для переопределения "на лету" разрешений, которые имеет текущий пользователь, в отношении различных объектов сайта. Объектами могут выступать блоки, записи, страницы сайта, другие пользователи сайта, сам сайт и т.д.

Каждый тип объекта имеет собственный набор возможных разрешений. Например, в отношении записей пользователи могут иметь следующие разрешения: создание новых записей, редактирование и удаление записей.

Описание параметров, используемых во всех методах

  • $type — тип объекта

    Полный перечень типов объектов приведен таблице в конце.
    Примеры зарезервированных типов: 'record', 'block', 'page'.

    Вы, в принципе, можете придумывать собственные типы объектов, и задавать им собственные разрешения — главное, чтобы названия этих типов не совпадали с зарезервированными. При этом, естесвенно, нужно иметь в виду, что ваши разрешения не будут влиять на работу служебных скриптов.

  • $keys — ключи объекта

    Ключи — это массив идентификаторов или один идентификатор. В качестве идентификаторов можно использовать строковые данные или целые числа (для штатных типов объектов используются только целые числа). Например, для объекта типа "page", ключом является номер страницы. А чтобы изменить разрешения на запись, нужны два ключа: кроме номера записи, нужно знать и номер блока, в котором находится эта запись.

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

    Permission::add('record', [$srcBlockId, $recId], 'edit');


Permission::add($type, $keys, $permits)

Метод, добавляющий разрешения текущему пользователю.

Обратите внимание на то, что метод Permission:add является чисто виртуальным — он работает только во время вызова страницы. Для сохранения же прав на постоянной основе и получении информации обо всех правах пользуйтесь классом Proposition, методом Blox::info() и др.

  • Возвращает false в случае ошибки.
  • $type — тип объекта (строка).
  • $keys — ключи объекта (целое число, строка или массив).
  • $permits — массив разрешений или разрешительная функция.

Пример:

# Разрешить текущему пользователю редактировать запись 333 в блоке 22
Permission::add('record', [22, 333], ['edit'=>true]);
  • Параметр $permits — массив разрешений

    Это ассоциативный массив в формате: ['имяРазрешения'=>true, ...]. Каждый тип объекта имеет собственный набор возможных разрешений. Полный список объектов разрешений приведен в таблице в конце.

    Обычно в качестве значений элементов массива $permits используется true. Однако, если необходимо отменить разрешение, нужно явно задавать значение false .

    Для массива $permits допустимы более компактные форматы:

    • Простой массив: ['имяРазрешения', ...].
    • Строка: 'имяРазрешения' (если добавляется одно разрешение).
    Ясно, что компактные форматы не позволяют отменять разрешения.
  • Как установить разрешение для всех объектов

    Можно установить одно и то же разрешение $permits для всех объектов. Для этого вместо указания конкретного ключа, нужно поставить пустой ключ/ключи. В качестве значений пустых ключей нужно брать пустую строку. Пустыми могут быть только младшие ключи или все ключи сразу. Ситуация, когда старший ключ пустой, а младший задан, недопустима.

    Примеры:

    # Текущий пользователь имеет право создавать новые записи в блоке 22.
    Permission::add('record', [22, ''], 'create');
    # Кстати, для разрешения create второй ключ вообще всегда должен быть пустым.

    Всегда нужно указывать полный набор ключей (даже пустых) для данного типа объекта. Неполные наборы ключей в методе Permission::add() запрещены

  • Параметр $permits как разрешительная функция

    В момент вызова метода Permission::add() не всегда бывают известны объекты (ключи объектов), которым нужно установить те или иные разрешения. Для этого нужны дополнительные данные, которые становятся известными только в момент вызова метода Permission::get().

    В таких случаях вместо массива $permits нужно поставить определение разрешительной функции (callback).

    Разрешительная функция должна быть определена как анонимная функция, и она должна возвращать массив разрешений. Формат возвращаемого массива, должен быть ассоциативный: ['имяРазрешения'=>true, ...], такой же как и у массива $permits в полной форме.

    У разрешительной функции могут быть два параметра: $keys и $data. Они совпадают с одноименными параметрами метода Permission::get(). Задавать эти параметры в разрешительной функции необязательно — делайте это, если они нужны.

    # Разрешить текущему пользователю редактировать записи
    # блока 22, в которых значение поля 1 равно его ID.
    $permits = function($keys, $data) {
        if ($data['dat'][1] == Blox::info('user','id'))
            return ['edit'=>true];
    };
    Permission::add('record', [22, ''], $permits);

    При использовании разрешительной функции, младшие ключи часто бывают пустыми. Это ясно из назначения разрешительной функции — уточнять, каким объектам давать разрешения, а каким нет. Однако разрешительную функцию можно применять и для конкретного объекта с конкретными ключами.

  • Когда разрешительная функция возвращает не массив разрешений, а строку 'undefined'

    Если не хватает входных данных для вычислений, например, было указано меньше ключей, чем надо, то желательно сделать так чтобы, функция возвращала строку 'undefined', а не null (то есть, ничего), как в предыдущем примере. Это будет означать, что какие-то разрешения все-таки могут быть, и система это учтет, когда данных окажется достаточно.

    Permission::add(
        'record', 
        [22, ''], 
        function($keys, $data) {
            if (!isset($keys) && !isset($data['dat']))
                return 'undefined';
            . . .
        };
    );
  • Отмена разрешений

    Для отмены какого-либо разрешения необходимо задать разрешению значение false.

    # Запретить редактировать все блоки сайта
    Permission::add('record', ['', ''], ['edit'=>false]);

    Если перед назначением разрешения вам важно знать о том, что разрешение не просто отсутствовало, а было отменено, то предварительно нужно производить проверку:

    if (Permission::get('record', [11,''])['edit'] !== false) {
        Permission::add('record', [11, ''], function(){...});
    }

    Вызывать метод Permission::get() внутри разрешительной функции для проверки разрешений запрещено — это приведет к бесконечному циклу.


Permission::get($type, $keys, $data)

Метод для извлечения разрешений.

  • Возвращает массив разрешений в формате ['имяРазрешения1'=>true, 'имяРазрешения2'=>false, ...]. Формат такой же, как у параметра $permits в полном формате. В случае ошибки вместо массива возвращается false.
  • $type — тип объекта (строка).
  • $keys — ключи объекта (целое число, строка или массив).
  • $data — входные данные для разрешительной функции (любой тип данных). Этот параметр нужен только в тех случаях, когда в методе Permission::add() параметр $permits задан, как разрешительная функция. Каждый тип объекта имеет свою структуру входных данных (см. таблицу в конце ).
    Применять параметр $data (как и параметр $keys) в разрешительных функциях необязательно. Если параметр $data нигде для данного типа объекта не используется, то при вызове метода Permission::get() этот параметр $data также не нужен. Однако, при вызове метода Permission::get() для системных типов объектов всегда используйте полный параметр $data.

Чтобы узнать какое-либо разрешение на конкретный объект, все ключи должны быть заданы, например:

if (Permission::get('record', [22, 333])['edit'])
    echo'Вам разрешено редактировать запись 333 в блоке 22';

Неполные наборы ключей

Применять пустые ключи в методе Permission::get(), в отличие от метода Permission::add(), запрещено! Однако, ключи можно просто опустить, то есть, применять неполные наборы ключей. Если опустить младшие ключи, то будет выведен массив всех опущенных младших ключей и их разрешений :

if (Permission::get('record', [22]))
    echo'Вы имеете какие-то разрешения, касающиеся записей в блоке 22';

Советы по удалению пустых разрешений

  • Если разрешение не просто отсутствовало, а было отмено, то оно будет иметь значение false. Если вас не интересует факт явного удаления разрешения, то все разрешения со значением false можно разом удалить с помощью метода Arr::remove(). Заодно будут удалены и все пустые элементы: '', 0, '0', null.

    $permissions = Arr::remove(
        Permission::get('record', [22])
    );
  • Если вас не интересуют разрешения, которые определить не удалось, то их можно удалить так:

    $permissions = Arr::remove(
        Permission::get('record', [22]),
        'undefined'
    );
  • Если вы хотите убрать из массива разрешений все отмены, неопределенные разрешения, и заодно все пустые элементы, которые могли появится по ошибке, делайте так:

    $permissions = Arr::remove(
        Permission::get('record', [22]),
        ['undefined', false, '', 0, '0', null]
    );

Примечания

  • Собственно прибегать к методу Permission::get() обычно не приходится — он вызывается в служебных скриптах для управления доступом к штатным типам объектов (record, block, page). Другое дело, если вы создаете собственные типы объектов — там оба метода: Permission::add() и Permission::get(), нужно использовать в связке.

  • Метод Permission::get() возвращает массив всех разрешений, как есть, без всякой интерпретации. Более строгим аналогом этого метода является метод Permission::ask().


Permission::ask($type, $keys, $data)

Метод Permission::ask() — это более строгий аналог метода Permission::get(). Узнавать окончательные разрешения, как с конкретными ключами, так и с неполными набороми ключей, лучше с помощью именно данного метода.

Отличия метода Permission::ask() от Permission::get()

  • Если есть частичный запрет (в подуровнях) при неполном наборе ключей, метод Permission::ask() не дает вообще никакого разрешения.
  • Метод Permission::ask() не возвращает запреты, он возвращает только разрешения со значением true.
  • В методе Permission::ask() более общие запреты отменяют более частные разрешения
Поясним это на примере. Допустим, Permission::get('record') возвращает:
[
    '' => ['' => ['edit' => false]],
    22 => ['' => ['edit' => true]]
]

Этот массив говорит о том, что был запрет редактировать все блоки сайта. Также где-то было дано разрешение редактировать все записи блока 22.

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

Если мы применим метод Permission::get() к блоку 22, то есть, Permission::get('record', 22), то получим массив:

['' => ['edit' => true]]
которое, казалось бы должно означать, что все записи блока 22 разрешено редактировать. Однако в этом же случае метод Permission::ask('record', 22) вернет пустой массив.

На практике метод Permission::get используется для получения полной картины обо всех разрешениях и запретах, и для этой цели ключи вообще лучше не применять.


Таблица зарезервированных (системных) разрешений

$type
Тип объекта
$keys
Ключи
$permits
Разрешения
$data
record
(запись)
$srcBlockId, # Номер исходного блока
$recId # Номер записи

При назначении разрешения 'create' второй ключ ($recId) должен быть пустым.

'edit', # Редактировать записи
'create' # Создавать новые записи
'dat'=>$dat, # Данные записи
'tdd'=>$tdd # Данные дескриптора
block
(блок)
В работе ...
$srcBlockId # Номер исходного блока
page
(страница)
$pageId # Номер страницы
'see', # Видеть скрытую страницу даже если она скрыта