Почему при использовании RLS в виртуальных таблицах остатков в 1С появляется «Объект не найден» и как это исправить?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При настройке прав доступа на уровне записей (Row-Level Security, или RLS) разработчики часто сталкиваются с неочевидным поведением платформы «1С:Предприятие». Типичный пример: для пользователя ограничили доступ к определенным элементам справочника (например, к банковским счетам или кассам). Однако при построении отчета или выполнении запроса к виртуальной таблице остатков регистра накопления пользователь все равно видит строки по заблокированным счетам. При этом в поле самого счета отображается грустное сообщение «<Объект не найден...>» (в старых версиях платформы) или «<Доступ ограничен>» (в новых версиях 8.3), а суммы остатков выводятся в полном объеме.

В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем внутренние механизмы работы платформы и рассмотрим три практических способа решения этой задачи с примерами кода.

Почему платформа «маскирует» объект, но выводит остатки?

Для того чтобы понять логику работы системы, проанализируем, как платформа обрабатывает конструкцию ВЫБРАТЬ РАЗРЕШЕННЫЕ применительно к виртуальным таблицам.

Когда мы пишем запрос к виртуальной таблице остатков (например, РегистрНакопления.ДенежныеСредства.Остатки), платформа проверяет права доступа именно на сам регистр накопления. Если у пользователя есть полные права на чтение этого регистра (и на него не наложен индивидуальный RLS), СУБД безоговорочно возвращает все строки из таблицы остатков.

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

Выяснив причину, рассмотрим три способа решения этой проблемы, начиная от самых простых программных обходов и заканчивая методологически правильными архитектурными решениями.

Решение №1. Параметризация виртуальной таблицы (Рекомендуемый и самый быстрый способ)

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

Рассмотрим по шагам, как это реализовать в коде:

  1. Сначала мы делаем быстрый и легкий запрос к справочнику с использованием ключевого слова РАЗРЕШЕННЫЕ. Платформа автоматически отсечет все запрещенные RLS элементы.
  2. Выгружаем полученные разрешенные ссылки в массив.
  3. Передаем этот массив в параметры виртуальной таблицы остатков в поле «Условие».

Посмотрим на пример реализации данного подхода:


// Шаг 1. Получаем список только тех счетов, к которым у пользователя есть доступ
ЗапросПоСчетам = Новый Запрос;
ЗапросПоСчетам.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	БанковскиеСчета.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.БанковскиеСчета КАК БанковскиеСчета";

ТаблицаРазрешенныхСчетов = ЗапросПоСчетам.Выполнить().Выгрузить();
МассивРазрешенныхСчетов = ТаблицаРазрешенныхСчетов.ВыгрузитьКолонку("Ссылка");

// Шаг 2. Запрашиваем остатки только по разрешенным счетам
ЗапросОстатков = Новый Запрос;
ЗапросОстатков.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	ДенежныеСредстваОстатки.БанковскийСчетКасса КАК БанковскийСчетКасса,
    |	ДенежныеСредстваОстатки.СуммаОстаток КАК СуммаОстаток
    |ИЗ
    |	РегистрНакопления.ДенежныеСредства.Остатки(, БанковскийСчетКасса В (&МассивРазрешенныхСчетов)) КАК ДенежныеСредстваОстатки";

ЗапросОстатков.УстановитьПараметр("МассивРазрешенныхСчетов", МассивРазрешенныхСчетов);
Результат = ЗапросОстатков.Выполнить();

Преимущества метода: Высокая производительность. Фильтрация данных происходит на уровне СУБД на самом раннем этапе. СУБД не тратит ресурсы на обработку заведомо запрещенных записей регистра.

Решение №2. Фильтрация в запросе через разыменование ссылки и «ЕСТЬ NULL»

Если по каким-то причинам мы не можем или не хотим делать предварительный запрос (например, запрос пишется внутри сложной динамической схемы или отчета), можно прибегнуть к обходному пути на уровне языка запросов 1С.

Мы можем обратиться «через точку» к реквизиту запрещенного объекта (например, к его уникальному идентификатору или полю Ссылка). При обращении через точку к полям объекта, на который у пользователя нет прав, платформа возвращает значение NULL.

Проанализируем пример такого запроса:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ДенежныеСредстваОстатки.БанковскийСчетКасса КАК БанковскийСчетКасса,
    ДенежныеСредстваОстатки.СуммаОстаток КАК СуммаОстаток
ИЗ
    РегистрНакопления.ДенежныеСредства.Остатки КАК ДенежныеСредстваОстатки
ГДЕ
    НЕ ДенежныеСредстваОстатки.БанковскийСчетКасса.Ссылка ЕСТЬ NULL

Как это работает изнутри: Обращение к полю Ссылка через точку заставляет платформу выполнить неявное левое соединение (LEFT JOIN) таблицы остатков регистра с таблицей справочника банковских счетов на уровне SQL-запроса. Для всех запрещенных RLS элементов справочника результатом соединения полей станет значение NULL. Условие ГДЕ НЕ ... ЕСТЬ NULL успешно исключит эти строки из финальной выборки.

Важное предупреждение о производительности: Мы настоятельно рекомендуем использовать этот способ с осторожностью. Неявные левые соединения сильно нагружают СУБД, особенно на больших базах данных. На таблицах с миллионами записей такой запрос может привести к серьезным просадкам производительности, зависаниям и превышению времени ожидания блокировок.

Решение №3. Наложение RLS на сам регистр накопления (Методологически верный способ)

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

Разберем этот процесс по шагам в конфигураторе 1С:

  1. Откроем нужную роль (например, роль с ограничениями для кассира или бухгалтера).
  2. Перейдем на вкладку «Шаблоны ограничений» и настроим шаблон доступа по аналогии со справочником (или используем готовые шаблоны, если они есть в конфигурации).
  3. В списке объектов найдем наш регистр накопления ДенежныеСредства.
  4. Для права «Чтение» добавим ограничение RLS по измерению БанковскийСчетКасса.

Пример текста ограничения RLS для регистра:


ГДЕ БанковскийСчетКасса В (
    ВЫБРАТЬ 
        БанковскиеСчета.Ссылка 
    ИЗ 
        Справочник.БанковскиеСчета КАК БанковскиеСчета
)

После настройки такого ограничения любой запрос со словом РАЗРЕШЕННЫЕ к регистру остатков будет автоматически отсекать строки по недоступным счетам на уровне самой платформы. Никаких «Объект не найден» больше не появится.

Современные реалии: RLS в управляемом приложении (платформа 8.3 и БСП)

Описанные выше проблемы наиболее остро стояли в старых конфигурациях (таких как «Управление торговлей 10.3», «Управление производственным предприятием 1.3»). В современных конфигурациях на платформе 8.3 («Управление торговлей 11», «ERP Управление предприятием», «Комплексная автоматизация») используется современный инструментарий БСП (Библиотеки стандартных подсистем).

Рассмотрим ключевые отличия современных механизмов:

← На главную