Как наложить ограничения RLS на данные, полученные из внешней базы через веб-сервисы в отчете СКД

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке сложных распределенных систем часто возникает задача построения отчетов в одной базе (потребителе) на основе данных, хранящихся в другой базе (источнике). Стандартный механизм получения данных через веб-сервисы под техническим пользователем с полными правами создает серьезную проблему безопасности: данные прилетают «как есть», без учета прав доступа текущего пользователя в основной базе. В этой статье мы разберем, как правильно интегрировать механизм RLS (Record Level Security) в процесс получения внешних данных и как заставить Систему компоновки данных (СКД) фильтровать информацию из таблицы значений.

Вариант 1: Установка параметров сеанса в базе-источнике (через единого пользователя)

Это один из самых гибких подходов, который позволяет использовать уже настроенные правила RLS в базе-источнике, не создавая при этом сотни учетных записей. Суть метода заключается в использовании «пула» соединений под одной служебной учетной записью, но с динамической подменой контекста безопасности.

Рассмотрим алгоритм реализации:

  1. В базе-источнике создаем метод веб-сервиса, который первым параметром принимает идентификатор (или логин) пользователя из базы-потребителя.
  2. Внутри метода веб-сервиса, используя привилегированный режим, мы устанавливаем значения в ПараметрыСеанса, на которых завязаны правила RLS в этой базе (например, ПараметрыСеанса.РазрешенныеОрганизации или ПараметрыСеанса.ТекущийПользователь).
  3. Выполняем запрос к данным. Поскольку параметры сеанса установлены, платформа автоматически применит RLS к запросу.

Посмотрим на пример кода в методе веб-сервиса базы-источника:


Функция ПолучитьДанныеПродаж(ИмяПользователяПотребителя, ПараметрыФильтра) Экспорт
    
    // Переходим в привилегированный режим, чтобы установить параметры сеанса
    УстановитьПривилегированныйРежим(Истина);
    
    // Находим соответствующего пользователя в текущей базе или определяем его права
    ПользовательСсылка = Справочники.Пользователи.НайтиПоНаименованию(ИмяПользователяПотребителя);
    
    // Заполняем параметры сеанса, которые используются в RLS (например, из БСП)
    ПараметрыСеанса.ТекущийПользователь = ПользовательСсылка;
    // Здесь может быть вызов процедуры инициализации параметров доступа
    // ПараметрыДоступа.УстановитьПараметрыСеанса(ПользовательСсылка);
    
    УстановитьПривилегированныйРежим(Ложь);
    
    // Теперь обычный запрос отработает с учетом RLS для установленного пользователя
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ * ИЗ РегистрНакопления.Продажи КАК Продажи";
    
    Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Важный нюанс: Чтобы этот метод работал, правила RLS в метаданных должны быть прописаны с использованием параметров сеанса, а не функции ТЕКУЩИЙПОЛЬЗОВАТЕЛЬ(), так как фактически сессия веб-сервиса запущена под служебным аккаунтом.

Вариант 2: Использование связей наборов данных в СКД (на стороне приемника)

Если мы не можем или не хотим менять логику во внешней базе, мы можем наложить фильтр уже в основной базе, где формируется отчет. Проанализируем ситуацию: у нас есть набор данных «Объект» (внешняя ТЗ) и нам нужно ограничить его по справочникам (например, Контрагенты), на которые в текущей базе уже действует RLS.

Для реализации этого метода выполним следующие шаги в конструкторе СКД:

  1. Создадим первый набор данных — Запрос. В тексте запроса просто выберем разрешенные элементы справочника: ВЫБРАТЬ Ссылка КАК Контрагент ИЗ Справочник.Контрагенты Благодаря тому, что запрос выполняется в обычном режиме, платформа сама добавит условия RLS к этому запросу.
  2. Создадим второй набор данных — Объект. В качестве имени объекта укажем нашу внешнюю ТаблицуЗначений.
  3. Перейдем на закладку Связи наборов данных. Настроим связь от набора «Запрос» (источник) к набору «Объект» (приемник) по полю Контрагент.

Проблема «Левого соединения»: По умолчанию СКД соединяет наборы данных левым соединением. Это значит, что если в ТЗ есть 500 контрагентов, а разрешено только 10, отчет выведет все 500 строк, но данные из первого набора для «запрещенных» будут пустыми. Чтобы имитировать Inner Join (внутреннее соединение) и полностью скрыть строки, выполним следующее:

Вариант 3: Использование Менеджера временных таблиц в программном коде

Рассмотрим более продвинутый способ, который позволяет объединить внешние данные и RLS текущей базы в одном SQL-запросе. Это обеспечит максимальную производительность при больших объемах данных.

Проанализируем шаги реализации:

  1. Получаем данные из веб-сервиса в ТаблицуЗначений.
  2. Создаем МенеджерВременныхТаблиц и помещаем туда эту таблицу.
  3. В СКД создаем набор данных «Запрос», который обращается к этой временной таблице и соединяет её с реальными таблицами базы.

Пример программной подготовки данных:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // 1. Получаем данные извне
    ТЗВнешниеДанные = ПолучитьДанныеИзВебСервиса();
    
    // 2. Готовим менеджер временных таблиц
    МенеджерВТ = Новый МенеджерВременныхТаблиц;
    ЗапросПодготовки = Новый Запрос("ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_ВнешниеДанные ИЗ &ТЗ КАК ТЗ");
    ЗапросПодготовки.МенеджерВременныхТаблиц = МенеджерВТ;
    ЗапросПодготовки.УстановитьПараметр("ТЗ", ТЗВнешниеДанные);
    ЗапросПодготовки.Выполнить();
    
    // 3. Настраиваем компоновщик
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
    
    // 4. Передаем менеджер ВТ в процессор компоновки
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
    
    // В тексте запроса СКД теперь можно писать:
    // ВЫБРАТЬ * ИЗ ВТ_ВнешниеДанные КАК ВнешниеДанные 
    // ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты ПО ВнешниеДанные.КонтрагентСсылка = Контрагенты.Ссылка
    
    // 5. Вывод результата...
КонецПроцедуры

Этот метод хорош тем, что RLS наложится автоматически на Справочник.Контрагенты, а внутреннее соединение отсечет все запрещенные записи из временной таблицы ВТ_ВнешниеДанные.

Вариант 4: Передача «Фильтра разрешенных ссылок» (Pre-filtering)

Выясним причину, по которой иногда стоит фильтровать данные еще до того, как они покинут базу-источник. Если внешняя база содержит миллионы записей, а пользователю разрешено видеть только сотню, гонять всю таблицу через сеть нерационально.

Мы можем программно получить список разрешенных идентификаторов в базе-потребителе и передать их в качестве параметра веб-сервису. В конфигурациях на базе БСП это делается так:


// Получаем массив разрешенных организаций для текущего пользователя
МассивРазрешенныхОрганизаций = УправлениеДоступом.ДоступныеЗначения(Метаданные.Справочники.Организации);

// Передаем этот массив в веб-сервис
Данные = Proxy.ПолучитьДанные(МассивРазрешенныхОрганизаций);

На стороне веб-сервиса этот массив вставляется в условие ГДЕ Ссылка В (&Массив). Таким образом, мы убиваем двух зайцев: соблюдаем безопасность и экономим трафик.

Резюме и рекомендации

Выбор конкретного метода зависит от архитектуры ваших баз:

Помните, что безопасность данных — это комплексная задача. Наложение RLS на внешние данные требует внимательности к типу соединения и способу передачи контекста пользователя, чтобы исключить утечку информации.

← На главную