Пользователь присылает скриншот с одной строкой "Нарушение прав доступа!" и вопросом "что мне дать?". На каком объекте и какого права не хватило, он не знает. Штатный отчёт "Права доступа" в карточке пользователя отвечает на обратный вопрос: что у человека есть. А нужно понять, чего не хватило вот сейчас.
Это уже записано. Каждый такой отказ платформа кладёт в журнал регистрации событием "Доступ. Отказ в доступе": объект в колонке "Метаданные", имя права в данных события. Специально включать ничего не надо, достаточно, чтобы журнал писал уровень "Информация". Я вызвал такие отказы на 8.3.27.1606 под пользователем с урезанным профилем и отдельно убедился, что на 8.3.20 событие пишется так же. От права до группы доступа, которая снимет отказ, три шага кодом, и в конце статьи обработка, которая проходит их за одно нажатие.
Где смотреть руками
Журнал регистрации, отбор по событию "Доступ. Отказ в доступе". В строке события:
- колонка "Метаданные":
Справочник.ВидыКартЛояльности,Документ.РеализацияТоваровУслуг, или пусто, если право на всю программу (например, на открытие внешних обработок); - колонка "Данные": структура с полем
Право. ТамЧтение,Изменение,Проведение,ИнтерактивноеОткрытиеВнешнихОбработок,Администрирование; - уровень "Информация".
Из-за уровня отказов может не быть вовсе: раз событие пишется уровнем "Информация", журнал в режиме "только ошибки" его не сохранит, хотя пользователь сообщение увидит. Проверить можно в конфигураторе (Администрирование - Настройка журнала регистрации) или кодом: ПолучитьИспользованиеЖурналаРегистрации() должен вернуть массив с уровнем Информация.
То же кодом, чтобы не листать журнал:
События = Новый ТаблицаЗначений; Отбор = Новый Структура; Отбор.Вставить("ДатаНачала", НачалоДня(ТекущаяДатаСеанса()) - 7 * 86400); Отбор.Вставить("Событие", "_$Access$_.AccessDenied"); ВыгрузитьЖурналРегистрации(События, Отбор, "Дата,ИмяПользователя,Метаданные,Данные"); Для Каждого Событие Из События Цикл Объект = ?(Событие.Метаданные.Количество() > 0, Событие.Метаданные[0], "Конфигурация"); Право = "нет поля Право"; Если ТипЗнч(Событие.Данные) = Тип("Структура") И Событие.Данные.Свойство("Право") Тогда Право = Событие.Данные.Право; КонецЕсли; Сообщить(Формат(Событие.Дата, "ДЛФ=DT") + " " + Событие.ИмяПользователя + ": " + Объект + ", " + Право); КонецЦикла;
Метаданные у этого события приходит массивом строк даже для одного объекта, отсюда [0]. Тип Данные проверяю явно: у отказа по записи (ограничение на уровне записей) поля Право по описанию платформы нет. Запускать под администратором: для чтения журнала нужно право "Журнал регистрации", у обычных пользователей его нет.
Что именно пишет платформа
Стенд: Управление торговлей 11.5 на 8.3.27.1606, БСП (Библиотека стандартных подсистем) 3.1. Тестовый пользователь в группе с поставляемым профилем "Работник склада". Тонкий клиент я не запускаю, поэтому сеанс под этим пользователем открывал внешним соединением (COM), и все действия ниже делал код в его сеансе. Запись документа шла в транзакции с откатом, базу я не менял.
| Что сделал код под пользователем | Что записано в событии: метаданные и право |
|---|---|
| запрос к справочнику "Виды карт лояльности" | Справочник.ВидыКартЛояльности, право Чтение |
| запрос к документам "Установка цен номенклатуры" | Документ.УстановкаЦенНоменклатуры, право Чтение |
запись реализации с проведением, Записать(РежимЗаписиДокумента.Проведение) |
Документ.РеализацияТоваровУслуг, право Изменение |
ВыполнитьПроверкуПравДоступа с правом "Проведение" для реализации |
Документ.РеализацияТоваровУслуг, право Проведение |
ВыполнитьПроверкуПравДоступа с правом на интерактивное открытие внешних обработок |
метаданные пустые, право ИнтерактивноеОткрытиеВнешнихОбработок |
ПользователиИнформационнойБазы.ПолучитьПользователей() |
метаданные пустые, право Администрирование, объект доступа ПользователиИнформационнойБазы |
Две строки с ВыполнитьПроверкуПравДоступа - замена кликам, которых я сделать не мог. Эта функция по описанию пишет событие отказа, если права нет, и событие вышло того же вида, что у настоящего действия.
В третьей строке документ проводили, а в журнале Изменение: у меня первым проверилось право записать. На практике это значит, что роль надо искать по праву из журнала, но следить, чтобы она давала и проведение. На стенде это почти автоматически: я перебрал все роли и все проводимые документы его конфигурации (УТ 11.5 с доработками, не чистая типовая), пар с правом проведения нашлись сотни, и ни в одной нет проведения без изменения. Обратное неверно, ролей на изменение без проведения хватает. У себя это проверяется тем же перебором через ПравоДоступа.
Внешние обработки пишутся другим событием. Неудачное подключение ложится как _$Session$_.ExternalDataProcessorConnectError уровнем "Ошибка", в данных путь к файлу и флаги: подключали ли в безопасном режиме, какой профиль безопасности, включена ли у пользователя защита от опасных действий. Причину платформа кладёт в комментарий. Разбор ниже, в разделе про внешние обработки.
Чего в журнале нет. В колонках события нет имени формы или команды, из которой пришёл отказ: есть время, сеанс и приложение (тонкий клиент, COM-соединение, HTTP-сервис). По соседним событиям того же сеанса иногда понятно больше: если за минуту до отказа в этом сеансе подключали внешнюю обработку, скорее всего, отказ пришёл из неё. И ещё одно наблюдение, одно, поэтому без обобщений: когда тестовому пользователю не хватило права "Использование" на методе HTTP-сервиса (это право задаётся в роли, у каждого метода сервиса), он получил ответ 403, а в журнале события отказа не появилось.
От права к группе доступа
Какие роли дают право на объект, платформа отвечает одной функцией, роль передаётся третьим параметром:
Объект = Метаданные.Справочники.ВидыКартЛояльности; Для Каждого Роль Из Метаданные.Роли Цикл Если ПравоДоступа("Чтение", Объект, Роль) Тогда Сообщить(Роль.Имя); КонецЕсли; КонецЦикла;
На стенде таких ролей четыре: ЧтениеВидовКартЛояльности, ДобавлениеИзменениеВидовКартЛояльности, ПолныеПрава и служебная УдаленныйДоступOData. Две последние выкидываем сразу: полные права ради одного справочника не выдают, роль для OData живому пользователю тоже.
В базе на БСП роли руками не назначают, они приходят из профилей групп доступа. Следующий шаг: в каких профилях эти роли есть и какие группы доступа на этих профилях живут.
Запрос = Новый Запрос( "ВЫБРАТЬ РАЗЛИЧНЫЕ | Группы.Наименование КАК Группа, | Группы.Профиль.Наименование КАК Профиль |ИЗ | Справочник.ПрофилиГруппДоступа.Роли КАК РолиПрофиля | ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа КАК Группы | ПО РолиПрофиля.Ссылка = Группы.Профиль |ГДЕ | РолиПрофиля.Роль.Имя = &Роль | И НЕ Группы.ПометкаУдаления"); Запрос.УстановитьПараметр("Роль", "ЧтениеВидовКартЛояльности"); Выборка = Запрос.Выполнить().Выбрать(); Пока Выборка.Следующий() Цикл Сообщить(Выборка.Группа + " (профиль " + Выборка.Профиль + ")"); КонецЦикла;
РолиПрофиля.Роль в БСП ссылается на справочник идентификаторов объектов метаданных, у которого есть реквизит Имя, так что сравнение с именем роли работает (проверял на том же стенде). Личные группы и группы администраторов этот запрос для краткости не отсекает, в обработке это сделано отдельно.
Подходящих групп обычно несколько, и тут начинается выбор. На стенде я завёл рабочие группы на поставляемых профилях, без участников, как в обычной базе. Справочник видов карт открывали четыре: кассиры розницы, маркетинг, кладовщики (профиль "Кладовщик", он шире "Работника склада") и продажи. Профили у них от сотни с небольшим ролей у кассиров до трёх с лишним сотен у продаж. Часть этих ролей у пользователя уже есть через его профиль, но добавить его в продажи ради одного справочника всё равно значит выдать сотни ролей в нагрузку. Правило я взял простое: побеждает группа, у профиля которой меньше всего ролей. Мера грубая, но самое лишнее отсекает.
Две группы не предлагаю никогда. Администраторов, даже если их профиль формально "подходит". И личные группы, которые в БСП заводятся на одного конкретного пользователя.
Отказы приходят слоями
Я гонял тестового пользователя кругами: код под ним делает шесть действий из таблицы выше, я читаю журнал напрямую, добавляю пользователя в те группы, которые получились по правилу из прошлого раздела, и он повторяет те же шесть действий. Весь эксперимент я прогнал три раза с нуля, и все три прогона совпали до строки.
Первый круг: шесть действий, шесть отказов. Пять из них по правилу разошлись на три группы: кассиры (справочник видов карт и чтение установки цен), продажи (оба отказа по реализации, для неё подошла только эта группа) и поставляемая "Открытие внешних отчетов и обработок". Шестой, чтение списка пользователей с правом Администрирование, группой не лечится, о нём ниже. После добавления в три группы из журнала ушли все пять отказов по праву. Но появились новые, которых в первом круге не было и быть не могло.
Установка цен в первом круге упала на Чтение: пользователь не видел даже списка документов. Узкая группа на чтение это сняла, и следующим пришёл отказ на Изменение. Платформа останавливается на первой стене, вторую видно только после того, как убрали первую. Поэтому, если человек не смотрит справочник или документ, а вводит его, группу стоит подбирать сразу на запись. Во втором круге это была группа маркетинга, и отказ на изменение ушёл.
Третий слой уже не про роли. Запись реализации и установки цен в режиме проведения после всех правок упала внутри кода проведения:
Недостаточно прав для работы с таблицей "РегистрСведений.АналитикаУчетаНаборов"
У установки цен то же самое с регистром ВерсииПодсистем. Читать оба регистра в конфигурации стенда умеют только ПолныеПрава и та же УдаленныйДоступOData, ни в один пользовательский профиль они не входят. Здесь я осторожен. Сеанс у меня шёл через внешнее соединение, и подозреваю, что в тонком клиенте этот путь проходит иначе: иначе реализацию не мог бы провести ни один продавец. Про ошибку типовой я говорить не буду. Полезен сам признак, независимо от причины: если право на таблицу дают только административные и служебные роли, выдачей прав это не лечится. Разбирать надо код или то, как устроен сеанс.
На этом слое я сам сначала ошибся. Первая версия правила выбирала "самую узкую роль" из всех, у кого есть право, и для этого регистра честно предложила создать профиль с УдаленныйДоступOData: других ролей нет, значит, берём её. Совет вредный, роль для интеграций живому пользователю не выдают. Теперь служебные роли идут вместе с административными: если право дают только они, вердикт "чинить код", а не "добавить в группу".
Тот же класс, только нагляднее, отказ с правом Администрирование и ОбъектДоступа = ПользователиИнформационнойБазы. На стенде его дал прямой вызов ПользователиИнформационнойБазы.ПолучитьПользователей() под обычным пользователем. В живой базе так отказывает обработка, отчёт или расширение, которое читает список пользователей без привилегированного режима, и по журналу не видно, какое именно. Но первое желание выдать администрирование тут самое вредное. Чинить надо обращение: УстановитьПривилегированныйРежим(Истина) на сервере или вызов из привилегированного общего модуля.
Сортировка получается такая. Если право даёт хоть одна обычная роль, это вопрос группы доступа. Если только административные и служебные, это вопрос кода, и пользователю честнее сказать "это ошибка программы, права тут ни при чём".
Внешняя обработка не открывается: у одной жалобы разные причины
Отдельная частая жалоба: "не открывается внешняя обработка". Под ней прячутся три разных механизма, и журнал их различает.
Нет права. Без роли "Интерактивное открытие внешних отчетов и обработок" Файл - Открыть даёт "Нарушение прав доступа!", а проверка этого права пишет AccessDenied с правом ИнтерактивноеОткрытиеВнешнихОбработок и пустыми метаданными (у меня это событие вызвано проверкой права, само окно открытия файла я не воспроизводил). В БСП для этого есть поставляемый профиль "Открытие внешних отчетов и обработок" из одной роли, и группа на нём часто уже заведена.
Защита от опасных действий. У пользователя стоит флажок "Защита от опасных действий", и платформа спрашивает "Разрешить открывать данный файл?". В интерактивном сеансе на вопрос можно ответить. Там, где спросить некого (внешнее соединение, фоновое задание, открытие из кода), подключение падает, и в комментарии события "Предупреждение безопасности". Так у меня и вышло во внешнем соединении. Права тут ни при чём, добавлять в группы бесполезно.
Безопасный режим. Комментарий "Установлен безопасный режим. Выполнение операции запрещено". Так бывает, когда внешнюю обработку пытается открыть код, который сам работает в безопасном режиме: дополнительная обработка БСП без выданных разрешений, расширение в безопасном режиме. Я получил это, открыв обработку из кода другой обработки, подключённой безопасно. Рядом живёт профиль безопасности в кластере 1С, тогда в тексте будет про профиль.
Оба последних варианта легли у меня в журнал отдельными строками события подключения, у каждой свой текст. Если смотреть только AccessDenied, их не видно, поэтому при жалобе на внешнюю обработку отбирайте в журнале и _$Session$_.ExternalDataProcessorConnectError.
Когда в журнале пусто
Три причины, проверять я бы начал с первой.
Журнал пишет только ошибки. Отказ записывается уровнем "Информация", значит, в такой базе его нет. Лечится одной настройкой, но только для будущих отказов, пользователю придётся повторить действие.
Отказ был раньше выбранного периода, или журнал сократили.
Это вообще не отказ. RLS, ограничение доступа на уровне записей, в списках не ругается, строки просто не выбираются. Пользователь видит пустой список документов при полной базе и называет это "нет прав". По описанию платформы отказ RLS, если он всё-таки случился при открытии конкретного объекта, приходит с полями Действие и Данные без поля Право. На стенде я этого не воспроизводил: ограничение на уровне записей там выключено, а включать его в общей копии значит пересчитывать ключи доступа на всю базу. Практически: пустой список при полной базе - это повод смотреть значения доступа в группах пользователя (организации, склады), а не роли.
Обработка
Всё это я собрал во внешнюю обработку "Нарушение прав доступа: что добавить, чтобы заработало". Параметров два: период и, по желанию, пользователь. Главная кнопка "Найти отказы", рядом выгрузка в Markdown и частые вопросы.
Сверху крупная строка итога: сколько отказов, у скольких пользователей, сколько снимается одной правкой, сколько требует решения администратора. Если журнал пишет только ошибки, это сказано красным. Ниже таблица, где повторы свёрнуты в одну строку "пользователь, объект, право", с цветным вердиктом: зелёный "Добавить в группу", оранжевый "Создать группу", "Нужен профиль", "Чинить код" или причина для внешней обработки, серый "Право уже есть", фиолетовый "Режет RLS", синий "Объекта нет" для отказов на объект, которого в конфигурации уже нет. Фиолетовый вердикт стоит на описании платформы, на живом отказе RLS я его не видел.
Под таблицей карточка выбранной строки: что сделать по шагам, подходящие группы, роли с этим правом, в каких группах пользователь состоит сейчас, что записано в журнале. Для документа карточка говорит, даст ли предложенная группа ещё и проведение. Для чтения справочника или документа называет группу на запись, если предложенная даёт только чтение: тот самый слой со стенда. "Право уже есть" значит, что сейчас право у пользователя есть. Обычно его выдали после отказа и человек не перезашёл, а если в базе включено ограничение на уровне записей, причина может быть в нём, и карточка об этом скажет.
Если выбрать пользователя, обработка отдельно скажет, может ли он открывать внешние обработки и включена ли у него защита от опасных действий, даже когда событий в журнале нет. Кнопка "Выгрузить в Markdown для нейросети" сохраняет всё одним файлом с инструкцией для модели, имена пользователей в нём заменены на "Пользователь 1", "Пользователь 2".
Без БСП групп нет, и обработка предлагает роль. Число ролей в профиле тут не посчитать, поэтому меру "лишнего" я взял другую: из подходящих ролей та, что даёт чтение меньшего числа справочников и документов. Права она не меняет и никого никуда не добавляет.
Частые вопросы
Отказ пишется только при включённой "регистрации доступа"? Нет. Функция УстановитьИспользованиеСобытияЖурналаРегистрации нужна, чтобы событие выключить или задать поля, которые отказ RLS положит в данные события. Так сказано в описании этой функции. Без неё событие включено: на моём стенде, где журнал никто не трогал, ПолучитьИспользованиеСобытияЖурналаРегистрации отдаёт для него Использование = Истина.
Какую роль выдать, если в журнале Изменение, а человек проводил документ? Ту, что даёт изменение и проведение этого документа. На моём стенде роль с проведением всегда давала и изменение, так что ищите среди них, но у себя проверьте перебором.
Где взять форму или модуль, из которых пришёл отказ? В журнале их нет. Модуль виден в окне ошибки у пользователя, кнопка "Подробно" показывает стек. Попросите прислать его вместе со скриншотом.
Выдал право, а ошибка осталась. Роли применяются при входе, пользователь должен закрыть программу и зайти заново. Если не помогло и право у него точно есть, смотрите значения доступа его групп, это ограничение на уровне записей.
Вердикту "Режет RLS" можно верить? С оговоркой. Он ставится, когда в событии есть Действие и нет Право, так отказ RLS описан в документации платформы. На живом отказе RLS я его не проверял, стенд работал с выключенным ограничением.
На 8.3.20 работает? Событие там пишется тем же видом, по умолчанию, с полем Право в данных. Отказы по объектам (Чтение, Изменение) на 8.3.20 я сам не вызывал, стенд с урезанным пользователем был на 8.3.27.1606.
Вопрос к тем, кто разбирает такие обращения каждую неделю: вы выдаёте пользователю ту роль, на которой он упал, или сразу рабочий профиль целиком? У меня на стенде узкая группа на чтение сняла отказ и тут же открыла следующий, на изменение, и я не уверен, что это аргумент против узких правок.
Другие наши инструменты
- Матрица прав доступа для нейросети - выгрузка ролей, прав и назначений в Markdown: спрашивать у модели, у кого какие права и где роли избыточны.
- Журнал регистрации, свёрнутый для нейросети - журнал за период одним файлом для чата: ошибки, входы, отказы, изменения.
- Анализ кода внешних обработок 1С - что делают внешние обработки и печатные формы из справочника базы, до того как их запустили.
- Чек-ап СУБД под 1С - настройки сервера баз данных, которые тормозят 1С.
- Карта объёмов базы 1С - какие таблицы занимают место и почему.
Соседняя статья про отказ, который вообще не ругается: Пользователю не выдали право на одно поле - СКД молча выкинула его из отчёта.
Вступайте в нашу телеграмм-группу Инфостарт