Итак, поехали…
Начать хочу с ошибок, связанных нарушением доступа на уровне записей. Наш любимый rls. Ошибка, которая возникает в таких случаях, всем хорошо знакома:

Ошибка возникает в том случае, если в запросе выбираются данные, которые пользователю запрещены на уровне записей и в запросе не указано ключевое слово РАЗРЕШЕННЫЕ.
Если ключевое слово РАЗРЕШЕННЫЕ указать, то запрещенных записей в результате запроса не будет. А если основная таблица выборки разрешена, а запрещено одно из вложенных полей, это поле будет показано в выборки как битая ссылка, а все реквизиты этого поля будут иметь значение NULL.
Давайте посмотрим, что же меняется в запросе к серверу СУБД, в зависимости от наличия или отсутствия инструкции РАЗРЕШЕННЫЕ?
А меняется следующее:
Если ключевого слова РАЗРЕШЕННЫЕ нет в запросе, в выборке будет присутствовать дополнительное поле (первое по порядку). в этом поле, собственно, и выполняются все проверки доступа на уровне записей. Поле будет приблизительно такого вида:
CASE WHEN (EXISTS(SELECT 0x01 AS Q_001_F_000_
FROM dbo._Reference250X1 T4
INNER JOIN dbo._Reference165 T5
ON (T4._Fld59163 = ?) AND EXISTS(SELECT 0x01 AS Q_004_F_000_
FROM dbo._InfoRg47609X1 T6
WHERE ((T6._Fld2589 = ?)) AND ((T6._Fld47610_TYPE = 0x08 AND T6._Fld47610_RTRef = 0x000000FA AND T6._Fld47610_RRRef = T4._IDRRef) AND (T6._Fld47611RRef = T5._IDRRef))) AND T5._IDRRef IN
(SELECT T8._Reference165_IDRRef AS ACCESS_GROUPRRef
FROM dbo._Reference165_VT56822 T8
INNER JOIN dbo._InfoRg46624 T9
ON (T9._Fld46626_TYPE = 0x08 AND T9._Fld46626_RTRef = 0x000001F8 AND T9._Fld46626_RRRef = ?) AND (T9._Fld46625_TYPE = T8._Fld56824_TYPE AND T9._Fld46625_RTRef = T8._Fld56824_RTRef AND T9._Fld46625_RRRef = T8._Fld56824_RRRef)
WHERE ((T8._Fld2589 = ?)) AND (T9._Fld2589 = ?))
WHERE ((T5._Fld2589 = ?)) AND ((1=1) AND (CASE WHEN EXISTS(SELECT 0x01 AS Q_002_F_000_
FROM dbo._InfoRg40111 T10
WHERE ((T10._Fld2589 = ?)) AND ((T10._Fld40112RRef = T5._IDRRef) AND (T10._Fld40113_TYPE = 0x08 AND T10._Fld40113_RTRef = 0x0000018D AND T10._Fld40113_RRRef = T1._Fld20869RRef))) THEN 0x01 ELSE 0x00 END = CASE WHEN EXISTS(SELECT 0x01 AS Q_003_F_000_
FROM dbo._InfoRg40118 T11
WHERE ((T11._Fld2589 = ?)) AND ((T11._Fld40119RRef = T5._IDRRef) AND T11._Fld40120_TYPE = 0x08 AND T11._Fld40120_RTRef = 0x0000018D AND (T11._Fld40121 = 0x00))) THEN 0x01 ELSE 0x00 END)))) = 0x01 THEN 0x01 ELSE 0x00 END
Если в нем разобраться, то будет видно, что это фрагмент кода из шаблона ограничений доступа на уровне записей ПоЗначениям. И это один из самых простых видов ограничений всего лишь по одному виду доступа и по одному объекту )). Теперь становиться понятно, почему запросы с rls так тормозят.
И так, если доступ к записи разрешен, в первом поле выборки возвращается значение ИСТИНА, если не разрешен, возвращается ЛОЖЬ. После этого платформа проверяет выборку на наличие записей со значением ЛОЖЬ в первом поле. И если такие записи есть, выдает наше любимое сообщение об ошибке.
А вот если ключевое слово РАЗРЕШЕННЫЕ в запросе есть, он строиться принципиально по-другому. Все проверки выполняются не в дополнительном поле, а в секции ГДЕ. Соответственно, если проверка не проходит, запись попросту не будет возвращена, и ни каких ошибок.
Теперь посмотрим, что меняется в запросе, если доступ на уровне записей выполняется еще и для вложенного объекта. Например, выборка из регистра «Остатки по складам». В дополнении к самому регистру есть ограничения на уровне записей к номенклатуре. Отметим, что выборке должен присутствовать какой-либо реквизит номенклатуры. Это важно, так как если в выборке присутствует только ссылка на номенклатуру, никаких дополнительных проверок выполнено не будет. Ссылка вернется запросом. А вот получение основного представления ссылки получено не будет, так как для его получения будет выполнен отдельный запрос к СУБД с дополнительной проверкой. В результате мы увидим ту самую битую ссылку.
Что поменяется в запросе к серверу СУБД?
Без инструкции РАЗРЕШЕННЫЕ в запросе будет дополнительная таблица, соединенная с основной левым соединением. Во второй таблице также будет дополнительное поле (ИСТИНА / ЛОЖЬ), в котором будут выполняться проверки rls уже для справочника Номенклатура. Поле это будет иметь специальный алиас: SDBL_RLS_SIGNAL_. Далее значение этого поля добавляется к первому полю основной выборки:
…AND ISNULL(T2.SDBL_RLS_SIGNAL_,0x01)
В результате мы получаем следующее поведение:
Если доступ на уровне записей к основной таблице есть, а к одному из полей нет, и в выборке есть вложенный реквизит этого поля, будет ошибка ограничения прав на уровне записей, так как в выборке будут присутствовать строки с значением первого поля = ЛОЖЬ. Если в выборке присутствует только ссылка на недоступный объект, дополнительной таблицы в запросе не будет и проверки прав на нее тоже не будет. Следовательно, не будет и ошибки, а вместо представления объекта будет показана битая ссылка.
В случае, когда в запросе есть инструкция РАЗРЕШЕННЫЕ, в запросе к СУБД также будет дополнительная таблица с левым соединением, только проверки теперь будут в секции ГДЕ этой таблицы. Соответственно, если прав к записям таблицы нет, она их просто не вернет и вложенное поле будет иметь значение NULL.
Исходя из вышеописанного можно сделать вывод, что ключевое слова РАЗРЕШЕННЫЕ влияет на поведение запроса только в случае использования rls.
Так ли это?
Давайте экспериментировать дальше.
Представим, что у пользователя есть доступ к основной таблице выборки и нет к вложенному объекту. Нет доступа не на уровне записей, а на уровне ролей, то есть ко всем записям таблицы.
Сформируем следующий простой запрос, без инструкции РАЗРЕШЕННЫЕ:
ВЫБРАТЬ ПЕРВЫЕ 1
ПервичныйДокумент.эу_ВПД КАК ВПД,
ПервичныйДокумент.эу_ВПД.Номер КАК ВПДНомер,
ПервичныйДокумент.Ссылка КАК Ссылка
ИЗ Документ.ПервичныйДокумент КАК ПервичныйДокумент
ГДЕ ПервичныйДокумент.Номер = &Номер
При условии, что у пользователя нет прав на уровне ролей к документу эу_ВПД, в результате мы получим ошибку:

Все очевидно и понятно. Такое же сообщение будет выведено, если запросить выборку из недоступной таблицы явно, вне зависимости от опции РАЗРЕШЕННЫЕ.
Теперь добавим инструкцию РАЗРЕШЕННЫЕ в запрос.
Опять ошибка, но сообщение следующее:

[ОшибкаВоВремяВыполненияВстроенногоЯзыка]
по причине:
{(3, 41)}: Поле не найдено "ПервичныйДокумент.эу_ВПД.Номер"
ПервичныйДокумент.эу_ВПД.Номер КАК ВПДНомер
Теперь изменим обращение к полю Номер используя конструкцию ВЫРАЗИТЬ
ВЫРАЗИТЬ(ПервичныйДокумент.эу_ВПД КАК Документ. эу_ВПД).Номер
Выполним запрос без конструкции РАЗРЕШЕННЫЕ.
И получаем… до боли знакомое сообщение об ошибке:

Что такое?
У нас ведь ограничение на уровне ролей, а не записей!
Ладно, давайте добавим инструкцию РАЗРЕШЕННЫЕ.
И-и-и. УСПЕХ!
Сообщения об ошибки нет. Данные возвращены. Поле ВПД представлено битой ссылкой, а поле ВПДНомер – значение NULL. Так и предполагалось.
Давайте разбираться.
Без конструкции ВЫРАЗИТЬ система явно показывает, что не так. В первом случае явно пишет, что к такой-то таблице нет доступа. Во-втором указывает на отсутствие реквизита объекта. Важно отметить, что в обоих случаях это проверки платформы до формирования запроса к серверу СУБД.
Почему поведение отличается?
Трудно сказать наверняка. Возможно, если платформа использует РАЗРЕШЕННЫЕ и встречает в запросе недоступный для пользователя объект, заменяет его на NULL. Соответственно, у поля NULL не находит нужного реквизита. В любом случае, в обоих вариантах запрос к серверу базы данных не формируется. По событию DBMSSQL в технологическом журнале вы ничего не найдете.
Если используется конструкция ВЫРАЗИТЬ, система при предварительном анализе запроса явных проблем не находит и формирует запрос к СУБД.
Без инструкции РАЗРЕШЕННЫЕ, на СУБД выполняется запрос такого вида:
SELECT TOP 1
ISNULL(T2.SDBL_RLS_SIGNAL_,0x01),
T1._Fld92959RRef,
T2.Number_,
T1._IDRRef
FROM dbo._Document1116 T1
LEFT OUTER JOIN (SELECT
(0x00) AS SDBL_RLS_SIGNAL_,
T3._Number AS Number_,
T3._IDRRef AS IDRRef,
T3._Fld2589 AS Fld2589_
FROM dbo._Document74610 T3
WHERE (T3._Fld2589 = ?)) T2
ON T1._Fld92959RRef = T2.IDRRef
WHERE ((T1._Fld2589 = ?)) AND ((T1._Number = ?))
Обратите внимание на поле: SDBL_RLS_SIGNAL_. Как видим, что поведение аналогично тому, если бы использовалось rls. Выбираются все данные и помечаются строки, в которых присутствуют запрещенные записи. В нашем случае, это все записи, в которых есть связанный объект эу_ВПД. По этой причине и сообщение об ошибке соответствующее.
С инструкцией РАЗРЕШЕННЫЕ, запрос меняется на следующий:
SELECT TOP 1
T1._Fld92959RRef,
T2._Number,
T1._IDRRef
FROM dbo._Document1116 T1
LEFT OUTER JOIN dbo._Document74610 T2
ON ((T1._Fld92959RRef = T2._IDRRef) AND (T2._Fld2589 = ?)) AND (0x00) = 0x01
WHERE ((T1._Fld2589 = ?)) AND ((T1._Number = ?))
Обратите внимание на выражение (0x00) = 0x01. Записи дополнительной таблицы явно отсекаются. Из-за этого получаем закономерное NULL при выводе номера и никаких ошибок.
Какие выводы можно сделать из вышесказанного
- Сообщения об ошибке с явным указанием таблиц или полей свидетельствует о том, что сработал предварительный анализ запроса платформой и запрос еще не был отправлен на сервер СУБД.
- Конструкция РАЗРЕШЕННЫЕ в запросах, влияет на результат не только в тех случаях, когда используется rls. Соответственно сообщение «У пользователя недостаточно прав на исполнение операции над базой данных» не всегда свидетельствует об отсутствии прав на уровне записей.
- Совместное использование конструкций РАЗРЕШЕННЫЕ и ВЫРАЗИТЬ позволяет избежать ошибок при обращении к вложенным полям объекта, недоступного для пользователя на уровне ролей.
Маленький бонус
В технологическом журнале есть одно событие - QERR.
Для его включения, в файле logcfg.xml можно сделать следующую настройку:
<log location="D:\1c\ERP" history="2">
<event>
<eq property="name" value="QERR"/>
<eq property="p:processName" value="ERP "/>
</event>
<property name="all"/>
</log>
Событие вызывается при любой из вышеприведенных ошибок с правами доступа. Даже в тех случаях, когда выполнен только предварительный анализ запроса платформой и он еще не был отправлен на сервер СУБД.
В этом событии в поле Query выводится текст запроса в таком виде, в каком он был задан в конфигураторе.
Напомню, события DBMSSQL и SDBL возвращают текст запроса, преобразованный к формату базы данных.
Также в событии QERR в поле Context выводится контекст возникновения ошибки.
Удачи Вам в расследовании ошибок с правами доступа!
А с какими не очевидными ошибками системы прав доступа сталкивались Вы?
Вступайте в нашу телеграмм-группу Инфостарт