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