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