Типовая задача: нужно создать отдельную группу пользователей Битрикс, участники которой могут заходить в административную панель, работать с разделом «Контент» и одним кастомным разделом — например, «Инструменты». При этом «Магазин», «Настройки», «Пользователи», «Переход в Битрикс24» и остальные пункты должны быть скрыты и недоступны.
На первый взгляд кажется, что достаточно создать группу и назначить ей права доступа 1С-Битрикс. На практике это не работает, потому что доступ к административной части состоит сразу из нескольких независимых уровней.
Разберём реальный кейс по слоям: от создания группы до фильтрации административного меню.Содержание
- Почему просто выдать права недостаточно
- Слой 1. Создаём группу со стабильным STRING_ID
- Слой 2. Выдаём права модулей
- Слой 3. Даём доступ к нужным инфоблокам
- Слой 4. Открываем /bitrix/admin/ для группы
- Слой 5. Исправляем проверки IsAdmin() в кастомных страницах
- Скрываем лишние разделы административного меню
- Переносим всё на production через Sprint.Migration
- Чек-лист: что проверить
- Резюме
Почему просто выдать права недостаточно
Главная особенность Битрикс: понятие «имеет доступ к админке» на самом деле складывается из нескольких разных проверок.
У пользователя могут быть права на модуль iblock, но он всё равно получит ошибку доступа к файлу /bitrix/admin/.... Можно открыть выполнение административных PHP-файлов, но не дать прав на конкретные инфоблоки — тогда раздел «Контент» окажется пустым. Можно настроить всё это, но кастомная страница продолжит проверять $USER->IsAdmin() и не пустит пользователя.
Поэтому правильнее воспринимать доступ к админке Битрикс как последовательность:
- пользователь входит в нужную группу;
- группа получает права необходимых модулей;
- для инфоблоков назначаются отдельные права;
- группе разрешается выполнять файлы из
/bitrix/admin/; - кастомные административные страницы умеют распознавать ограниченную группу;
- из меню удаляются все разделы, которые пользователь видеть не должен.
Пропуск любого из этих слоёв приводит к странному на первый взгляд поведению.
Слой 1. Создаём группу со стабильным STRING_ID
Начинаем с отдельной группы. Например, назовём её «Ограниченные администраторы».
<?php
$group = new CGroup();
$groupId = $group->Add([
'ACTIVE' => 'Y',
'NAME' => 'Ограниченные администраторы',
'STRING_ID' => 'limited_admin',
'C_SORT' => 500,
]);
if (!$groupId) {
throw new RuntimeException($group->LAST_ERROR);
}
Критически важное поле здесь — STRING_ID.
Не стоит строить код вокруг числового ID группы:
$groupId = 17;
На dev-сервере группа может получить ID 17, на stage — 24, а на production — 31. Код сразу перестанет быть переносимым.
Вместо этого задаём стабильный строковый идентификатор и каждый раз резолвим настоящий ID:
<?php
use Bitrix\Main\GroupTable;
$group = GroupTable::getList([
'filter' => [
'=STRING_ID' => 'limited_admin',
],
'select' => [
'ID',
'NAME',
'STRING_ID',
],
'limit' => 1,
])-fetch();
if (!$group) {
throw new RuntimeException('Группа limited_admin не найдена');
}
$groupId = (int)$group['ID'];
Это особенно важно для миграций: STRING_ID одинаковый на всех окружениях, ID — нет.
Слой 2. Права модулей: b_module_group, а не задачи
Здесь находится первая популярная ловушка.
Права группы на модули хранятся в таблице:
b_module_group
и назначаются через:
CMain::SetGroupRight($moduleId, $groupId, $permission);
Например:
<?php
CMain::SetGroupRight('main', $groupId, 'R');
CMain::SetGroupRight('iblock', $groupId, 'R');
Конкретный уровень права нужно выбирать исходя из того, какие операции группа действительно должна выполнять.
Почему CGroup::SetTasks здесь не поможет
В Битрикс есть ещё таблица:
b_group_task
Из-за этого возникает соблазн назначить группе какую-либо «задачу» и ожидать, что пользователь получит доступ к административному модулю.
Но это другой механизм.
Например, задачи модуля iblock могут иметь:
BINDING = iblock
То есть это задачи, относящиеся к правам на конкретные инфоблоки, а не модульное право, необходимое для административных страниц.
Поэтому попытка решить доступ в админку через CGroup::SetTasks() может выглядеть логично, но не даст ожидаемого результата.
Когда ядру нужно проверить модульное право, используется примерно такая проверка:
<?php
$groups = $USER->GetUserGroupArray();
$right = $APPLICATION->GetGroupRight(
'iblock',
$groups
);
GetGroupRight() работает именно с модульными правами из b_module_group.
Итого: модульные права и задачи группы — два разных слоя. Не смешивайте их.
Слой 3. Доступ к «Контенту»: права конкретных инфоблоков
После назначения права на модуль iblock можно столкнуться со следующей ситуацией: пользователь заходит в административную панель, но нужных инфоблоков в «Контенте» всё равно не видит.
Причина в том, что для инфоблоков в простом режиме:
RIGHTS_MODE = S
права задаются отдельно для каждого инфоблока.
Они хранятся в:
b_iblock_group
и устанавливаются через CIBlock::SetPermission().
Пример:
<?php
$iblockId = 12;
$permissions = CIBlock::GetGroupPermissions($iblockId);
$permissions[$groupId] = 'X';
CIBlock::SetPermission(
$iblockId,
$permissions
);
Вместо X можно назначить более ограниченный уровень, например W, если полный доступ к инфоблоку пользователю не нужен.
Это важно ещё и потому, что административное меню модуля iblock формируется с учётом минимально необходимого права на конкретные инфоблоки. Поэтому одного модульного права недостаточно, чтобы раздел «Контент» начал отображать нужные сущности.
Почему SetPermission может выполняться долго
На больших каталожных инфоблоках вызов CIBlock::SetPermission() может занять заметное время.
Это не обязательно означает, что миграция зависла. При изменении прав ядро может пересчитывать связанные данные доступа. Для крупного каталога такая операция ощутимо тяжелее, чем простая запись одной строки в таблицу.
Это разовая операция, но при деплое стоит учитывать её продолжительность.
Слой 4. Файловый доступ к /bitrix/admin/ — главная засада
Допустим, группа существует. Права модулей назначены. Права на инфоблоки тоже.
Но при попытке открыть:
/bitrix/admin/iblock_admin.php
пользователь получает редирект на:
#authorize
или сообщение вроде:
Просмотр файла /bitrix/admin/... запрещён
Это самый неочевидный слой системы доступа.
Перед выполнением административной страницы Битрикс проверяет файловое право примерно через:
$APPLICATION->CanDoFileOperation(
'fm_view_file',
[SITE_ID, $path]
);
А в стандартном:
/bitrix/.access.php
обычно присутствует правило:
$PERM["admin"]["*"] = "D";
То есть каталог /bitrix/admin/ закрыт для обычных групп независимо от того, какие модульные права мы им назначили.
Отсюда и парадокс: право на модуль есть, но выполнить административный PHP-файл пользователь не может.
Разрешаем административные файлы только нашей группе
Добавляем отдельное правило:
$PERM["admin"]["17"] = "R";
где 17 — реальный ID нашей группы на конкретном сервере.
Групповое правило перекрывает общий запрет:
$PERM["admin"]["*"] = "D";
При этом R не превращает пользователя в полного администратора.
Мы лишь разрешили ему выполнять административные файлы. Уже внутри конкретной страницы продолжают работать проверки модульных прав, прав инфоблоков и дополнительная бизнес-логика.
Поэтому давать файловое право R ограниченной группе нормально при условии, что остальные уровни доступа настроены правильно.
Почему нельзя просто закоммитить .access.php
/bitrix/.access.php относится к файловой структуре ядра и во многих проектах вообще не хранится в Git.
Кроме того, числовой ID группы на production будет отличаться от dev.
Поэтому правило лучше устанавливать миграцией или deployment-скриптом, предварительно находя настоящий ID группы по STRING_ID.
Например:
<?php
use Bitrix\Main\Application;
function ensureAdminAccessForGroup(int $groupId): void
{
$file = Application::getDocumentRoot() . '/bitrix/.access.php';
if (!is_file($file)) {
throw new RuntimeException('Файл не найден: ' . $file);
}
$contents = file_get_contents($file);
if ($contents === false) {
throw new RuntimeException('Не удалось прочитать ' . $file);
}
$rule = '$PERM["admin"]["' . $groupId . '"]="R";';
$pattern = '/^\s*\$PERM\["admin"\]\["'
. preg_quote((string)$groupId, '/')
. '"\]\s*=\s*"[A-Z]";\s*$/m';
if (preg_match($pattern, $contents)) {
$contents = preg_replace(
$pattern,
$rule,
$contents
);
} else {
$phpClosePosition = strrpos($contents, '?>');
if ($phpClosePosition === false) {
$contents = rtrim($contents)
. PHP_EOL
. $rule
. PHP_EOL;
} else {
$contents =
substr($contents, 0, $phpClosePosition)
. $rule
. PHP_EOL
. substr($contents, $phpClosePosition);
}
}
if (file_put_contents($file, $contents, LOCK_EX) === false) {
throw new RuntimeException('Не удалось изменить ' . $file);
}
}
Такой подход позволяет не хардкодить ID окружения и повторно запускать миграцию без создания дубликатов правила.
Слой 5. Кастомные административные страницы и IsAdmin()
Следующая проблема часто появляется в собственных модулях и старом проектном коде.
Внутри кастомной административной страницы можно встретить:
if (!$USER->IsAdmin()) {
die('Access denied');
}
Но IsAdmin() означает именно полного администратора Битрикс.
Если добавить нашу ограниченную группу в администраторы только ради прохождения этой проверки, весь смысл ограничения исчезнет: пользователь получит практически всю административную часть.
Лучше вынести проверку в единый класс доступа.
<?php
namespace Local\Security;
use Bitrix\Main\GroupTable;
final class Access
{
private const GROUP_STRING_ID = 'limited_admin';
private static $groupId;
public static function getGroupId(): ?int
{
if (self::$groupId !== null) {
return self::$groupId ?: null;
}
$group = GroupTable::getList([
'filter' => [
'=STRING_ID' => self::GROUP_STRING_ID,
],
'select' => [
'ID',
],
'limit' => 1,
])->fetch();
self::$groupId = $group ? (int)$group['ID'] : 0;
return self::$groupId ?: null;
}
public static function isLimitedAdmin(): bool
{
global $USER;
if (!is_object($USER) || !$USER->IsAuthorized()) {
return false;
}
$groupId = self::getGroupId();
if (!$groupId) {
return false;
}
$userGroups = array_map(
'intval',
$USER->GetUserGroupArray()
);
return in_array($groupId, $userGroups, true);
}
public static function canManage(): bool
{
global $USER;
return $USER->IsAdmin()
|| self::isLimitedAdmin();
}
}
Теперь вместо:
$USER->IsAdmin()
на наших административных страницах используем:
if (!\Local\Security\Access::canManage()) {
die('Access denied');
}
Плюс такого решения — вся логика находится в одном месте. Если позднее понадобится добавить ещё одну роль, не придётся искать десятки вызовов IsAdmin() по проекту.
Как скрыть разделы админки Битрикс через OnBuildGlobalMenu
После всех предыдущих шагов пользователь уже может работать в административной части. Но интерфейс всё ещё может показывать лишние разделы.
Теперь решаем задачу скрыть разделы админки Битрикс.
Глобальное административное меню формируется в CAdminMenu::Init(). Сначала создаются системные разделы вроде:
global_menu_desktop;global_menu_content;global_menu_store;global_menu_settings.
Затем вызывается событие:
OnBuildGlobalMenu
и к глобальным разделам добавляются пункты различных модулей.
Для ограниченной группы удобно использовать не чёрный, а белый список: оставить только явно разрешённые разделы.
Допустим, нам нужны:
- «Рабочий стол»;
- «Контент»;
- кастомный раздел «Инструменты» с кодом
global_menu_tools.
Регистрируем обработчик с высоким sort, чтобы он выполнялся после большинства остальных обработчиков меню:
<?php
AddEventHandler(
'main',
'OnBuildGlobalMenu',
[
'\Local\Admin\MenuFilter',
'onBuildGlobalMenu',
],
9999
);
Сам фильтр:
<?php
namespace Local\Admin;
use Local\Security\Access;
final class MenuFilter
{
public static function onBuildGlobalMenu(
&$aGlobalMenu,
&$aModuleMenu
): void {
global $USER;
if ($USER->IsAdmin()) {
return;
}
if (!Access::isLimitedAdmin()) {
return;
}
$allowedSections = [
'global_menu_desktop',
'global_menu_content',
'global_menu_tools',
];
foreach (array_keys($aGlobalMenu) as $menuCode) {
if (!in_array($menuCode, $allowedSections, true)) {
unset($aGlobalMenu[$menuCode]);
}
}
foreach ($aModuleMenu as $key => $menuItem) {
$parentMenu = $menuItem['parent_menu'] ?? '';
if (!in_array($parentMenu, $allowedSections, true)) {
unset($aModuleMenu[$key]);
}
}
}
}
Здесь принципиальны две вещи.
Во-первых, фильтр применяется только к пользователям нашей группы, которые не являются полными администраторами.
Во-вторых, используется белый список.
Если завтра после обновления Битрикс или установки нового модуля появится ещё один глобальный административный раздел, пользователь ограниченной группы его автоматически не увидит.
Скрытие меню — не механизм безопасности
Это важно: удаление пункта из $aGlobalMenu или $aModuleMenu само по себе не запрещает открыть страницу напрямую по URL.
Например, отсутствие пункта «Настройки» в левом меню ещё не означает, что:
/bitrix/admin/settings.php
недоступен.
Безопасность обеспечивают предыдущие уровни: модульные права и проверки внутри страниц.
Фильтрация меню нужна прежде всего для корректного и понятного интерфейса пользователя.
Почему нельзя удалить вообще всё
Есть ещё один нюанс: административный desktop.php ориентируется на итоговую структуру меню. Если глобальное меню окажется полностью пустым, поведение админки может выглядеть как отсутствие нормального доступа.
На практике global_menu_desktop обычно остаётся, поэтому при whitelist-подходе его лучше сохранять явно.
Переносим настройки на production через Sprint.Migration
Ручная настройка такой группы через административный интерфейс почти гарантированно приведёт к различиям между dev, stage и production.
Поэтому весь набор настроек лучше оформить миграцией.
Одна миграция должна последовательно:
- найти группу по
STRING_ID; - если группы нет — создать её;
- назначить необходимые права модулей через
CMain::SetGroupRight(); - найти необходимые инфоблоки и назначить права через
CIBlock::SetPermission(); - получить реальный ID созданной группы;
- идемпотентно добавить для него правило
Rв/bitrix/.access.php.
При этом ID не должен переноситься между серверами в конфигурации:
$groupId = 17;
Вместо этого всегда:
$group = \Bitrix\Main\GroupTable::getList([
'filter' => [
'=STRING_ID' => 'limited_admin',
],
'select' => [
'ID',
],
'limit' => 1,
])-fetch();
$groupId = (int)$group['ID'];
Миграция должна быть идемпотентной: повторный запуск не должен создавать вторую группу, дублировать правила или ломать уже назначенные права.
Как правильно тестировать
После настройки создайте отдельного тестового пользователя, добавьте его в новую группу и войдите под ним обычным способом через логин и пароль.
Не используйте для этого административную функцию «Авторизоваться под пользователем».
В нашем случае это оказалось отдельной ловушкой: такая авторизация не воспроизводила полноценную сессию административной панели, из-за чего вместо реальной картины постоянно появлялся #authorize.
Проверять ограниченного администратора нужно именно реальным входом в его аккаунт.
Чек-лист: что проверить
- У группы задан уникальный и стабильный
STRING_ID. - Код нигде не зависит от числового ID группы.
- Права модулей назначены через
CMain::SetGroupRight(). - Вы не пытаетесь заменить модульные права записями в
b_group_task. - Для нужных инфоблоков назначены отдельные права.
- Проверено значение
RIGHTS_MODEинфоблоков. - В
/bitrix/.access.phpдля группы разрешено чтение каталогаadmin. - ID для
.access.phpопределяется поSTRING_IDнепосредственно на текущем окружении. - Кастомные страницы не требуют исключительно
$USER->IsAdmin(). - Для кастомной функциональности используется единая проверка вроде
Access::canManage(). - Через
OnBuildGlobalMenuприменяется белый список административных разделов. - Для полных администраторов фильтрация меню отключена.
- Проверено, что скрытые страницы нельзя открыть напрямую без соответствующих модульных прав.
- Настройка проверена обычным входом под тестовым пользователем, а не через «Авторизоваться под пользователем».
Резюме
Основная ошибка при настройке ограниченной административной группы в 1С-Битрикс — искать один параметр, который отвечает за весь доступ.
Такого параметра нет.
Нужно последовательно пройти несколько независимых уровней:
- Группа со стабильным
STRING_ID. - Модульные права из
b_module_group. - Пер-инфоблочные права из
b_iblock_group. - Файловый доступ к
/bitrix/admin/через/bitrix/.access.php. - Проверки внутри кастомного кода вместо безусловного
IsAdmin(). - Фильтрация административного меню через
OnBuildGlobalMenu.
После этого можно получить действительно ограниченную административную роль: пользователь видит только «Рабочий стол», «Контент» и нужные бизнесу инструменты, но не получает доступ ко всему остальному сайту.
Именно разделение этих механизмов важно понимать разработчику, который настраивает права доступа 1С-Битрикс. Оно же объясняет большинство ситуаций, когда «права вроде выдали, но админка всё равно не работает».
NJSoft занимается разработкой и доработкой проектов на 1С-Битрикс: нестандартными административными интерфейсами, интеграциями, миграциями, оптимизацией производительности и сложной логикой разграничения доступа. Если стандартных настроек Битрикс недостаточно — можем спроектировать и реализовать решение под конкретную бизнес-логику.




