При настройке ограничений доступа на уровне записей (RLS) разработчики часто сталкиваются с необходимостью фильтрации данных по реквизитам текущего пользователя. Типичный пример: ограничить видимость документов только тем подразделением, к которому привязан текущий пользователь. Однако простая попытка написать условие вида Подразделение = &ТекущийПользователь.Подразделение в конструкторе ограничений прав доступа приводит к ошибке или неработоспособности правила. Разберем подробно, почему так происходит, и как правильно решить эту задачу тремя разными способами.
Давайте проанализируем ситуацию с точки зрения платформы 1С:Предприятие 8.3. Когда мы пишем запрос в обычном коде, платформа автоматически выполняет LEFT JOIN к соответствующим таблицам при обращении через точку. В механизме RLS ситуация иная. Оптимизатор запросов должен преобразовать ваше условие в максимально эффективный SQL-запрос, который будет добавляться к каждому обращению к защищаемой таблице.
Если бы платформа позволяла обращаться через точку от параметра сеанса, это порождало бы неявные соединения с таблицами справочников при каждом запросе. Это критически снижает производительность базы данных, особенно в высоконагруженных системах. Именно поэтому прямое разыменование ссылок в параметрах сеанса внутри условий RLS ограничено или вовсе не поддерживается в зависимости от режима совместимости и версии платформы.
Рассмотрим первый способ, который позволяет обойти ограничение без изменения метаданных конфигурации. Мы можем использовать классический подзапрос SQL прямо в поле условия RLS. Вместо того чтобы пытаться «вытащить» подразделение из ссылки, мы обращаемся напрямую к таблице справочника Пользователи.
Схема условия будет выглядеть следующим образом:
ГДЕ Подразделение В
(ВЫБРАТЬ
Т.Подразделение
ИЗ
Справочник.Пользователи КАК Т
ГДЕ
Т.Ссылка = &ТекущийПользователь)
Преимущества этого метода:
Недостатки:
&ТекущийПользователь пуст, запрос может работать некорректно.Иногда платформа «теряет» тип параметра сеанса, из-за чего обращение к его свойствам становится невозможным. Рассмотрим вариант с принудительным приведением типа. В тексте ограничения можно попробовать использовать функцию ВЫРАЗИТЬ.
Пример синтаксиса:
ГДЕ Подразделение = ВЫРАЗИТЬ(&ТекущийПользователь КАК Справочник.Пользователи).Подразделение
Однако стоит учитывать, что в современных версиях платформы (особенно при включенном режиме «Производительный» для RLS) этот метод часто блокируется. Платформа стремится к тому, чтобы в условиях прав доступа использовались только простые типы или заранее подготовленные значения.
Наиболее правильным и производительным способом с точки зрения архитектуры 1С является вынос необходимого реквизита в отдельный параметр сеанса. Вместо того чтобы заставлять базу данных искать подразделение пользователя при каждом запросе, мы определим его один раз при входе пользователя в систему.
Разберем этот процесс по шагам:
Общие -> Параметры сеанса и создадим новый параметр, например, ТекущееПодразделение с типом СправочникСсылка.Подразделения.УстановкаПараметровСеанса пропишем логику заполнения этого параметра:
Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)
Если ТребуемыеПараметры = Неопределено Или ТребуемыеПараметры.Найти("ТекущееПодразделение") <> Неопределено Тогда
ПараметрыСеанса.ТекущееПодразделение = ПоискПодразделенияПользователя(ПараметрыСеанса.ТекущийПользователь);
КонецЕсли;
КонецПроцедуры
ГДЕ Подразделение = &ТекущееПодразделение
Почему это лучше? В этом случае SQL-сервер получает готовое значение (GUID подразделения) и делает прямое сравнение без лишних JOIN или подзапросов. Это дает колоссальный прирост скорости на больших таблицах.
Как мы выяснили, у пользователя может быть несколько подразделений (например, при совмещении должностей). В таком случае использование одиночного параметра сеанса не решит задачу. Разберем, как поступить в этой ситуации:
1. Установим тип параметра сеанса как ФиксированныйМассив.
2. В модуле сеанса соберем все доступные пользователю подразделения в этот массив.
3. В условии RLS будем использовать оператор В:
ГДЕ Подразделение В (&ДоступныеПодразделенияПользователя)
Выясним причину ситуации, когда условия по параметрам сеанса «вдруг» перестают работать (что упоминалось в сообщениях форума). Чаще всего это связано с фоновыми заданиями. Фоновые задания выполняются в отдельном сеансе, где параметры сеанса могут быть не инициализированы автоматически, если в коде модуля сеанса нет безусловной проверки. Если ваше условие RLS опирается на параметр, который в фоновом задании равен Неопределено или ПустаяСсылка, доступ к данным может быть полностью закрыт или открыт ошибочно.
Всегда проверяйте, чтобы процедура УстановкаПараметровСеанса корректно отрабатывала для любых видов соединений, включая фоновые и внешние (COM, WS).
Если ваша конфигурация разработана на базе Библиотеки стандартных подсистем (БСП), мы настоятельно рекомендуем не писать «самописные» условия RLS. В БСП реализован мощный механизм управления доступом через «Группы доступа» и «Профили». В этом случае вам достаточно лишь указать в метаданных использование стандартных шаблонов, таких как #ПоПодразделению, а вся логика работы с параметрами сеанса и кэшированием прав будет выполнена библиотекой автоматически. Это избавит вас от ошибок производительности и проблем с обновлением системы.