Гибкая система прав в CMS Bitrix: от простых проверок до собственных контроллеров
Управление доступом в Bitrix: обзор методов и примеров
В CMS Bitrix существует несколько подходов для проверки прав пользователей. Наиболее современным и гибким является использование пространства имён Bitrix\Main\Access. Ниже рассмотрены различные варианты, от простых проверок групп до создания собственных контроллеров доступа. Для каждого варианта указана цель, случаи использования, пример кода и типичные проблемы.
Как реализовать гибкую систему прав с помощью Bitrix\Main\Access?
Цель: создание масштабируемой системы разрешений на основе ролей и операций. Случаи использования: модули с кастомными сущностями, сложная иерархия прав, разделение доступа к элементам, разделам, админке.
Решение: использование классов Bitrix\Main\Access\AccessController и Bitrix\Main\Access\Permission\PermissionDictionary. Необходимо унаследовать AccessController, реализовать метод canDo, и зарегистрировать свои права через событие onBuildPermissionsList.
namespace MyModule\Access;
use Bitrix\Main\Access\AccessController;
use Bitrix\Main\Access\Permission\PermissionDictionary;
use Bitrix\Main\Access\User\AccessibleUser;
class ArticleAccessController extends AccessController
{
protected function canDo(string $action, int $itemId = null, AccessibleUser $user = null): bool
{
if ($user === null) {
$user = $this->user;
}
// Получаем права пользователя (роли) из базы
$permissions = $this->getUserPermissions($user->getUserId());
// Проверяем, есть ли нужное действие в правах
return in_array($action, $permissions, true);
}
private function getUserPermissions(int $userId): array
{
// Логика получения прав для пользователя (из таблиц модуля)
// Возвращает массив строк-действий, например ['edit_article', 'read_article']
return ['read_article', 'edit_article'];
}
}
Bitrix access php (управление доступом в bitrix php)
Далее в любом месте сайта можно проверить право:
use MyModule\Access\ArticleAccessController;
$accessController = new ArticleAccessController(\Bitrix\Main\Engine\CurrentUser::get());
if ($accessController->check('edit_article', $articleId)) {
echo 'Разрешено';
} else {
echo 'Запрещено';
}
Типичные ошибки: забыть зарегистрировать права через событие onBuildPermissionsList (для отображения в админке); неправильная работа с кешированием ролей – после изменения прав необходимо сбрасывать кеш пользователя (\Bitrix\Main\Data\Cache::clearCache(true, '/user_permissions/')); отсутствие проверки на $user (может быть null в консоли).
Как проверить, является ли пользователь администратором?
Цель: быстрая проверка глобальных прав. Случаи использования: защита одиночных кнопок, вызов служебных функций, отладка.
Вариант 1: использование метода $USER->IsAdmin().
global $USER;
if ($USER->IsAdmin()) {
echo 'Администратор';
}
Вариант 2: проверка принадлежности к группе администраторов (ID=1).
global $USER;
$groups = $USER->GetUserGroupArray();
if (in_array(1, $groups)) {
echo 'Администратор';
}
Типичные ошибки: вызов $USER->IsAdmin() в неподходящем контексте (например, до инициализации ядра) приводит к ошибке; полагаться только на проверку группы 1 (группа может быть переименована).
Как получить список групп пользователя и проверить произвольную?
Цель: проверка принадлежности к определённой группе (например, «Менеджеры», «Авторы»). Случаи использования: простые ролевые ограничения без необходимости строить сложные права.
Решение: получение массива групп через $USER->GetUserGroupArray() или CUser::GetUserGroup($userId).
global $USER;
$userGroups = $USER->GetUserGroupArray();
$managerGroupId = 7;
if (in_array($managerGroupId, $userGroups)) {
echo 'Пользователь является менеджером';
}
Типичные ошибки: использование устаревшего CGroup::GetList() без фильтра; забыть учесть, что группы могут быть добавлены через включаемые группы (parent groups) – функция возвращает только прямые группы, для полной картины нужно использовать \Bitrix\Main\GroupTable::getList() с join на user_group.
Как проверить права на элемент инфоблока?
Цель: определить, может ли пользователь читать/редактировать/удалять конкретный элемент. Случаи использования: публичная часть, админка, custom компоненты.
Решение: использование метода CIBlockElement::checkAccess() или CIBlock::GetPermission().
$iblockId = 2;
$elementId = 15;
$userId = $USER->GetID();
$access = CIBlockElement::checkAccess($elementId, $iblockId, $userId, 'read');
if ($access) {
echo 'Доступ к чтению есть';
}
Для проверки права на редактирование передаётся 'edit'. Метод CIBlock::GetPermission() возвращает уровень доступа (0-100).
Типичные ошибки: передача неверного ID инфоблока; попытка проверить доступ до инициализации инфоблоков; забыть, что права на элемент наследуются от раздела, а права раздела от инфоблока – нужно явно вызывать соответствующие методы проверки.
Как проверить доступ к модулю или странице админки?
Цель: ограничить доступ к определённым разделам административного интерфейса. Случаи использования: кастомные модули, настройки прав на страницы.
Решение: использование $APPLICATION->GetGroupRight($moduleId) или CMain::GetGroupRight().
$moduleId = 'my.module';
$groupRight = $APPLICATION->GetGroupRight($moduleId);
if ($groupRight >= 'R') {
echo 'Пользователь имеет доступ на чтение к модулю';
}
Типичные ошибки: неверное имя модуля; отсутствие регистрации прав в файле options.php или в базе; путаница между буквенными правами ('D','R','W','X').
Выбор конкретного подхода зависит от сложности системы прав. Для единичных проверок достаточно $USER->IsAdmin() или проверки групп. Для гибких и расширяемых решений рекомендуется использовать Bitrix\Main\Access.
Расширенные примеры управления доступом
Пример 1: Создание полноценного AccessController с ролевой моделью
Создадим контроллер, который использует таблицу ролей. Предположим, есть таблица b_my_module_role_permission с полями: ROLE_ID, PERMISSION_ID (например, 'edit_article', 'delete_article'), а у пользователя есть связь с ролями через таблицу b_my_module_user_role.
namespace MyModule\Access;
use Bitrix\Main\Access\AccessController;
use Bitrix\Main\Access\User\AccessibleUser;
class RoleBasedAccessController extends AccessController
{
protected function canDo(string $action, int $itemId = null, AccessibleUser $user = null): bool
{
if (!$user) {
$user = $this->user;
}
$userId = $user->getUserId();
if (!$userId) {
return false;
}
// Получаем роли пользователя
$roles = $this->getUserRoles($userId);
foreach ($roles as $roleId) {
// Проверяем, есть ли у роли нужное действие
if ($this->roleHasPermission($roleId, $action)) {
return true;
}
}
return false;
}
private function getUserRoles(int $userId): array
{
$result = [];
$dbRoles = \Bitrix\Main\Application::getConnection()->query(
"SELECT ROLE_ID FROM b_my_module_user_role WHERE USER_ID = ".intval($userId)
);
while ($row = $dbRoles->fetch()) {
$result[] = $row['ROLE_ID'];
}
return $result;
}
private function roleHasPermission(int $roleId, string $permissionId): bool
{
$row = \Bitrix\Main\Application::getConnection()->query(
"SELECT 1 FROM b_my_module_role_permission WHERE ROLE_ID = ".$roleId." AND PERMISSION_ID = '".\Bitrix\Main\Application::getConnection()->getSqlHelper()->forSql($permissionId)."'"
)->fetch();
return $row !== false;
}
}
Пример проверки:
$controller = new RoleBasedAccessController(\Bitrix\Main\Engine\CurrentUser::get());
var_dump($controller->check('edit_article', null)); // true, если право есть
Пример 2: Использование PermissionDictionary для описания прав
Права можно описать в виде конфигурации и зарегистрировать через событие onBuildPermissionsList.
use Bitrix\Main\Access\Permission\PermissionDictionary;
class ArticlePermission extends PermissionDictionary
{
const EDIT_ARTICLE = 'edit_article';
const DELETE_ARTICLE = 'delete_article';
const READ_ARTICLE = 'read_article';
public static function getPermissions(): array
{
return [
self::EDIT_ARTICLE => [
'title' => 'Редактирование статьи',
'description' => 'Позволяет изменять содержание статьи',
],
self::DELETE_ARTICLE => [
'title' => 'Удаление статьи',
'description' => 'Позволяет удалять статьи',
],
self::READ_ARTICLE => [
'title' => 'Чтение статьи',
'description' => 'Просмотр текста статьи',
],
];
}
}
Регистрация в обработчике:
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'main',
'OnBuildPermissionsList',
function (\Bitrix\Main\Event $event) {
return new \Bitrix\Main\EventResult(
\Bitrix\Main\EventResult::SUCCESS,
[
'my_module' => [
'title' => 'Мой модуль',
'permissions' => ArticlePermission::getPermissions(),
]
]
);
}
);
Пример 3: Проверка прав с учётом владельца элемента
Часто нужно, чтобы пользователь мог редактировать только свои элементы. Модифицируем canDo:
protected function canDo(string $action, int $itemId = null, AccessibleUser $user = null): bool
{
if (!$user) {
$user = $this->user;
}
// Если действие 'edit_article', проверяем владельца
if ($action === 'edit_article' && $itemId) {
$ownerId = $this->getArticleOwner($itemId);
if ($ownerId === $user->getUserId()) {
return true;
}
// Если не владелец, смотрим, есть ли глобальное право
return $this->hasGlobalPermission($user->getUserId(), 'edit_any_article');
}
return false;
}
Пример 4: Работа с правами на раздел инфоблока
$sectionId = 10;
$iblockId = 2;
$userId = $USER->GetID();
// Получить уровень доступа к разделу
$sectionPermission = CIBlockSection::checkAccess($sectionId, $iblockId, $userId, 'read');
if ($sectionPermission) {
echo 'Раздел доступен для чтения';
}
Результат: true или false.
Пример 5: Кеширование прав в AccessController
use Bitrix\Main\Data\Cache;
protected function getUserPermissionsCache(int $userId): array
{
$cache = Cache::createInstance();
$cacheKey = 'user_'.$userId.'_permissions';
$cachePath = '/user_permissions/';
if ($cache->initCache(86400, $cacheKey, $cachePath)) {
return $cache->getVars();
}
$permissions = $this->loadPermissionsFromDb($userId);
$cache->startDataCache();
$cache->endDataCache($permissions);
return $permissions;
}
Эти примеры покрывают большинство сценариев управления доступом в Bitrix. Выбор конкретной реализации зависит от архитектуры модуля и требований к производительности.