При настройке системы прав доступа в 1С разработчики часто сталкиваются с задачей ограничения видимости данных на уровне записей (Record Level Security — RLS). Классический пример: кладовщик Вася должен видеть поступления только по своему складу, а менеджер Петя должен иметь доступ ко всем складам компании. Если прописать простое ограничение в лоб через запрос к регистру сведений, можно столкнуться с ситуацией, когда пользователи без записей в этом регистре перестают видеть данные вообще. Разберем, как правильно реализовать такую логику, обеспечив при этом высокую производительность системы.
Рассмотрим ситуацию, описанную в исходном вопросе. Разработчик создал регистр ДоступностьПоСкладам и прописал в роли следующее ограничение:
ТекущаяТаблица ГДЕ ТекущаяТаблица.Склад В
(ВЫБРАТЬ
ДоступностьПоСкладам.ДоступныйСклад
ИЗ
РегистрСведений.ДоступностьПоСкладам КАК ДоступностьПоСкладам
ГДЕ
ДоступностьПоСкладам.Пользователь = &ТекущийПользователь)
Проанализируем этот запрос. Когда система проверяет права для Васи, у которого в регистре есть запись "Склад афроамериканцев", запрос возвращает этот склад, и условие В (Список) срабатывает. Однако для Пети записей в регистре нет. Вложенный запрос возвращает пустой результат. С точки зрения SQL условие превращается в Склад В (ПУСТОЙ_СПИСОК), что всегда ложно. В итоге Петя не видит ни одной записи. Нам же нужно реализовать логику: "Если записей в регистре нет — разрешить всё".
Прежде чем погружаться в сложные запросы RLS, проанализируем архитектуру ролей. В 1С права суммируются по принципу ЛОГИЧЕСКОГО ИЛИ. Если пользователю назначено несколько ролей, и хотя бы одна из них разрешает чтение записи без ограничений, то ограничения из других ролей будут проигнорированы платформой.
Рассмотрим по шагам, как это применить:
Склады_ОграничениеПоСкладам, где пропишем запрос RLS для Васи.Склады_ПолныйДоступ, где не будет никаких ограничений RLS на справочник Склады и связанные документы.В этом случае платформа увидит, что у Пети есть роль без RLS, и не будет тратить ресурсы на выполнение дополнительных проверок в запросах. Это самый производительный метод.
Использование вложенных запросов непосредственно в тексте RLS (как в примере с ВЫБРАТЬ... ИЗ ДоступностьПоСкладам) — это "антипаттерн". Такой запрос будет выполняться СУБД для каждой проверки, что может существенно замедлить работу динамических списков и отчетов. Рассмотрим более профессиональный подход с использованием ПараметровСеанса.
Шаг 1. Создание параметра сеанса. Создадим параметр сеанса ДоступныеСклады с типом ФиксированныйМассив.
Шаг 2. Заполнение при начале работы. В модуле сеанса, в процедуре УстановкаПараметровСеанса, проанализируем настройки пользователя. Если пользователю разрешены все склады, мы можем положить в массив специальный маркер (например, пустую ссылку) или просто оставить его пустым, но лучше предусмотреть флаг ОграничениеПоСкладамУстановлено.
Шаг 3. Настройка RLS через шаблон. Пропишем условие в роли, используя логику "разрешено всё, если маркер присутствует":
ТекущаяТаблица ИЗ #ТекущаяТаблица КАК ТекущаяТаблица
ГДЕ ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка) В (&ДоступныеСклады)
ИЛИ ТекущаяТаблица.Склад В (&ДоступныеСклады)
Проанализируем работу этого условия: если мы добавили в массив &ДоступныеСклады пустую ссылку для Пети, то первое условие ЗНАЧЕНИЕ(...) В (&ДоступныеСклады) всегда будет истинным для любой строки таблицы. Платформа оптимизирует такой запрос, и Петя получит доступ ко всем данным быстро.
Если по каким-то причинам использование параметров сеанса невозможно, и нужно решить задачу только средствами запроса RLS, можно использовать проверку на наличие записей в регистре. Разберем, как это сделать:
ТекущаяТаблица ГДЕ (НЕ 1 В
(ВЫБРАТЬ ПЕРВЫЕ 1 1
ИЗ РегистрСведений.ДоступностьПоСкладам КАК Т
ГДЕ Т.Пользователь = &ТекущийПользователь))
ИЛИ ТекущаяТаблица.Склад В
(ВЫБРАТЬ ДоступностьПоСкладам.ДоступныйСклад
ИЗ РегистрСведений.ДоступностьПоСкладам КАК ДоступностьПоСкладам
ГДЕ ДоступностьПоСкладам.Пользователь = &ТекущийПользователь)
Как это работает:
ИЛИ) проверяет, есть ли вообще записи для данного пользователя в регистре прав. Если записей нет, условие НЕ 1 В (...) возвращает истину, и доступ разрешается ко всем записям таблицы.ИЛИ) работает только если в регистре есть настройки, ограничивая выборку конкретными складами.Когда мы работаем с RLS, крайне важно, чтобы СУБД могла эффективно использовать индексы. Проанализируем рекомендации по настройке регистра ДоступностьПоСкладам:
Индексировать на измерение Пользователь. Так как RLS будет фильтровать данные по этому полю при каждом обращении, поиск должен быть мгновенным.Пользователь первым ведущим измерением.WHERE.В современных конфигурациях (таких как ERP, КА 2 или УТ 11) крайне редко пишут запросы RLS вручную. Рассмотрим, как это реализовано в подсистеме "Управление доступом" БСП. Вместо написания кода программист настраивает "Виды доступа".
Для нашей задачи это выглядело бы так:
Склады.Система БСП сама сформирует нужные запросы и параметры сеанса, используя свои оптимизированные шаблоны. Если вы разрабатываете конфигурацию с нуля, крайне рекомендуется интегрировать БСП для управления правами.
При написании запросов RLS важно помнить о поведении NULL. Если вы используете ЛЕВОЕ СОЕДИНЕНИЕ для проверки прав и запись в таблице прав отсутствует, результатом будет NULL. Любое сравнение с NULL (например, Склад = NULL) дает результат "Ложь", и запись будет скрыта. Всегда используйте ЕСТЬNULL(Поле, Значение) или конструкцию В (ВЫБРАТЬ...), чтобы избежать случайного скрытия данных.
Выясним, как проверить, правильно ли работает наше ограничение. Использовать обычную консоль запросов недостаточно, так как она часто запускается с полными правами. Рекомендуем следующие шаги:
УстановитьПривилегированныйРежим(Истина).Следуя этим методикам, мы построим гибкую и быструю систему прав доступа, которая корректно обрабатывает как ограниченных в правах пользователей, так и администраторов.