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