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