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