При разработке конфигураций на базе Библиотеки стандартных подсистем (БСП) разработчики часто сталкиваются с ситуацией, когда ограничение доступа на уровне записей (RLS) ведет себя непоследовательно. В формах списков мы видим только разрешенные данные, но стоит выполнить запрос в консоли или программном коде с использованием ключевого слова ВЫБРАТЬ РАЗРЕШЕННЫЕ, как в результат попадают записи, которые пользователь видеть не должен. При этом вместо ссылок на объекты отображается надпись "Объект не найден".
Разберем подробно, почему так происходит, как работает "ленивое" наложение ограничений и какие шаги нужно предпринять для исправления этой проблемы.
В современных версиях БСП (начиная с 3.0) используется производительный режим RLS. В отличие от классического режима, где платформа добавляла сложные подзапросы к каждой таблице, производительный режим опирается на заранее рассчитанные таблицы прав (ключи доступа). Проанализируем механизм: если запрос выбирает только данные самого регистра (например, измерения-ссылки), но не обращается к реквизитам этих ссылок, платформа может применить оптимизацию.
Если в метаданных или настройках прав есть неточности, система не выполняет соединение с таблицей ограничений справочника в момент выполнения запроса к регистру. В итоге запись из регистра считывается, но когда система пытается получить Представление для ссылки, на которую у пользователя нет прав, RLS справочника срабатывает "постфактум", скрывая данные самого объекта. Это и порождает эффект битых ссылок в результате запроса.
Одним из самых быстрых способов заставить платформу выполнить проверку прав принудительно является обращение к любому полю объекта через точку в секции ГДЕ. Рассмотрим пример. Если у нас есть регистр сведений, где измерение Контрагент ограничено по RLS, обычный запрос может вернуть лишние строки:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
МойРегистр.Контрагент,
МойРегистр.Сумма
ИЗ
РегистрСведений.МойРегистр КАК МойРегистр
Чтобы отсечь запрещенные записи, добавим условие, которое заставит платформу выполнить JOIN с основной таблицей справочника, где RLS работает безусловно:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
МойРегистр.Контрагент,
МойРегистр.Сумма
ИЗ
РегистрСведений.МойРегистр КАК МойРегистр
ГДЕ
(НЕ МойРегистр.Контрагент.ЭтоГруппа) ИЛИ (МойРегистр.Контрагент.ЭтоГруппа)
Такое условие всегда истинно, но оно вынуждает оптимизатор запросов применить правила доступа справочника Контрагенты к выборке регистра. Также можно использовать функцию ПРЕДСТАВЛЕНИЕ(МойРегистр.Контрагент) или получать любое поле справочника в списке полей выборки.
Часто проблема кроется в том, что для конкретного регистра не были сформированы Ключи доступа. Если система "не видит" правил, по которым нужно фильтровать записи в конкретной выборке, она вернет все строки. Выполним проверку по шагам:
ИдентификаторыОбъектовМетаданных. Если объекта там нет или он помечен как удаленный, механизмы БСП работать не будут.ВладелецОбъектаДоступа или ОбъектДоступа. Без этого система не будет генерировать записи в технических регистрах КлючиДоступаКОбъектам и КлючиДоступаКРегистрам./C ЗапуститьОбновлениеИнформационнойБазы.ПриЗаполненииОграниченияДоступа.Для корректной работы производительного режима RLS измерения, по которым наложено ограничение, обязательно должны быть индексированы. Отсутствие индекса по полю ИзмерениеСсылкаНаСправочник может привести к тому, что платформа проигнорирует таблицы ключей доступа для оптимизации скорости.
Проверим код в модуле менеджера регистра. Он должен выглядеть примерно так:
Процедура ПриЗаполненииОграниченияДоступа(Ограничение) Экспорт
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(ИзмерениеСсылкаНаСправочник)";
КонецПроцедуры
А в самой роли для этого регистра должно быть прописано ограничение с использованием стандартных шаблонов БСП. Посмотрим на пример правильной настройки в роли:
#Если &ОграничениеДоступаНаУровнеЗаписейУниверсально #Тогда
#ДляРегистра("ИдентификаторыОбъектовМетаданных.РегистрСведенийМойРегистр", "ИзмерениеСсылкаНаСправочник", "", "", "", "")
#Иначе
#ПоЗначениям( "РегистрСведений.МойРегистр", "", "", "МойВидДоступа", "ИзмерениеСсылкаНаСправочник", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","")
#КонецЕсли
Выясним причину, по которой динамические списки отображают данные корректно, а запросы — нет. Динамические списки платформы 1С используют собственные механизмы построения запросов. Они автоматически добавляют выборку представлений и полей, необходимых для отрисовки интерфейса. Это неявно активирует те самые механизмы "принудительного" RLS, о которых мы говорили в первом решении. Прямой же запрос в консоли максимально "сырой", и если вы не обратились к полям связанного объекта, платформа выбирает данные из физической таблицы регистра напрямую, минуя фильтры связанных таблиц.
Важный момент: Если вы используете составной тип данных в измерении, стандартный шаблон #ДляРегистра может работать нестабильно. В этом случае рекомендуется явно указывать типы в коде модуля менеджера или ограничивать использование составных типов в полях, подлежащих фильтрации по RLS.
Подводя итог: для исправления работы RLS в запросах, во-первых, обеспечьте актуальность ключей доступа через обновление информационной базы, а во-вторых, при написании программных запросов всегда обращайтесь к полям ограничиваемых объектов (через точку) или используйте функцию ПРЕДСТАВЛЕНИЕ, чтобы активировать механизм фильтрации платформы.