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