При работе в крупных информационных базах на базе конфигурации «1С:Управление производственным предприятием» (УПП) часто возникает задача разграничения прав доступа. Типичный пример: руководство казначейства требует, чтобы менеджеры разных отделов (групп пользователей) не видели документы Заявка на расходование денежных средств друг друга. При этом стандартные механизмы настройки прав по организациям или подразделениям не всегда подходят из-за организационной структуры предприятия.
В данной статье мы подробно разберем, почему у автора темы на форуме не взлетел классический запрос RLS, проанализируем ограничения платформы «1С:Предприятие» при работе с иерархией в запросах ограничений и рассмотрим три надежных практических способа решения этой задачи.
Автор темы пытался использовать конструкции вида:
ГДЕ Ответственный = &ТекущийПользователь
// или
ГДЕ Родитель.Ответственный = &ТекущийПользователь
Выясним причину, почему эта логика ошибочна и система выдает ошибку:
Заявка на расходование денежных средств (как и у любого другого документа в системе) физически нет реквизита Родитель. Документы не являются иерархическими объектами. Иерархию (папки) имеет справочник Пользователи.Ответственный в документе ссылается на элемент справочника Пользователи. Чтобы проверить, находится ли ответственный за документ в той же папке, что и текущий сеансовый пользователь, синтаксически верный запрос должен был выглядеть так:
ГДЕ Ответственный.Родитель = &ТекущийПользователь.Родитель. Однако и такое решение использовать на практике категорически не рекомендуется.Попытка использовать обращение через точку к реквизитам родителя (Ответственный.Родитель.Родитель) или оператор В ИЕРАРХИИ непосредственно в тексте RLS приводит к двум критическим проблемам:
LEFT JOIN). Когда пользователь будет открывать динамический список документов, СУБД начнет соединять многотысячные таблицы документов с таблицами справочника пользователей и их родителей. Это вызовет колоссальные задержки при открытии списков и взаимные блокировки базы данных.Рассмотрим по шагам три профессиональных способа решения этой задачи.
Проанализируем ситуацию: чтобы база данных работала быстро, RLS-запрос должен быть максимально простым и сравнивать данные по индексам. Оптимальное решение — перенести всю тяжелую работу по расчету иерархии и состава доступных пользователей на этап запуска сеанса работы пользователя.
Выполним следующую последовательность действий:
Шаг 1. Создание параметра сеанса
В конфигураторе создадим новый параметр сеанса с именем ДоступныеПользователи. В качестве типа укажем ФиксированныйМассив.
Шаг 2. Заполнение параметра сеанса при старте системы
Откроем Модуль сеанса и найдем процедуру УстановкаПараметровСеанса. Пропишем код, который определит текущего пользователя, найдет его группу (папку) в справочнике Пользователи и соберет всех пользователей из этой папки в массив:
Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)
Если ТребуемыеПараметры = Неопределено Или ТребуемыеПараметры.Найти("ДоступныеПользователи") <> Неопределено Тогда
ТекущийПользовательСистемы = ПараметрыСеанса.ТекущийПользователь;
МассивПользователей = Новый Массив;
// Если пользователь не задан, доступ закрыт
Если ЗначениеЗаполнено(ТекущийПользовательСистемы) Тогда
// Получаем родительскую группу текущего пользователя
РодительПользователя = ТекущийПользовательСистемы.Родитель;
Если ЗначениеЗаполнено(РодительПользователя) Тогда
// Запрос для получения всех подчиненных пользователей в данной ветке иерархии
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Пользователи.Ссылка КАК Ссылка
|ИЗ
| Справочник.Пользователи КАК Пользователи
|ГДЕ
| Пользователи.Ссылка В ИЕРАРХИИ(&Родитель)";
Запрос.УстановитьПараметр("Родитель", РодительПользователя);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
МассивПользователей = РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка");
КонецЕсли;
Иначе
// Если пользователь лежит в корне справочника, он видит только себя
МассивПользователей.Добавить(ТекущийПользовательСистемы);
EndIf;
КонецЕсли;
// Записываем полученный список в параметр сеанса в виде ФиксированногоМассива
ПараметрыСеанса.ДоступныеПользователи = Новый ФиксированныйМассив(МассивПользователей);
КонецЕсли;
КонецПроцедуры
Шаг 3. Настройка RLS в роли
Теперь перейдем к настройке роли (например, создадим новую или отредактируем существующую роль для казначеев). В ограничении доступа на чтение для объекта Документ.ЗаявкаНаРасходованиеДенежныхСредств пропишем лаконичное и очень быстрое условие:
ГДЕ Ответственный В (&ДоступныеПользователи)
Почему это работает быстро? СУБД получает готовый плоский список идентификаторов пользователей. Сравнение происходит мгновенно по первичному ключу таблицы, без дополнительных тяжелых соединений таблиц.
Использовать папки внутри справочника Пользователи для разделения прав — это архитектурно негибкое решение. В реальном бизнесе один сотрудник может временно выполнять обязанности другого или входить сразу в несколько проектных групп (например, «Казначейство» и «Администрация»). Элемент справочника может находиться только в одной папке.
Для реализации связи «многие ко многим» в типовых конфигурациях 1С предназначен отдельный справочник ГруппыПользователей. В нем состав пользователей определяется табличной частью ПользователиГруппы.
Посмотрим на пример запроса RLS, который позволит пользователю видеть документы тех коллег, с которыми он состоит хотя бы в одной общей группе пользователей:
ТекущаяТаблица ИЗ #ТекущаяТаблица КАК ТекущаяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
ПО СоставГруппы.Пользователь = &ТекущийПользователь
ГДЕ ТекущаяТаблица.Ответственный В (
ВЫБРАТЬ РАЗЛИЧНЫЕ СоставГруппыОтветственного.Пользователь
ИЗ Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппыОтветственного
ГДЕ СоставГруппыОтветственного.Ссылка = СоставГруппы.Ссылка
)
Разберем логику запроса:
ГруппыПользователей) входит текущий авторизованный пользователь (&ТекущийПользователь).Ответственный входит в этот результирующий список.Разберем ситуацию, когда вносить изменения в конфигурацию (снимать с поддержки, дописывать код) крайне нежелательно. В УПП 1.3 уже встроен мощный готовый универсальный механизм разграничения прав доступа на уровне записей.
Нам достаточно правильно настроить систему в режиме «1С:Предприятие»:
Группы пользователей и создадим нужные группы сотрудников (например, «Отдел продаж Восток», «Отдел продаж Запад»). Заполним состав этих групп.Сервис -> Настройка доступа пользователей -> Настройка доступа на уровне записей.Пользователи, чьи документы им разрешено просматривать.Типовой механизм RLS в УПП автоматически свяжет эти настройки через регистры сведений НастройкиПравДоступаПользователей и задействует их при формировании списков документов. Документы казначейства по умолчанию используют реквизит Ответственный для сопоставления с видом доступа «Пользователи».
Выбор конкретного метода зависит от требований к производительности вашей системы и необходимости кастомизации: