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