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