Как ограничить доступ пользователям к данным разных филиалов без использования RLS

Программист Управленческий учет IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С нередко возникают ситуации, когда стандартный механизм RLS (Record Level Security) по тем или иным причинам признается нецелесообразным. Это может быть связано с требованиями производительности, сложностью настройки или специфическим видением архитектуры со стороны аналитика. Рассмотрим подробно, как реализовать надежное разграничение данных по филиалам программными методами, обеспечив при этом удобство сопровождения и целостность базы данных.

Программная установка отборов в динамических списках

Для того чтобы пользователь видел в списках (документов или справочников) только информацию своего подразделения, нам необходимо программно вмешаться в настройки динамического списка управляемой формы. Разберем, почему простая попытка обратиться к ЭтаФорма.Список.Отбор напрямую на клиенте может не сработать, и как сделать это правильно.

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

Пример реализации в коде формы:


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

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

Централизация логики через общие модули

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

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


// В общем серверном модуле (например, РаботаСПравамиДоступа)

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

Теперь в каждой форме списка или журнала документов нам достаточно вызвать одну строку кода: РаботаСПравамиДоступа.УстановитьОтборПоФилиалу(Список); в процедуре ПриСозданииНаСервере.

Обеспечение безопасности: контроль записи данных

Важно понимать, что визуальные отборы в формах — это лишь «косметическая» мера. Продвинутый пользователь может попытаться программно создать документ или изменить филиал в существующем объекте. Чтобы закрыть эту дыру в безопасности, необходимо использовать подписки на события.

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


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

Механизм разделения данных (Общие реквизиты)

Если задача стоит максимально жестко, и мы хотим, чтобы платформа сама фильтровала данные на уровне СУБД, стоит рассмотреть механизм Разделения данных. Это более мощная альтернатива RLS.

Выясним, как это работает по шагам:

  1. Создаем объект метаданных ОбщийРеквизит с именем Филиал.
  2. Включаем его в состав тех документов и справочников, которые нужно разделять.
  3. Устанавливаем свойство «Разделение данных» в значение «Независимо».
  4. В МодулеСеанса прописываем установку параметров разделения при входе пользователя.

В этом случае платформа будет автоматически добавлять условие ГДЕ Филиал = &ЗначениеРазделителя во все запросы, включая отчеты и программный перебор выборок. Это избавляет программиста от необходимости вручную настраивать отборы в каждой форме.

Ограничение выбора данных в полях ввода

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

В модуле менеджера соответствующего объекта (например, справочника Склады) используем обработчик ОбработкаПолученияДанныхВыбора. Рассмотрим пример:


Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Склады.Ссылка КАК Ссылка,
        |   Склады.Наименование КАК Наименование
        |ИЗ
        |   Справочник.Склады КАК Склады
        |ГДЕ
        |   Склады.Филиал = &Филиал
        |   И Склады.Наименование ПОДОБНО &СтрокаПоиска";
    
    Запрос.УстановитьПараметр("Филиал", ПараметрыСеанса.ТекущийФилиал);
    Запрос.УстановитьПараметр("СтрокаПоиска", "%" + Параметры.СтрокаПоиска + "%");
    
    ДанныеВыбора = Новый СписокЗначений;
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        ДанныеВыбора.Добавить(Выборка.Ссылка, Выборка.Наименование);
    КонецЦикла;
    
КонецПроцедуры

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

← На главную