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