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

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

Статья описывает базовый сценарий интеграции прав доступа в модуль 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
Наверх