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