Как ограничить просмотр сертификатов номенклатуры по роли и данным регистра сведений?

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

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

Разберем, как правильно реализовать такой отбор в коде формы обработки или отчета, выясним причину возможных проблем с производительностью и рассмотрим альтернативные варианты через механизм ограничения прав доступа (RLS).

Вариант 1. Построение запроса с проверкой роли пользователя

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

В исходной постановке задачи часто пытаются использовать подзапрос с конструкцией НЕ ... В (ВЫБРАТЬ ...) и передачей параметра-флага:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Номенклатура,
    |	Номенклатура.Сертификат КАК Сертификат
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	(&БезОтбора
    |		ИЛИ НЕ Номенклатура.Сертификат В
    |			(ВЫБРАТЬ
    |				ЗБ_СписокОтслеживаемыхSKU.Сертификат
    |			ИЗ
    |				РегистрСведений.ЗБ_СписокОтслеживаемыхSKU КАК ЗБ_СписокОтслеживаемыхSKU))";

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

Однако такая реализация имеет существенные архитектурные недостатки:

  1. Просадка производительности СУБД: Условие &БезОтбора ИЛИ ... заставляет оптимизатор базы данных выполнять полное сканирование таблицы (Index Scan или Table Scan), так как он не может заранее применить индексы.
  2. Неоптимальная конструкция подзапроса: Проверка НЕ ... В (...) при больших объемах данных в регистре работает крайне медленно по сравнению с соединением таблиц.
  3. Ошибка логики связи данных: Если один и тот же сертификат привязан к нескольким позициям номенклатуры, проверка только по полю Сертификат заблокирует показ даже для неотслеживаемых товаров. В регистре присутствуют два измерения — Номенклатура и Сертификат, следовательно, соединять и проверять нужно именно связку этих полей.

Посмотрим на оптимизированный вариант с динамическим формированием текста запроса и ЛЕВЫМ СОЕДИНЕНИЕМ:


Запрос = Новый Запрос;
ТекстЗапроса = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Номенклатура,
    |	Номенклатура.Сертификат КАК Сертификат
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура";

Если НЕ РольДоступна("ПечатьВсехСертификатов") Тогда
    // Исключаем отслеживаемые SKU через соединение и проверку на NULL
    ТекстЗапроса = ТекстЗапроса + "
    |	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СписокОтслеживаемыхSKU КАК Отслеживаемые
    |		ПО Номенклатура.Ссылка = Отслеживаемые.Номенклатура
    |		И Номенклатура.Сертификат = Отслеживаемые.Сертификат
    |ГДЕ
    |	Отслеживаемые.Номенклатура ЕСТЬ NULL";
КонецЕсли;

Запрос.Текст = ТекстЗапроса;
Результат = Запрос.Выполнить();

В этом случае, если пользователю доступна роль, соединение с регистром вообще не выполняется, и запрос отрабатывает мгновенно. Если роли нет, СУБД использует соединение по индексам измерений регистра.

Вариант 2. Точечная проверка при активизации строки в форме

В интерфейсных механизмах (например, в формах подбора или просмотра) сертификат часто отображается не общей таблицей, а для одной текущей строки, выбранной пользователем в списке номенклатуры. Выполнять тяжелый запрос ко всей таблице номенклатуры при каждом клике нерационально.

Рассмотрим, как выполнить быструю проверку конкретной позиции:


Если СертификатыХарактеристик = Ложь Тогда
    Если ЭлементыФормы.СписокНоменклатур.ТекущиеДанные = Неопределено Тогда
        Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
    Иначе
        Номенклатура = ЭлементыФормы.СписокНоменклатур.ТекущиеДанные.Ссылка;
    КонецЕсли;

    Если НЕ Номенклатура.ЭтоГруппа Тогда
        Сертификат = Номенклатура.Сертификат;
    Иначе
        Сертификат = Справочники.СертификатыНоменклатуры.ПустаяСсылка();
    КонецЕсли;
КонецЕсли;

// Проверяем доступность сертификата к просмотру
РазрешеноКПросмотру = Истина;

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

// Управляем отображением данных в интерфейсе
Если РазрешеноКПросмотру Тогда
    ЭтаФорма.ТекущийЭлемент = ЭтаФорма.ЭлементыФормы.СписокИзображений;
    // Код вывода изображения сертификата
Иначе
    // Очищаем область просмотра, чтобы не оставалось изображение предыдущего товара
    ЭлементыФормы.СписокИзображений.Значение = Неопределено;
    Сообщить("Просмотр сертификата по данной позиции ограничен правами доступа.", СтатусСообщения.Внимание);
КонецЕсли;

Важные нюансы: права на регистр и привилегированный режим

При использовании проверки через запрос к регистру сведений СписокОтслеживаемыхSKU важно учесть права рядового пользователя:

  1. Ошибка прав доступа: Если у пользователя без роли ПечатьВсехСертификатов нет права на чтение самого регистра сведений, выполнение запроса вызовет ошибку Нарушение прав доступа.
  2. Использование привилегированного режима: Для безопасного чтения данных проверки без раздачи лишних прав на регистр вызов запроса следует помещать в привилегированный общий модуль либо использовать метод УстановитьПривилегированныйРежим(Истина) в серверном контексте.

Вариант 3. Разграничение на уровне записей (RLS)

Если требуется запретить просмотр сертификатов не только в конкретной обработке, но и во всей системе (в общих списках, отчетах, связанных документах), целесообразно использовать механизм RLS (Record Level Security).

Проанализируем особенности настройки RLS для справочника СертификатыНоменклатуры:

  1. В роли, назначенной обычным пользователям, настраивается ограничение доступа на чтение справочника сертификатов с условием проверки отсутствия записи в регистре.
  2. В текстах программных запросов конфигурации при выборке данных обязательно должна присутствовать директива ВЫБРАТЬ РАЗРЕШЕННЫЕ. Без этой конструкции запрос, встретивший недоступный сертификат, завершится аварийно с ошибкой доступа.
  3. Пользователи с ролью ПечатьВсехСертификатов получают полные права на чтение без ограничений RLS, благодаря чему они видят полный состав данных без накладных расходов на проверки.
← На главную