Разграничение прав доступа на уровне записей (RLS — Row Level Security) в платформе «1С:Предприятие» позволяет гибко настраивать видимость данных для различных пользователей. Однако перед разработчиками часто возникает сложная задача: как проверить, доступен ли конкретный объект данных (например, определенный контрагент или документ) для чтения или изменения конкретному пользователю информационной базы?
Стандартная платформенная функция ПравоДоступа() проверяет права только на уровне метаданных и не учитывает ограничения RLS для конкретной ссылки. Проанализируем несколько эффективных способов решения этой задачи: от современных механизмов БСП до классических низкоуровневых методов платформы.
В современных типовых конфигурациях (ERP, КА, УТ, БП 3.0), построенных на базе Библиотеки Стандартных Подсистем (БСП) версии 3.x, по умолчанию используется производительный (универсальный) режим RLS. Его ключевая особенность — предварительный расчет прав доступа.
Система хранит готовые связи ограничений в служебных регистрах сведений. Благодаря этому мы можем получить информацию о доступности объекта без авторизации под каждым пользователем, выполнив один простой запрос к базе данных:
КлючиДоступаКОбъектам хранит связь между конкретной ссылкой на объект (например, контрагентом) и его Ключом Доступа.КлючиДоступаПользователей связывает пользователей информационной базы с этими же Ключами Доступа.Рассмотрим пример запроса, который выбирает всех пользователей, имеющих доступ к проверяемому объекту:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КлючиПользователей.Пользователь КАК Пользователь
|ИЗ
| РегистрСведений.КлючиДоступаКОбъектам КАК КлючиОбъектов
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаПользователей КАК КлючиПользователей
| ПО КлючиОбъектов.КлючДоступа = КлючиПользователей.КлючДоступа
|ГДЕ
| КлючиОбъектов.Объект = &ПроверяемыйОбъект";
Запрос.УстановитьПараметр("ПроверяемыйОбъект", СсылкаНаОбъект);
ТаблицаДоступа = Запрос.Выполнить().Выгрузить();
Важное примечание: Этот запрос возвращает только тех пользователей, чьи права действительно ограничены на уровне RLS. Тех пользователей, у которых установлены полные права (или роли, для которых RLS на данный объект отключен), в этих регистрах не будет. Их доступность нужно добавить в итоговую таблицу программно.
Если в вашей конфигурации интегрирована БСП, вы можете использовать готовые методы общего модуля УправлениеДоступом. Функция ЕстьПраво() позволяет точечно проверить доступность конкретного объекта для любого пользователя ИБ.
Разберем пример обхода пользователей и проверки прав на чтение конкретного объекта:
ВыборкаПользователей = Справочники.Пользователи.Выбрать();
ТаблицаРезультатов = Новый ТаблицаЗначений;
ТаблицаРезультатов.Колонки.Добавить("Пользователь");
ТаблицаРезультатов.Колонки.Добавить("ДоступРазрешен");
Пока ВыборкаПользователей.Следующий() Цикл
Если ВыборкаПользователей.ПометкаУдаления Или ВыборкаПользователей.Недействителен Тогда
Продолжить;
КонецЕсли;
// Проверяем право на чтение конкретного объекта под пользователем
Доступ = УправлениеДоступом.ЕстьПраво("Чтение", СсылкаНаОбъект, ВыборкаПользователей.Ссылка);
НоваяСтрока = ТаблицаРезультатов.Добавить();
НоваяСтрока.Пользователь = ВыборкаПользователей.Ссылка;
НоваяСтрока.ДоступРазрешен = Доступ;
КонецЦикла;
Если в информационной базе производительный RLS отключен (или используется старая версия БСП), единственным надежным способом проверки RLS остается выполнение запроса с ключевым словом РАЗРЕШЕННЫЕ непосредственно в сеансе целевого пользователя.
Платформа «1С:Предприятие» позволяет администратору программно запускать фоновые задания под учетной записью любого пользователя без необходимости знать или временно менять его пароль. Для этого в методе ФоновыеЗадания.Выполнить() предусмотрен соответствующий параметр.
Выясним алгоритм реализации данного подхода:
ПроверитьДоступОбъекта), который будет выполняться в фоновом задании. Метод выполняет запрос РАЗРЕШЕННЫЕ и возвращает результат через временное хранилище.Пример кода экспортного метода общего модуля:
Процедура ВыполнитьПроверкуДоступаНаСервере(ПараметрыПроверки, АдресРезультата) Экспорт
СсылкаНаОбъект = ПараметрыПроверки.СсылкаНаОбъект;
ИмяТаблицы = СсылкаНаОбъект.Метаданные().Имя;
ТипМетаданных = СсылкаНаОбъект.Метаданные().Родитель().Имя; // Например: Справочники, Документы
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Ссылка КАК Ссылка
|ИЗ
| " + ТипМетаданных + "." + ИмяТаблицы + "
|ГДЕ
| Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
ДоступРазрешен = Не Запрос.Выполнить().Пустой();
ПоместитьВоВременноеХранилище(ДоступРазрешен, АдресРезультата);
КонецПроцедуры
Пример вызова проверки из основного сеанса под администратором:
ПараметрыПроверки = Новый Структура("СсылкаНаОбъект", СсылкаНаОбъект);
АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, Новый УникальныйИдентификатор);
// ИмяПользователяИБ - имя пользователя, права которого проверяются
Задание = ФоновыеЗадания.Выполнить(
"ИмяОбщегоМодуля.ВыполнитьПроверкуДоступаНаСервере",
Новый Массив(ПараметрыПроверки, АдресРезультата),
"КлючЗадания_" + ИмяПользователяИБ,
"Проверка доступа под " + ИмяПользователяИБ,
ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПользователяИБ)
);
Задание.ОжидатьЗавершения();
ДоступРазрешен = ПолучитьИзВременногоХранилища(АдресРезультата);
Если по каким-то причинам использование фоновых заданий невозможно (например, база работает в файловом режиме с ограничениями или требуется запустить внешнее COM-соединение), администратор может прибегнуть к обходному пути: временной подмене хэша пароля пользователя.
Объект ПользовательИнформационнойБазы имеет свойство СохраняемоеЗначениеПароля, доступное для чтения и записи пользователям с административными правами. С его помощью мы можем:
Посмотрим на практический пример реализации этого алгоритма:
ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПользователя);
СтарыйХэшПароля = ПользовательИБ.СохраняемоеЗначениеПароля;
// Устанавливаем временный пароль для авторизации соединения
ПользовательИБ.Пароль = "TemporaryPass_123!";
ПользовательИБ.Записать();
ДоступРазрешен = Ложь;
Попытка
// Создаем внешнее соединение под тестируемым пользователем
COMКоннектор = Новый COMОбъект("V83.COMConnector");
СтрокаСоединения = "File=""C:\Base1C""; Usr=""" + ИмяПользователя + """; Pwd=""TemporaryPass_123!"";";
Соединение = COMКоннектор.Connect(СтрокаСоединения);
// Внутри соединения выполняем запрос РАЗРЕШЕННЫЕ
ДоступРазрешен = Соединение.ИмяОбщегоМодуля.ПроверитьДоступностьОбъекта(СсылкаНаОбъект);
Исключение
ЗаписьЖурналаРегистрации("ПроверкаДоступа", УровеньЖурналаРегистрации.Предупреждение, , , ОписаниеОшибки());
КонецПопытки;
// Обязательно восстанавливаем исходный хэш пароля пользователя в базе данных
ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПользователя);
ПользовательИБ.СохраняемоеЗначениеПароля = СтарыйХэшПароля;
ПользовательИБ.Записать();
Важное предупреждение безопасности: Восстановление хэша пароля обязательно должно происходить вне зависимости от того, возникли ли ошибки при выполнении внешнего соединения. Код сброса хэша обратно всегда должен находиться за пределами блока потенциально опасных операций или в блоке гарантированного завершения алгоритма.