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