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

Пример интеграции прав доступа

Статья описывает базовый сценарий интеграции прав доступа в модуль example. После настройки модуль сможет хранить роли, показывать таблицу прав через BX.UI.AccessRights и проверять доступ пользователя методом ExampleAccessController::can().

Как работает проверка доступа

Класс Bitrix\Main\Access\BaseAccessController принимает идентификатор пользователя, действие, идентификатор объекта и дополнительные параметры:

$canEdit = ExampleAccessController::can(
	$userId,
	ActionDictionary::ACTION_EDIT,
	$exampleId
);

Метод can() приводит идентификатор объекта к int, вызывает loadItem(), создает правило по коду действия и запускает execute(). Если правило не найдено, контроллер выбрасывает UnknownActionException.

Правило ищется в пространстве имен Rule рядом с контроллером. Код действия преобразуется в имя класса: create — в CreateRule, edit — в EditRule, task_admin — в TaskAdminRule.

Создать словари действий и разрешений

Класс действий хранит коды операций, которые проверяет контроллер доступа:

<?php

namespace Bitrix\Example\Access;

final class ActionDictionary
{
	public const ACTION_CREATE = 'create';
	public const ACTION_EDIT = 'edit';
}

Класс разрешений хранит права, которые администратор включает в таблице BX.UI.AccessRights. Значения VALUE_NO = 0 и VALUE_YES = 1 уже определены в базовом PermissionDictionary.

<?php

namespace Bitrix\Example\Access\Permission;

class PermissionDictionary extends \Bitrix\Main\Access\Permission\PermissionDictionary
{
	public const EXAMPLE_CREATE = 'example_create';
	public const EXAMPLE_EDIT_OWN = 'example_edit_own';
	public const EXAMPLE_EDIT_ALL = 'example_edit_all';
}

Добавьте языковой файл рядом с PermissionDictionary. Метод PermissionDictionary::getPermission() возьмет из него название права и подсказку для таблицы:

<?php

$MESS['EXAMPLE_CREATE'] = 'Создание записей';
$MESS['EXAMPLE_EDIT_OWN'] = 'Редактирование своих записей';
$MESS['EXAMPLE_EDIT_ALL'] = 'Редактирование всех записей';
$MESS['HINT_EXAMPLE_EDIT_ALL'] = 'Право позволяет редактировать все записи модуля example.';

Создать таблицы ролей и разрешений

Для хранения прав нужны три ORM-класса:

  • ExampleRoleTable хранит роли с полями ID и NAME;
  • ExampleRoleRelationTable хранит привязки роли к пользователям, отделам и группам с полями ROLE_ID и RELATION;
  • ExamplePermissionTable хранит значения прав роли с полями ROLE_ID, PERMISSION_ID и VALUE.
<?php

namespace Bitrix\Example\Access\Role;

use Bitrix\Main\Access\Role\AccessRoleTable;

class ExampleRoleTable extends AccessRoleTable
{
	public static function getTableName(): string
	{
		return 'b_example_role';
	}
}
<?php

namespace Bitrix\Example\Access\Role;

use Bitrix\Main\Access\Role\AccessRoleRelationTable;

class ExampleRoleRelationTable extends AccessRoleRelationTable
{
	public static function getTableName(): string
	{
		return 'b_example_role_relation';
	}
}
<?php

namespace Bitrix\Example\Access\Permission;

use Bitrix\Main\Access\Permission\AccessPermissionTable;

class ExamplePermissionTable extends AccessPermissionTable
{
	public static function getTableName(): string
	{
		return 'b_example_permission';
	}
}

Создайте эти таблицы при установке модуля. Базовые ORM-классы описывают поля, но не создают таблицы в базе данных автоматически.

Создайте утилитный класс для работы с ролями. Он связывает базовый RoleUtil с ORM-классами модуля:

<?php

namespace Bitrix\Example\Access\Role;

use Bitrix\Main\Access\Role\RoleUtil;

class ExampleRoleUtil extends RoleUtil
{
	protected static function getRoleTableClass(): string
	{
		return ExampleRoleTable::class;
	}

	protected static function getRoleRelationTableClass(): string
	{
		return ExampleRoleRelationTable::class;
	}

	protected static function getPermissionTableClass(): string
	{
		return \Bitrix\Example\Access\Permission\ExamplePermissionTable::class;
	}

	protected static function getRoleDictionaryClass(): ?string
	{
		return null;
	}
}

Создать модель объекта и контроллер

Модель объекта должна реализовать Bitrix\Main\Access\AccessibleItem. Если правило проверяет владельца записи, добавьте в модель метод, который вернет идентификатор владельца.

<?php

namespace Bitrix\Example\Access;

use Bitrix\Main\Access\AccessibleItem;

class ExampleModel implements AccessibleItem
{
	private int $id;
	private int $ownerId;

	public function __construct(int $id, int $ownerId)
	{
		$this->id = $id;
		$this->ownerId = $ownerId;
	}

	public static function createFromId(int $itemId): AccessibleItem
	{
		$row = ExampleTable::getRow([
			'select' => ['ID', 'CREATED_BY'],
			'filter' => ['=ID' => $itemId],
		]);

		if (!$row)
		{
			return new self(0, 0);
		}

		return new self((int)$row['ID'], (int)$row['CREATED_BY']);
	}

	public function getId(): int
	{
		return $this->id;
	}

	public function getOwnerId(): int
	{
		return $this->ownerId;
	}
}

Контроллер загружает пользователя и объект. Для действий без объекта loadItem() может вернуть null, потому что BaseAccessController допускает ?AccessibleItem.

<?php

namespace Bitrix\Example\Access;

use Bitrix\Main\Access\AccessibleItem;
use Bitrix\Main\Access\BaseAccessController;
use Bitrix\Main\Access\Model\UserModel;
use Bitrix\Main\Access\User\AccessibleUser;

class ExampleAccessController extends BaseAccessController
{
	protected function loadItem(?int $itemId = null): ?AccessibleItem
	{
		if (!$itemId)
		{
			return null;
		}

		return ExampleModel::createFromId($itemId);
	}

	protected function loadUser(int $userId): AccessibleUser
	{
		return UserModel::createFromId($userId);
	}
}

Создать правила

Каждое действие должно иметь отдельное правило в пространстве имен Bitrix\Example\Access\Rule. Правило наследует AbstractRule и реализует execute(?AccessibleItem $item = null, $params = null): bool.

<?php

namespace Bitrix\Example\Access\Rule;

use Bitrix\Example\Access\Permission\PermissionDictionary;
use Bitrix\Main\Access\AccessibleItem;
use Bitrix\Main\Access\Rule\AbstractRule;

class CreateRule extends AbstractRule
{
	public function execute(?AccessibleItem $item = null, $params = null): bool
	{
		if ($this->user->isAdmin())
		{
			return true;
		}

		return $this->user->getPermission(PermissionDictionary::EXAMPLE_CREATE)
			=== PermissionDictionary::VALUE_YES;
	}
}
<?php

namespace Bitrix\Example\Access\Rule;

use Bitrix\Example\Access\ExampleModel;
use Bitrix\Example\Access\Permission\PermissionDictionary;
use Bitrix\Main\Access\AccessibleItem;
use Bitrix\Main\Access\Rule\AbstractRule;

class EditRule extends AbstractRule
{
	public function execute(?AccessibleItem $item = null, $params = null): bool
	{
		if ($this->user->isAdmin())
		{
			return true;
		}

		if (
			$this->user->getPermission(PermissionDictionary::EXAMPLE_EDIT_ALL)
			=== PermissionDictionary::VALUE_YES
		)
		{
			return true;
		}

		if (!$item instanceof ExampleModel)
		{
			return false;
		}

		return $item->getOwnerId() === $this->user->getUserId()
			&& $this->user->getPermission(PermissionDictionary::EXAMPLE_EDIT_OWN)
				=== PermissionDictionary::VALUE_YES;
	}
}

Подготовить данные для BX.UI.AccessRights

Компонент BX.UI.AccessRights принимает два массива:

  • accessRights — секции и права, которые нужно показать в таблице;
  • userGroups — роли, участники ролей и текущие значения прав.
<?php

namespace Bitrix\Example\Access\Component;

use Bitrix\Example\Access\Permission\PermissionDictionary;
use Bitrix\Example\Access\Role\ExampleRoleUtil;
use Bitrix\Main\Access\AccessCode;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\UI\AccessRights\DataProvider;

class ConfigPermissions
{
	public function getAccessRights(): array
	{
		return [
			[
				'sectionTitle' => Loc::getMessage('EXAMPLE_ACCESS_SECTION') ?? 'Example',
				'rights' => [
					PermissionDictionary::getPermission(PermissionDictionary::EXAMPLE_CREATE),
					PermissionDictionary::getPermission(PermissionDictionary::EXAMPLE_EDIT_OWN),
					PermissionDictionary::getPermission(PermissionDictionary::EXAMPLE_EDIT_ALL),
				],
			],
		];
	}

	public function getUserGroups(): array
	{
		$roles = [];
		$rows = ExampleRoleUtil::getRoles();

		foreach ($rows as $row)
		{
			$roleId = (int)$row['ID'];
			$roles[] = [
				'id' => $roleId,
				'title' => $row['NAME'],
				'accessRights' => $this->getRoleAccessRights($roleId),
				'members' => $this->getRoleMembers($roleId),
			];
		}

		return $roles;
	}

	private function getRoleAccessRights(int $roleId): array
	{
		$rights = [];
		$permissions = (new ExampleRoleUtil($roleId))->getPermissions();

		foreach ($permissions as $permissionId => $value)
		{
			$rights[] = [
				'id' => $permissionId,
				'value' => (string)$value,
			];
		}

		return $rights;
	}

	private function getRoleMembers(int $roleId): array
	{
		$members = [];
		$rows = (new ExampleRoleUtil($roleId))->getMembers();

		while ($row = $rows->fetch())
		{
			$accessCode = (string)$row['RELATION'];
			$code = new AccessCode($accessCode);
			$member = (new DataProvider())->getEntity($code->getEntityType(), $code->getEntityId());

			$members[$accessCode] = $member->getMetaData();
		}

		return $members;
	}
}

Для секции добавьте языковую строку:

<?php

$MESS['EXAMPLE_ACCESS_SECTION'] = 'Права на записи';

Передать данные в компонент настройки прав

В PHP-компоненте подготовьте данные:

<?php

use Bitrix\Example\Access\Component\ConfigPermissions;

class ExampleConfigPermissionsComponent extends CBitrixComponent
{
	public function executeComponent(): void
	{
		$configPermissions = new ConfigPermissions();

		$this->arResult['USER_GROUPS'] = $configPermissions->getUserGroups();
		$this->arResult['ACCESS_RIGHTS'] = $configPermissions->getAccessRights();

		$this->includeComponentTemplate();
	}
}

В шаблоне передайте подготовленные массивы в BX.UI.AccessRights. Подробное описание параметров, подключения bitrix:main.ui.selector и панели кнопок смотрите в статье о компоненте BX.UI.AccessRights.

<?php

use Bitrix\Main\UI\Extension;

Extension::load(['ui.buttons', 'ui.icons', 'ui.notification', 'ui.accessrights']);
?>

<div id="example-config-permissions"></div>

<script>
	let AccessRights = new BX.UI.AccessRights({
		component: 'bitrix:example.config.permissions',
		renderTo: document.getElementById('example-config-permissions'),
		userGroups: <?= CUtil::PhpToJSObject($arResult['USER_GROUPS']) ?>,
		accessRights: <?= CUtil::PhpToJSObject($arResult['ACCESS_RIGHTS']) ?>,
	});

	AccessRights.draw();
</script>

Сохранить роли и права

При сохранении BX.UI.AccessRights вызывает action save у указанного компонента. В запросе приходит массив userGroups. Для каждой роли компонент передает:

  • id — идентификатор роли, для новой роли приходит 0;
  • title — название роли;
  • accessRights — массив прав с полями id и value;
  • accessCodes — привязки роли к пользователям, отделам и группам.
<?php

use Bitrix\Example\Access\Role\ExampleRoleUtil;
use Bitrix\Main\Engine\ActionFilter;
use Bitrix\Main\Engine\Controller;

class ExampleConfigPermissionsAjaxController extends Controller
{
	public function configureActions(): array
	{
		return [
			'save' => [
				'prefilters' => [
					new ActionFilter\Authentication(),
					new ActionFilter\HttpMethod(['POST']),
					new ActionFilter\Csrf(),
				],
			],
		];
	}

	public function saveAction(array $userGroups = []): void
	{
		foreach ($userGroups as $roleSettings)
		{
			$this->saveRoleSettings($roleSettings);
		}
	}

	private function saveRoleSettings(array $roleSettings): void
	{
		$roleId = (int)$roleSettings['id'];
		$roleTitle = (string)$roleSettings['title'];

		if ($roleId === 0)
		{
			$roleId = ExampleRoleUtil::createRole($roleTitle);
		}

		if ($roleId <= 0)
		{
			return;
		}

		$role = new ExampleRoleUtil($roleId);
		$role->updateTitle($roleTitle);

		$permissions = array_combine(
			array_column($roleSettings['accessRights'] ?? [], 'id'),
			array_column($roleSettings['accessRights'] ?? [], 'value')
		);

		$role->updatePermissions($permissions ?: []);
		$role->updateRoleRelations($roleSettings['accessCodes'] ?? []);
	}
}

Проверить доступ в коде модуля

Проверяйте доступ перед действием пользователя. Для создания запись еще не существует, поэтому идентификатор объекта можно не передавать:

<?php

use Bitrix\Example\Access\ActionDictionary;
use Bitrix\Example\Access\ExampleAccessController;

if (!ExampleAccessController::can($userId, ActionDictionary::ACTION_CREATE))
{
	ShowError('Access denied');
	return;
}

Для редактирования передайте идентификатор записи. Контроллер загрузит объект через loadItem() и передаст его в EditRule::execute():

<?php

use Bitrix\Example\Access\ActionDictionary;
use Bitrix\Example\Access\ExampleAccessController;

if (!ExampleAccessController::can($userId, ActionDictionary::ACTION_EDIT, $exampleId))
{
	ShowError('Access denied');
	return;
}

Особенности

  • Правило должно называться по коду действия. Для ACTION_EDIT = 'edit' создайте класс EditRule, а не ExampleEditRule.
  • Метод BaseAccessController::getRuleName() помечен как устаревший. Используйте стандартный поиск правил через RuleControllerFactory.
  • Значение права для toggler хранится как 0 или 1. В правилах сравнивайте результат getPermission() с PermissionDictionary::VALUE_YES.
  • Для сохранения ролей используйте наследник RoleUtil: он проверяет access code и сохраняет права в формате, который ожидают базовые таблицы доступа.
  • Таблица BX.UI.AccessRights показывает только те права, которые переданы в accessRights, и только те роли, которые переданы в userGroups.
  • Компонент настройки прав не заменяет проверку доступа в бизнес-логике. Проверку нужно вызывать в компонентах, контроллерах и обработчиках, которые выполняют действия пользователя.


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