При настройке прав доступа на уровне записей (RLS) разработчики часто сталкиваются с задачей ограничить видимость элементов иерархического справочника, например, предоставить пользователю доступ только к определенной группе номенклатуры и всем входящим в нее подпапкам. Однако язык ограничений RLS платформы «1С:Предприятие» не поддерживает конструкцию В ИЕРАРХИИ, которая доступна в обычном языке запросов. Разберем причины этого ограничения, возможные архитектурные решения и их влияние на производительность системы.
Попытка передать в параметр сеанса ссылку на разрешенную корневую группу и написать простое условие по полю Родитель приводит к тому, что список номенклатуры становится полностью пустым. Выясним причину такого поведения платформы:
Родитель не заполнен (является пустой ссылкой), поэтому платформа блокирует чтение самого верхнего каталога.Родитель является промежуточная подпапка, а не ссылка из параметра сеанса. В результате такие элементы также не проходят проверку.Проанализируем практическую ситуацию: если ограничение требуется не ради жесткой информационной безопасности, а исключительно для удобства работы пользователей (чтобы менеджеры или кассиры при подборе товара не путались в чужих каталогах), использование RLS является избыточным. Механизм RLS модифицирует каждый запрос к таблице номенклатуры и снижает общее быстродействие базы данных.
В обычных формах задачу сокрытия лишних веток можно эффективно решить на уровне формы списка или формы выбора.
Для жесткого ограничения области навигации пользователя конкретной веткой используем свойство НачальныйУзел табличного поля. Разберем пример кода в процедуре ПриОткрытии формы списка:
Процедура ПриОткрытии()
ПапкаТовары = Справочники.Номенклатура.НайтиПоНаименованию("Товары");
Если ЗначениеЗаполнено(ПапкаТовары) Тогда
ЭлементыФормы.СправочникСписок.НачальныйУзел = ПапкаТовары;
КонецЕсли;
КонецПроцедуры
Установка свойства НачальныйУзел полностью изолирует пользователя внутри заданной папки: верхние и соседние каталоги не отображаются в дереве, а переход выше заданного узла становится невозможен.
Если же требуется стандартный отбор, можно применить программную установку фильтра по иерархии:
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
ПапкаТовары = Справочники.Номенклатура.НайтиПоНаименованию("Товары");
Если ЗначениеЗаполнено(ПапкаТовары) Тогда
Отбор = СправочникСписок.Отбор;
Отбор.Ссылка.Значение = ПапкаТовары;
Отбор.Ссылка.ВидСравнения = ВидСравнения.ВИерархии;
Отбор.Ссылка.Использование = Истина;
КонецЕсли;
КонецПроцедуры
При использовании обычного отбора по иерархии соседние корневые папки могут оставаться в дереве в виде пустых узлов, поэтому свойство НачальныйУзел для обычных форм является более предпочтительным.
Если доступ необходимо закрыть на уровне СУБД, рассмотрим вариант построения RLS-запроса с перебором уровней вложенности через разыменование поля Родитель. Этот способ подходит для баз данных с небольшой глубиной иерархии (до 3–5 уровней).
Посмотрим на пример текста ограничения RLS с возможностью гибкого включения через параметр сеанса и проверкой прав по регистру сведений:
Справочник.Номенклатура КАК спр
ГДЕ НЕ &ТекущиеОграничения ИЛИ (
спр.Ссылка В (
ВЫБРАТЬ РАЗЛИЧНЫЕ Права.ОбъектДоступа
ИЗ РегистрСведений.ПраваДоступаПользователей КАК Права
ГДЕ Права.Пользователь В (&ГруппыТекущегоПользователя)
)
ИЛИ спр.Родитель В (
ВЫБРАТЬ РАЗЛИЧНЫЕ Права.ОбъектДоступа
ИЗ РегистрСведений.ПраваДоступаПользователей КАК Права
ГДЕ Права.Пользователь В (&ГруппыТекущегоПользователя)
И Права.ВидНаследованияПравДоступаИерархическихСправочников = ЗНАЧЕНИЕ(Перечисление.ВидыНаследованияПравДоступаИерархическихСправочников.РаспространитьНаПодчиненных)
)
ИЛИ спр.Родитель.Родитель В (
ВЫБРАТЬ РАЗЛИЧНЫЕ Права.ОбъектДоступа
ИЗ РегистрСведений.ПраваДоступаПользователей КАК Права
ГДЕ Права.Пользователь В (&ГруппыТекущегоПользователя)
И Права.ВидНаследованияПравДоступаИерархическихСправочников = ЗНАЧЕНИЕ(Перечисление.ВидыНаследованияПравДоступаИерархическихСправочников.РаспространитьНаПодчиненных)
)
ИЛИ спр.Родитель.Родитель.Родитель В (
ВЫБРАТЬ РАЗЛИЧНЫЕ Права.ОбъектДоступа
ИЗ РегистрСведений.ПраваДоступаПользователей КАК Права
ГДЕ Права.Пользователь В (&ГруппыТекущегоПользователя)
И Права.ВидНаследованияПравДоступаИерархическихСправочников = ЗНАЧЕНИЕ(Перечисление.ВидыНаследованияПравДоступаИерархическихСправочников.РаспространитьНаПодчиненных)
)
)
Важный нюанс: цепочки разыменования Родитель.Родитель... приводят к множественным неявным левым соединениям (LEFT JOIN) таблицы с самой собой на уровне SQL-сервера. Использование операторов ИЛИ между соединениями делает невозможным эффективное использование индексов СУБД, вызывая сканирование таблиц (Table Scan / Index Scan) при каждом запросе.
Наиболее производительным архитектурным решением для строгого разграничения прав по иерархии является создание служебного непериодического регистра сведений (например, СоставИерархииНоменклатуры).
Разберем структуру и логику работы этого метода:
Объект (тип СправочникСсылка.Номенклатура) и Предок (тип СправочникСсылка.Номенклатура).ПриЗаписи справочника Номенклатура.
Справочник.Номенклатура КАК спр
ГДЕ спр.Ссылка В (
ВЫБРАТЬ Иерархия.Объект
ИЗ РегистрСведений.СоставИерархииНоменклатуры КАК Иерархия
ГДЕ Иерархия.Предок В (&ДоступныеПапки)
)
Такой подход обеспечивает максимальное быстродействие запросов на стороне СУБД, устраняет необходимость сложных конструкций с ИЛИ и корректно отрабатывает иерархию любой глубины.
При полном закрытии чтения элементов номенклатуры через RLS неизбежно возникают побочные эффекты:
Если задача заключается исключительно в том, чтобы запретить пользователям выбирать и продавать определенные позиции, рекомендуется оставлять право Чтение без ограничений, а правила RLS или программные проверки в подписке на событие ПередЗаписью устанавливать только для прав Добавление и Изменение.