Как найти причину недостатка прав у пользователя для изменения данных и как правильно исправить ошибку RLS?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

В процессе разработки и поддержки конфигураций на платформе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда под учетной записью с полными правами все работает безупречно, но у рядового пользователя при выполнении тех же действий возникает критическая ошибка: «У пользователя недостаточно прав на исполнение операции над базой данных». Особое коварство этой ошибки заключается в том, что она может возникать не на этапе открытия формы или записи самого объекта, а в процессе интерактивного взаимодействия с элементами формы — например, при изменении строки в дереве значений или табличном поле.

В этой статье мы подробно разберем, почему возникает подобная ошибка, детально проанализируем механизм работы платформы при обращении к реквизитам «через точку», рассмотрим эффективные методы диагностики проблемы и изучим три правильных, безопасных способа её решения, которые соответствуют стандартам разработки фирмы 1С.


Истинная причина ошибки: разыменование ссылки и механизмы RLS

Давайте проанализируем конкретную ситуацию. В коде обработчика формы присутствует следующая строка:


ЭлементыФормы.Дерево1.ТекущаяСтрока.ДатаИa = ТекущаяСтрока.ЗаказНаПроизводство.ДатаИa;

На первый взгляд, это обычное присвоение значения одного поля другому. Однако здесь происходит так называемое разыменование ссылки. В правой части выражения мы обращаемся к реквизиту ДатаИa, который принадлежит объекту ЗаказНаПроизводство. Переменная ТекущаяСтрока.ЗаказНаПроизводство хранит лишь ссылку на этот документ (его уникальный идентификатор GUID), но не сам объект и не его реквизиты.

Когда встроенный язык платформы 1С встречает конструкцию обращения «через точку» от ссылки (Ссылка.ИмяРеквизита), он выполняет неявный и автоматический запрос к СУБД для извлечения значения этого реквизита из базы данных.

Здесь в силу вступает механизм RLS (Record Level Security) — ограничение доступа на уровне записей. Если в системе настроена роль, ограничивающая доступ к документам ЗаказНаПроизводство по определенным условиям (например, по организации, подразделению или складу), платформа проверяет, удовлетворяет ли конкретный документ, на который указывает ссылка, условиям RLS для текущего пользователя.

Если документ не удовлетворяет условиям RLS, СУБД полностью блокирует чтение этой записи для данного пользователя. Так как обращение происходит напрямую через встроенный язык, платформа не может просто вернуть пустое значение или Неопределено. Она обязана прервать выполнение операции и инициировать исключение. В результате пользователь видит системное сообщение об ошибке базы данных, а работа алгоритма аварийно завершается.


Методы диагностики: как локализовать проблему?

Прежде чем исправлять код, нам необходимо точно выяснить, на какой именно объект базы данных у пользователя не хватает прав. Для этого применим несколько последовательных шагов диагностики.

Шаг 1. Анализ Журнала регистрации (ЖР)

Журнал регистрации — это первый инструмент, к которому должен обратиться разработчик. При возникновении ошибок доступа платформа фиксирует событие в ЖР.

  1. Откроем Журнал регистрации в режиме «1С:Предприятие» (или через Конфигуратор).
  2. Установим фильтр по пользователю, у которого воспроизводится ошибка, и по периоду возникновения проблемы.
  3. В колонке «Событие» будем искать записи со статусом «Ошибка» и событием «Доступ.Отказ в доступе».
  4. В детальном описании события будет четко указано имя метаданных (например, Документ.ЗаказНаПроизводство) и вид права, которого не хватило (обычно это право Чтение).

Важное примечание: Если журнал регистрации в базе данных временно отключен в целях оптимизации производительности, настоятельно рекомендуется включить его хотя бы на минимальный уровень логирования (фиксировать только ошибки) на время проведения теста, воспроизвести ошибку и затем снова настроить систему под рабочие нужды.

Шаг 2. Анализ настроек ролей в Конфигураторе

После того как мы выяснили, какой объект вызывает ошибку (в нашем примере это Документ.ЗаказНаПроизводство), необходимо проверить права этого пользователя в Конфигураторе:

  1. Откроем Конфигуратор и найдем нужный объект метаданных.
  2. Перейдем на вкладку «Права» в свойствах объекта.
  3. Проанализируем список ролей, назначенных данному пользователю. Убедимся, что хотя бы в одной из его ролей установлены флажки напротив прав Чтение, Просмотр и Использование.
  4. Если базовые права на чтение объекта у роли присутствуют, обратим внимание на нижнее окно настроек ограничений. Если там прописаны шаблоны ограничений (SQL-подобные условия RLS), значит, проблема именно в ограничениях на уровне записей. Конкретный документ просто не попадает под разрешенные условия (например, относится к другой организации).

Шаг 3. Использование Технологического журнала (ТЖ)

В сложных случаях, когда Журнал регистрации не дает подробной информации, мы можем прибегнуть к настройке Технологического журнала сервера 1С. Для отслеживания ошибок компиляции запросов и наложения RLS нам понадобится событие QERR (ошибки выполнения запросов) и событие EXCP (исключительные ситуации).

Пример простейшего файла настройки logcfg.xml для сбора таких событий:


<config xmlns="http://v8.1c.ru/v8/tech-log">
    <log location="C:\v8_tech_log" history="2">
        <event>
            <eq property="Name" value="EXCP"/>
        </event>
        <event>
            <eq property="Name" value="QERR"/>
        </event>
        <property name="All"/>
    </log>
</config>

Анализ полученных логов ТЖ позволит увидеть точный SQL-запрос, который отправляла платформа в СУБД, и понять, на каком именно условии RLS споткнулась система.


Анализ неоптимальных решений: почему так делать не надо?

В процессе обсуждения проблемы часто выдвигаются простые, но крайне нежелательные варианты решения. Рассмотрим, почему они являются антипаттернами.

Антипаттерн №1: Использование проверки ЗначениеЗаполнено()

Некоторые разработчики предлагают добавить перед обращением к реквизиту следующую проверку:


Если ЗначениеЗаполнено(ТекущаяСтрока.ЗаказНаПроизводство) Тогда
    ТекущаяСтрока.ДатаИa = ТекущаяСтрока.ЗаказНаПроизводство.ДатаИa;
КонецЕсли;

Эта проверка абсолютно бесполезна в контексте борьбы с RLS. Функция ЗначениеЗаполнено() проверяет лишь то, не является ли ссылка пустой (то есть не равна ли она пустому GUID вида 00000000-0000-0000-0000-000000000000). Если в поле содержится реальная ссылка на существующий в базе документ, к которому у текущего пользователя просто нет прав, функция вернет Истина. Код попытается обратиться «через точку» к реквизиту ДатаИa, и система немедленно упадет в ошибку.

Антипаттерн №2: Обертывание в блок Попытка ... Исключение

Второй популярный, но неверный подход — подавление ошибки:


Попытка
    ТекущаяСтрока.ДатаИa = ТекущаяСтрока.ЗаказНаПроизводство.ДатаИa;
Исключение
    // Игнорируем ошибку или пишем лог
КонецПопытки;

Использование этого блока для обхода бизнес-логики прав доступа крайне не рекомендуется по следующим причинам:


Правильные способы решения проблемы

Теперь рассмотрим профессиональные и безопасные способы получения данных в условиях действующих ограничений RLS.

Способ А. Использование явного запроса с ключевым словом РАЗРЕШЕННЫЕ

Это самый надежный способ, гарантирующий стабильную работу системы без генерации исключений. Вместо прямого обращения к реквизиту «через точку» мы напишем небольшой запрос, в котором явно укажем платформе, что хотим получить только те данные, к которым у пользователя есть доступ.

Рассмотрим пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	Заказ.ДатаИа КАК ДатаИа
    |ИЗ
    |	Документ.ЗаказНаПроизводство КАК Заказ
    |ГДЕ
    |	Заказ.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", ТекущаяСтрока.ЗаказНаПроизводство);
РезультатЗапроса = Запрос.Выполнить();

Если Не РезультатЗапроса.Пустой() Тогда
    Выборка = РезультатЗапроса.Выбрать();
    Выборка.Следующий();
    ТекущаяСтрока.ДатаИa = Выборка.ДатаИа;
Иначе
    // Если у пользователя нет прав по RLS, запрос вернет пустой результат,
    // и мы можем корректно обработать эту ситуацию без падения системы.
    ТекущаяСтрока.ДатаИa = Дата('00010101'); 
КонецЕсли;

Почему это работает: Ключевое слово РАЗРЕШЕННЫЕ в тексте запроса заставляет платформу автоматически отфильтровать записи, недоступные текущему пользователю согласно его правам RLS. Если права на документ отсутствуют, запрос просто не вернет ни одной строки, не вызывая при этом никаких системных ошибок СУБД.

Способ Б. Использование стандартных функций БСП

Если ваша конфигурация разработана на базе современных типовых решений фирмы 1С, она гарантированно содержит в своем составе Библиотеку Стандартных Подсистем (БСП). В составе БСП есть замечательный общий модуль ОбщегоНазначения, который содержит функции для безопасного чтения реквизитов объектов.

Для нашей задачи идеально подходит функция ЗначениеРеквизитаОбъекта. Нам нужно вызвать ее, обязательно передав признак безопасного чтения в условиях RLS. Посмотрим на код:


// Третий параметр "ВыбратьРазрешенные" передаем как Истина
ПараметрыЧтения = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
    ТекущаяСтрока.ЗаказНаПроизводство, 
    "ДатаИа", 
    Истина
);

Если ЗначениеЗаполнено(ПараметрыЧтения) Тогда
    ТекущаяСтрока.ДатаИa = ПараметрыЧтения;
Иначе
    ТекущаяСтрока.ДатаИa = Дата('00010101');
КонецЕсли;

Преимущества данного метода:

Способ В. Выполнение чтения данных в привилегированном режиме

Иногда бизнес-логика программы требует, чтобы определенная служебная информация (например, техническая дата согласования заказа) отображалась в отчете или обработке даже в том случае, если сам документ-источник скрыт от пользователя настройками RLS. То есть, пользователю запрещено видеть сам документ целиком, но конкретное поле в таблице заполнить необходимо.

В этом случае нам на помощь приходит привилегированный режим, в котором проверки RLS полностью отключаются платформой.

Для реализации этого способа нам потребуется вынести логику получения данных на сервер. Выполним следующие шаги:

  1. Создадим (или найдем существующий) общий модуль, у которого в свойствах установлены флажки «Сервер» и «Привилегированный». Назовем его, например, упПривилегированныйВызов.
  2. Внутри этого модуля опишем экспортную функцию получения даты:

// Функция выполняется на сервере без учета ограничений прав RLS текущего пользователя
Функция ПолучитьДатуЗаказаПривилегированно(СсылкаНаЗаказ) Экспорт
    
    // Платформа прочитает значение напрямую из СУБД без ошибок
    Возврат СсылкаНаЗаказ.ДатаИа;
    
КонецФункции
  1. В модуле нашей формы заменим небезопасное обращение на вызов нашей новой серверной функции:

ТекущаяСтрока.ДатаИa = упПривилегированныйВызов.ПолучитьДатуЗаказаПривилегированно(ТекущаяСтрока.ЗаказНаПроизводство);

Важное предупреждение: Использовать привилегированный режим следует с большой осторожностью. Передавайте в привилегированные модули только те операции, которые действительно необходимы для функционирования бизнес-процесса, чтобы не нарушить общую концепцию безопасности и разграничения прав доступа в компании.


Рекомендация по проектированию прав доступа

Для того чтобы в будущем минимизировать возникновение подобных ошибок, рекомендуется придерживаться хорошего тона при проектировании ролей и ограничений доступа в конфигураторе 1С.

При настройке RLS для ролей на объект метаданных (например, справочник или документ) разделяйте ограничения на две группы:

  1. Для полей Ссылка, Номер, Дата: не устанавливайте никаких ограничений RLS. Пусть эти поля будут доступны для чтения всегда. Это позволит платформе корректно отображать представления документов в списках, отчетах и полях ввода, не вызывая критических ошибок СУБД.
  2. Для поля <Прочие поля>: накладывайте реальные ограничения RLS (по организации, контрагенту и т.д.). Тогда конфиденциальная информация (суммы, табличные части, спецификации) будет надежно скрыта от пользователя, а обращение к базовым свойствам ссылки перестанет вызывать аварийное завершение работы программы.

Мы проанализировали причины возникновения ошибок доступа к данным, научились их локализовать и рассмотрели надежные паттерны программирования в среде 1С:Предприятие. Применяйте эти знания на практике, делайте ваш код отказоустойчивым, и пусть ваши пользователи никогда не увидят сообщений о нехватке прав!

← На главную