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