Настройка прав доступа и механизмов ограничения на уровне записей (RLS) в платформе «1С:Предприятие 8» — одна из самых нетривиальных задач для разработчика. Часто возникают ситуации, когда система ведёт себя непредсказуемо: выдает ошибки нарушения прав на тех участках кода, к которым у пользователя гарантированно есть полный доступ. Особенно ярко это проявляется при проведении документов в транзакции.
В этой статье мы подробно разберем классический сценарий такого поведения на примере конфигурации «Управление торговлей 10.3» (работающей в режиме обычного приложения), выясним истинные причины «призрачных» ошибок СУБД и рассмотрим надежные способы их устранения.
Рассмотрим ситуацию. При проведении документа «Реализация товаров и услуг» всё проходит штатно. Однако следующее за ним проведение связанного документа «Счет-фактура выданный» аварийно завершается с ошибкой:
Ошибка при вызове метода контекста (Выполнить): Ошибка выполнения запроса: У пользователя недостаточно прав на исполнение операции над базой данных.
При этом отладчик указывает на абсолютно безобидный и корректный запрос к регистру сведений Переменные:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Переменные.ЗначениеПеременной
|ИЗ
| РегистрСведений.Переменные КАК Переменные
|ГДЕ
| Переменные.НаименованиеПеременной = &ИмяПеременной";
Запрос.УстановитьПараметр("ИмяПеременной", ИмяПеременной);
Выборка = Запрос.Выполнить().Выбрать();
Разработчик проверяет права роли текущего пользователя и видит, что на регистр Переменные предоставлены полные права (на чтение, изменение и просмотр). Тип данных измерения — простая строка, а значение ресурса — ссылка на справочник Организации, к которому также есть полный доступ. Более того, при использовании альтернативного кода через выборку набора записей ошибка полностью повторяется:
Набор = РегистрыСведений.Переменные.СоздатьНаборЗаписей();
Набор.Отбор.НаименованиеПеременной.Установить(ИмяПеременной);
Набор.Прочитать();
Если Набор.Количество() > 0 Тогда
Возврат Набор[0].ЗначениеПеременной;
КонецЕсли;
Почему же система ругается на недостаток прав доступа там, где они точно есть?
Главный секрет кроется в поведении СУБД внутри транзакции. Проведение любого документа в 1С всегда выполняется строго в транзакции базы данных. Если в процессе проведения происходит критическая ошибка работы с базой (включая нарушение прав RLS при чтении или записи), СУБД переводит текущую транзакцию в особое состояние — «транзакция помечена для отмены» (doomed transaction).
Проанализируем цепочку событий, которая приводит к ошибке:
Попытка ... Исключение. С точки зрения кода 1С, ошибка «заглушается» в блоке Исключение, и программа продолжает работу дальше.Переменные, СУБД мгновенно отвергает его, так как транзакция уже «сломана».Попытка.Попытка разработчика временно обойти ограничение прав с помощью вызова УстановитьПривилегированныйРежим(ИСТИНА) не дает результатов по двум причинам:
УстановитьПривилегированныйРежим() на стороне клиента работает как «пустышка» — платформа игнорирует его без вызова ошибок, и RLS продолжает жестко проверять права.---
Давайте подробно разберем, как исправить эту ситуацию и как правильно диагностировать аналогичные проблемы в будущем.
РАЗРЕШЕННЫЕСамый быстрый и эффективный способ устранить ошибку «сломанной» транзакции, если она вызвана ограничениями RLS, — добавить ключевое слово РАЗРЕШЕННЫЕ в запросы, которые могут обращаться к закрытым для пользователя данным.
При использовании РАЗРЕШЕННЫЕ платформа 1С не генерирует исключение СУБД при обнаружении недоступных записей. Она просто молча отсекает их из результирующей выборки. Транзакция не помечается как аварийная, и последующий код выполняется без ошибок.
Изменим проблемный запрос, добавив ключевое слово:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Переменные.ЗначениеПеременной
|ИЗ
| РегистрСведений.Переменные КАК Переменные
|ГДЕ
| Переменные.НаименованиеПеременной = &ИмяПеременной";
Запрос.УстановитьПараметр("ИмяПеременной", ИмяПеременной);
Выборка = Запрос.Выполнить().Выбрать();
Важное примечание: Применяйте РАЗРЕШЕННЫЕ осознанно. Если вам жизненно необходимы данные, к которым у пользователя нет прав (например, константы настройки учета или параметры проведения), маскирование проблемы с помощью этого ключевого слова может привести к получению пустых значений и некорректному расчету себестоимости или налогов. В таких случаях нужно использовать привилегированные модули.
Если для алгоритма проведения крайне важно прочитать данные, защищенные RLS, без искажений, необходимо принудительно перевести выполнение запроса на сторону сервера в безопасный контекст.
Для этого выполним следующие шаги:
ПолныеПраваВызовСервера.
// Код в общем модуле ПолныеПраваВызовСервера (свойство Привилегированный = Истина)
Функция ПолучитьЗначениеПеременнойПривилегированно(ИмяПеременной) Экспорт
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Переменные.ЗначениеПеременной КАК Значение
|ИЗ
| РегистрСведений.Переменные КАК Переменные
|ГДЕ
| Переменные.НаименованиеПеременной = &ИмяПеременной";
Запрос.УстановитьПараметр("ИмяПеременной", ИмяПеременной);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Значение;
КонецЕсли;
Возврат Неопределено;
КонецФункции
Теперь при вызове этой функции из любого места конфигурации (даже на клиенте под урезанными правами) запрос будет выполнен на сервере без каких-либо проверок прав доступа и RLS. Это гарантирует стабильность транзакции проведения.
---
Чтобы оперативно находить истинного виновника падения транзакции, когда платформа указывает на ложное место в коде, используем три надежных инструмента диагностики.
При возникновении любых ошибок ограничения доступа платформа фиксирует детальное системное событие. Чтобы его увидеть:
Чтобы поймать ошибку в момент ее зарождения (до того, как управление уйдет в блок Исключение и транзакция заблокируется):
Для расследования сложных случаев на продуктивных базах можно настроить сбор логов технологического журнала. Создайте в каталоге настроек платформы (обычно C:\Program Files\1cv8\conf) файл logcfg.xml следующего содержания:
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="C:\v8logs\DBErrors" history="24">
<event>
<eq property="Name" value="QERR"/>
</event>
<property name="all"/>
</log>
</config>
Событие QERR собирает все ошибки выполнения запросов к СУБД. Проанализировав полученный лог-файл, вы увидите точный текст SQL/SDBL-запроса, выполнение которого привело к отказу в правах доступа, а также контекст вызова всей цепочки процедур и функций.