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

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

При настройке механизмов ограничения прав доступа на уровне записей (RLS) разработчики часто сталкиваются со специфическим поведением платформы «1С:Предприятие»: ограничение по табличным частям отлично работает при создании нового документа, но пропускает запись, если пользователь изменяет существующий документ и добавляет в него строки с запрещенной аналитикой.

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

Причина проблемы: как СУБД и RLS обрабатывают изменение табличных частей

Рассмотрим стандартную ситуацию: пользователю разрешено работать только с определенными подразделениями. На справочник Подразделения задан полный доступ (так как эти подразделения могут выбираться в других документах), но на документ наложено ограничение на право Изменение. Если в табличной части документа указано хотя бы одно запрещенное подразделение, документ не должен записываться или проводиться.

При использовании стандартного шаблона RLS для табличной части запрос ограничения доступа выглядит следующим образом:


ТекущаяТаблица ИЗ #ТекущаяТаблица КАК ТекущаяТаблица
ГДЕ НЕ 1 В
    (ВЫБРАТЬ ПЕРВЫЕ 1
        1
    ИЗ
        Документ.ПлатежноеПоручениеИсходящее.РасшифровкаПлатежа КАК ТабличнаяЧастьДокумента
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ПраваДоступаПользователейКОбъектам КАК ПраваДоступаПользователейКОбъектам
            ПО ПраваДоступаПользователейКОбъектам.ОбъектДоступа = ТабличнаяЧастьДокумента.ЦентрЗатрат
            И ПраваДоступаПользователейКОбъектам.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОграниченияПравДоступа.ПодразделенияПлатежныеДокументыЗапрет)
            И ПраваДоступаПользователейКОбъектам.Пользователь В (&ГруппыТекущегоПользователя)
            И ПраваДоступаПользователейКОбъектам.Чтение
    ГДЕ
        ТекущаяТаблица.Ссылка = ТабличнаяЧастьДокумента.Ссылка)

Для сравнения, ограничение, наложенное на реквизит шапки документа, работает безошибочно:


ТекущаяТаблица ИЗ #ТекущаяТаблица КАК ТекущаяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПраваДоступаПользователейКОбъектам КАК ПраваДоступаПользователейКОбъектам
    ПО ПраваДоступаПользователейКОбъектам.ОбъектДоступа = ТекущаяТаблица.Подразделение
    И ПраваДоступаПользователейКОбъектам.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОграниченияПравДоступа.ПодразделенияПлатежныеДокументыЗапрет)
    И ПраваДоступаПользователейКОбъектам.Пользователь В (&ГруппыТекущегоПользователя)
    И ПраваДоступаПользователейКОбъектам.Чтение
ГДЕ ПраваДоступаПользователейКОбъектам.ОбъектДоступа ЕСТЬ NULL

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

Проанализируем последовательность транзакции записи существующего документа в СУБД через замер профайлера SQL:

  1. Обновление шапки документа: Платформа отправляет SQL-инструкцию UPDATE к основной таблице документа. На этом этапе СУБД встраивает RLS-проверку. При проверке поля шапки (ТекущаяТаблица.Подразделение) используются новые данные, переданные в запросе обновления. Однако при обращении к табличной части через условие ТекущаяТаблица.Ссылка = ТабличнаяЧастьДокумента.Ссылка происходят выборка и соединение с физической таблицей табличной части, которая в этот момент еще содержит старые строки, ранее сохраненные в базе данных!
  2. Очистка и перезапись табличной части: После выполнения UPDATE шапки платформа выполняет команды DELETE для старых строк табличной части и INSERT для новых строк из памяти.
  3. Отсутствие повторной проверки ТЧ: Повторный RLS-контроль записанных строк табличной части на уровне базы данных платформой не выполняется в виде отдельного запроса сблокировки.

Таким образом, проверка RLS на изменение срабатывает до того, как новые строки табличной части фактически попадают в СУБД. В результате пользователь может открыть документ с разрешенным подразделением, добавить строку с запрещенным подразделением и успешно его записать.

Эффективные способы решения проблемы

Разберем три практических подхода к реализации ограничения доступа по табличным частям, исключающих данную архитектурную проблему.

1. Программный контроль в обработчике «ПередЗаписью»

Самый надежный и быстродействующий способ для конфигураций без БСП — вынос проверки логики изменений из RLS СУБД в программный код модуля объекта или подписки на событие.

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

  1. В RLS оставляем условие только на операцию Чтение (и при необходимости Добавление).
  2. В модуле объекта (или в подписке на событие ПередЗаписью) выполняем проверку содержимого табличной части объекта, находящегося в памяти (ЭтотОбъект).

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    Если Отказ Или ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |	1
        |ИЗ
        |	РегистрСведений.ПраваДоступаПользователейКОбъектам КАК ПраваДоступа
        |ГДЕ
        |	ПраваДоступа.ОбъектДоступа В(&МассивЦентровЗатрат)
        |	И ПраваДоступа.ОбластьДанных = Значение(Перечисление.ОбластиДанныхОграниченияПравДоступа.ПодразделенияПлатежныеДокументыЗапрет)
        |	И ПраваДоступа.Пользователь В(&ГруппыТекущегоПользователя)
        |	И ПраваДоступа.Чтение";
        
    МассивЦентровЗатрат = ЭтотОбъект.РасшифровкаПлатежа.ВыгрузитьКолонку("ЦентрЗатрат");
    Запрос.УстановитьПараметр("МассивЦентровЗатрат", МассивЦентровЗатрат);
    Запрос.УстановитьПараметр("ГруппыТекущегоПользователя", ПараметрыСеанса.ГруппыТекущегоПользователя);
    
    Результат = Запрос.Выполнить();
    Если НЕ Результат.Пустой() Тогда
        ВызватьИсключение "Документ содержит подразделения (центры затрат), запрещенные для использования текущим пользователем!";
    КонецЕсли;

КонецПроцедуры

2. Использование механизма ключей доступа БСП

Если разработка ведется в типовой или современной конфигурации на базе Библиотеки Стандартных Подсистем (БСП) с включенным производительным режимом RLS, проверка прав выполняется через регистры сведений КлючиДоступаКОбъектам.

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

  1. В модуле менеджера документа переопределяется процедура ЗаполнитьЗначенияИсточниковПолейДоступа или ЗаполнитьНаборыЗначенийДоступа.
  2. В описании источников данных указывается проверка поля табличной части с использованием уточнения ДляВсехСтрок или ДляОднойИзСтрок.
  3. При попытке записи объекта БСП формирует ключи доступа по фактическим данным объекта из оперативной памяти. Если ключ запрещен для пользователя, транзакция записи отменяется платформой.

3. Дублирование аналитики табличной части в шапку документа

Если необходимо использовать исключительно штатный RLS платформы без БСП и программных исключений в коде:

  1. Добавьте в документ служебный реквизит шапки или составной массив/строку со списком всех используемых в ТЧ подразделений.
  2. В обработчике ПередЗаписью автоматически заполняйте этот реквизит значениями из табличной части.
  3. Настройте RLS-ограничение не на табличную часть, а на данный реквизит шапки документа.Так как реквизиты шапки передаются непосредственно в параметрах SQL-команды UPDATE, ограничение RLS сработает корректно и заблокирует изменение документа.

Важные детали настройки интерфейса

Обратите внимание: если на сам справочник Подразделения пользователю предоставлены права на чтение, он сможет выбрать любое подразделение в форме документа. Ограничение RLS СУБД или программная проверка сработают именно в момент сохранения или проведения документа.

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

← На главную