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