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