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