Документация для разработчиков
Темная тема

Общая концепция

С версии 20.0.1200 Главного модуля (main) добавлен API для работы с новыми правами доступа.

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


Терминология

ТерминОписание
ПользовательАвторизованный пользователь системы. Обязательно имеет свой идентификатор (id).
Группа пользователейОбъединение пользователей, предоставляемое главным модулем: администраторы, сотрудники и т.д.
Access codeОбъединение пользователей по какому-либо критерию. Например: пользователи, состоящие в конкретной рабочей группе; сотрудники отдела и т.д. Предоставляется модулем main и хранится в таблице b_user_access.
ДействиеДействие (операция), совершаемое пользователем. Например: сохранение записи.
Разрешение доступаКонкретное право, предоставляемое пользователю. Например: редактирование своих записей или редактирование всех записей.
Правило доступаНабор логики, учитывающий предоставленные пользователю права и другие факторы, и разрешающий или нет выполнение выбранного действия.
РольСвязь между access кодами и выбранными правами доступа.

Общая концепция

В качестве основы для реализации контроля доступа выбрана rule based модель:

  1. Cоставляется список действий, которые может совершить пользователь;
  2. Cоставляется список разрешений, которые могут быть включены для пользователя;
  3. Для каждого действия описывается правило доступа (на основе разрешений или каких-то других факторов);
  4. Контроллер на основе входных данных (действие, пользователь или любая дополнительная информация) находит нужное правило, выполняет его и возвращает результат;
  5. Клиентский код определяет, что делать с результатом.

Система прав доступа предполагается независимой в каждом модуле, со своим набором действий, прав и правил, и со своими таблицами в БД для хранения настроек. Общее API лишь предоставляет единый подход, необходимые классы и методы, но их использование не обязательно.


Работа с БД

Предполагается, что каждый модуль будет хранить роли и права доступа в своих таблицах. API предоставляет базовые классы для упрощения работы с ORM:

КлассОписание
Bitrix\Main\Access\Permission\AccessPermissionTableКласс для работы с таблицей для хранения разрешений. Поддерживает иерархию разрешений и содержит необходимые для этого проверки.
Bitrix\Main\Access\Role\AccessRoleTableКласс для работы с таблицей для хранения ролей.
Bitrix\Main\Access\Role\AccessRoleRelationTableКласс для работы с таблицей для хранения связей между ролями и пользователями (access кодами пользователей).

Роли

Роль используется для обеспечения связи между access кодами пользователей и разрешениями. Если какой-то пользователь находится в нескольких ролях, то он получит все права, доступные этим ролям (сумму прав).

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


Разрешения

Для работы со списком разрешений реализован базовый класс справочника Bitrix\Main\Access\Permission\PermissionDictionary, предоставляющий методы для:

  • получения списка всех разрешений;
  • получения названий, подсказок и их переводов;
  • работы с иерархией в разрешениях (см. ниже).

При этом предполагается, что как таковые разрешения будут записаны в словарь в виде констант. Аналогичный пример можно посмотреть в модуле tasks: Bitrix\Tasks\Access\Permission\PermissionDictionary .


Иерархия разрешений

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

Редактирование записей

  • Редактирование записей в своем отделе;
  • Редактирование всех записей.

Без включения права Редактирование записей не получится включить остальные два. При выключении Редактирования записей будут автоматически выключены и все зависимые.

С технической точки зрения это реализуется с помощью особого формата значений в PermissionDictionary (используется material path нотация), вложенность при этом не ограничена. Пример:

const
	PERMISSION_EDIT = '1',
	PERMISSION_EDIT_DEPARTMENT = '1.1',
	PERMISSION_EDIT_ALL = '1.2';

Отдельно стоит отметить причины по которым используется именно такая запись:

  • константы легко отслеживать по коду с помощью IDE;
  • не требуется дополнительная запись о родительском элементе;
  • в целом компактный формат, удобный для записи словаря и чтения его человеком.

Контроллер

Единой точкой входа для проверки прав доступа в рамках модуля выступает контроллер, удовлетворяющий интерфейсу AccessibleController и, в общем случае, являющийся наследником BaseAccessController.

В каждом модуле, где предполагается использование, необходимо создавать свой контроллер. В минимальном случае достаточно отнаследоваться от BaseAccessController и реализовать два метода:

protected function loadItem(int $itemId = null): AccessibleItem
{
	//...
}

protected function loadUser(int $userId): AccessibleUser
{
	//...
}

Базовый контроллер предоставляет два способа использования (на примере модуля tasks).

Полный (об используемых моделях описано ниже):

$userModel = UserModel::createFromId($userId);
$item = TaskModel::createFromId($taskId);
$params = [];
$result = (new TaskAccessController($userModel))->check($action, $item, $params);

И упрощенный:

$result = TaskAccessController::can($userId, $action, $itemId, $params);

Кроме этого существует возможность пакетной проверки доступа к набору действий над одной сущностью:

$request = [
	$actionId => $params
];
$result = (new TaskAccessController($userModel))->batchCheck($request, $item);

В ответе такой запрос вернет массив $actionId => $result

В контроллере реализован статический кеш по пользователю, позволяющий сократить количество запросов к БД. Что бы получить уже созданный экземпляр, можно воспользоваться методом getInstance($userId).


Модели

Проверка прав доступа на базовом уровне работает с двумя моделями:

  • AccessibleUser - предоставляет необходимую информацию и методы о пользователе, совершающем действие. Для удобства реализован базовый класс UserModel. UserModel может быть создан из id пользователя или заполнен свойствами вручную.
  • AccessibleItem - модель сущности, над которой выполняются действия.

UserModel из коробки предоставляет методы, удобные для использования в правилах:

  • isAdmin() - проверка, является ли пользователь администратором;
  • getPermission(string $permissionId) - значение указанного разрешения для пользователя;
  • getUserDepartments() - список отделов пользователя;
  • getAccessCodes() - список access кодов пользователя;
  • getSubordinate($userId) - отношения в иерархии компании с указанным пользователем (об этом ниже).

Bitrix\Main\Access\User\UserSubordinate

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

UserSubordinate::getSubordinate($userId) - позволяет определить кем является указанный пользователь по отношению к текущему.

Возможные варианты:

  • UserSubordinate::RELATION_HIMSELF - один и тот же человек;
  • UserSubordinate::RELATION_DEPARTMENT - сотрудники одного отдела;
  • UserSubordinate::RELATION_SUBORDINATE - подчиненный;
  • UserSubordinate::RELATION_DIRECTOR - начальник;
  • UserSubordinate::RELATION_OTHER_DIRECTOR - начальник другого отдела;
  • UserSubordinate::RELATION_OTHER - все прочее (возможно получить, если, к примеру, не установлен модуль intranet).

Перехват управления

Для расширения функционала и перехвата управления реализована работа с событиями:

СобытиеОписание
Bitrix\Main\Access\Event\EventDictionary::EVENT_ON_BEFORE_CHECKВызывается перед проверкой доступа по конкретному правилу.
Bitrix\Main\Access\Event\EventDictionary::EVENT_ON_AFTER_CHECKВызывается после того как отрабатывает проверка по конкретному правилу.

В события передается следующий набор параметров:

[
	'user' // AccessibleUser
	'item' // AccessibleItem
	'action' // string действие, выполняемое пользователем
	'params' // misc любые дополнительные параметры
	'isAccess' // null|bool результат работы правила
]

Каждый обработчик, висящий на каком-либо событии должен вернуть объект Bitrix\Main\Access\Event\EventResult. Любые прочие значения будут проигнорированы.

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



© «Битрикс», 2001-2024, «1С-Битрикс», 2024