Матрица эквивалентности прав: как менять доступ в 1С и доказать, что ничего не сломалось
Иван Недомолков · Dərc edilib:
Есть изменения, после которых достаточно спросить “работает?” - и посмотреть. А есть одно, где этот вопрос бесполезен: изменение прав доступа. Система после него работает прекрасно. Списки открываются, отчёты считаются, ошибок нет. Просто кассир в одном из тридцати магазинов теперь видит чужие продажи, и узнаете вы об этом через две недели - от него самого, если повезёт, или от службы безопасности, если нет.
Права - единственная область, где “почти как раньше” равно аварии, причём сразу в двух направлениях. Увидел лишнее - инцидент безопасности: чужая выручка, чужие цены, персональные данные не своих сотрудников. Потерял своё - остановка работы: менеджер не видит свой склад и не может отгрузить. Первое молчит, второе кричит. Опаснее, разумеется, первое.
Эта статья - про инструмент, который закрывает вопрос: матрицу эквивалентности прав. В нашем проекте перевода базы с универсального RLS на классический в ней было 329 строк, расхождений - 0, и это единственная причина, по которой ночное переключение прав на 200 кассах прошло без единого инцидента.
Когда матрица обязательна
Не при каждой правке роли - иначе процесс умрёт от собственного веса. Матрица нужна там, где меняется механизм или массив прав:
- переход между универсальным и классическим ограничением доступа (RLS);
- переработка профилей групп доступа, объединение или разделение ролей;
- обновление конфигурации, затрагивающее типовые роли - особенно переход на новую редакцию;
- массовая чистка пользователей и групп доступа;
- изменение шаблонов ограничений, которые используются десятками ролей;
- перенос базы между контурами, где состав пользователей отличается.
Общий признак простой: если вы не можете назвать поимённо всех пользователей, чью видимость затронет правка, - нужна матрица.
Что такое матрица эквивалентности
Это таблица, где одна строка - один воспроизводимый вопрос к системе, на который есть однозначный ожидаемый ответ. Минимальный набор столбцов:
| Столбец | Что в нём | Пример |
|---|---|---|
| Роль / профиль | Проверяемая группа доступа | Кассир магазина |
| Тестовый пользователь | Учётка-двойник с этим профилем | test_kassir_01 |
| Сценарий | Что именно открываем | Список чеков за период |
| Объект | Конкретные данные | Чеки магазина №7 и магазина №12 |
| Ожидание | Что должно быть видно | Только магазин №7 |
| Замер “до” | Факт до изменения | 1 240 строк, хеш a3f… |
| Замер “после” | Факт после | 1 240 строк, хеш a3f… |
| Вердикт | Совпало / расхождение | ✅ |
Ключевое слово в названии - эквивалентность. Мы не проверяем, что права “правильные” в абсолютном смысле: это отдельная и куда более долгая работа. Мы доказываем, что после изменения видимость осталась точно такой же, как была до него. Это узкая задача, и именно поэтому она решаема за разумное время: у неё есть объективный критерий - совпадение с эталоном, снятым до работ.
Отсюда железное правило: срез “до” снимается до первой правки. Если изменения уже внесены, эталона больше нет, и матрица превращается в упражнение по угадыванию “а как оно было”.
Шаг 1. Инвентаризация: какие роли вообще ограничивают доступ
Начинать с полного списка ролей бессмысленно - в типовой конфигурации их сотни, и большинство не имеет отношения к ограничениям на уровне записей. Нужны те, где есть ограничения по условию.
Надёжный способ их найти - выгрузить конфигурацию в XML-файлы (Конфигуратор → Конфигурация → Выгрузить конфигурацию в файлы) и поискать в описаниях ролей элементы ограничений:
findstr /S /I /M "restrictionByCondition" C:\unload\Roles\*.xml
Каждый найденный файл - роль, которая реально что-то ограничивает. В нашем проекте таких оказалось 208 при формально “выключенном” RLS: регламентные задания пересчёта ключей не работали с 2020 года, и все были уверены, что механизм мёртв. Он был жив и ежедневно использовался - просто его кэширующая часть превратилась в свалку на 3,85 ТБ.
Дальше список ролей сокращается до профилей, которые реально выданы людям. Роль, не входящая ни в один профиль групп доступа, в матрицу не идёт - её видимость никого не касается:
-- Оценка сверху: сколько живых пользователей у каждого профиля.
-- Точные имена таблиц берутся из карты объёмов
-- (ПолучитьСтруктуруХраненияБазыДанных), они у каждой базы свои.
SELECT p._Description AS profile_name, COUNT(DISTINCT m._UserRef) AS users
FROM dbo._Reference_Profiles AS p
JOIN dbo._InfoRg_AccessGroupsUsers AS m ON m._ProfileRRef = p._IDRRef
JOIN dbo._Reference_Users AS u ON u._IDRRef = m._UserRef AND u._Marked = 0x00
GROUP BY p._Description
ORDER BY users DESC;
Практика показывает: из сотен ролей до матрицы доживают 15-30 профилей, и это уже подъёмный объём.
Шаг 2. Сценарии: что именно смотреть
Сценарий - это конкретное место, где ограничение проявляется. Не “проверить доступ к чекам”, а “открыть список чеков за вчера в рабочем месте кассира”. Разница принципиальна: первое невозможно воспроизвести дважды одинаково, второе - можно.
Что обязательно попадает в набор сценариев:
- Динамические списки рабочих мест - то, что пользователь открывает каждый день: чеки, заказы, документы отгрузки, справочник контрагентов.
- Отчёты с отбором по организации/подразделению/складу - там ограничения проявляются иначе, чем в списках, и часто на другом наборе таблиц.
- Формы выбора и подбора - классическое место утечки: список закрыт, а форма подбора отдаёт всё. Проверять отдельно.
- Печатные формы и выгрузки - обмены, веб-сервисы, выгрузка на сайт. Они часто ходят под служебной учёткой, и это отдельная строка матрицы.
- Негативные проверки - попытка открыть по ссылке объект чужого магазина. Ожидание: отказ. Это единственный тип строк, где “пусто” является правильным ответом.
Пункт 5 обычно и ловит ошибки. Списки после переделки прав почти всегда фильтруются корректно - а вот прямое обращение по ссылке, открытие из истории или переход по гиперссылке в отчёте нередко остаются открытой дверью.
Итоговый объём считается просто: профили × сценарии. Наши 329 строк - это ~20 профилей на ~16 сценариев, из которых часть неприменима (кассиру нечего делать в отчётах казначейства).
Шаг 3. Тестовые пользователи-двойники
Снимать срез под живым сотрудником нельзя: он работает, его данные меняются, а пароль вы не знаете и знать не должны. Поэтому под каждый проверяемый профиль заводится учётка-двойник: те же группы доступа, те же настройки, никакой личной привязки.
Правила, выстраданные практикой:
- Двойник создаётся копированием настроек доступа реального пользователя, а не “примерно так же” руками. Любое расхождение в профилях делает весь замер бессмысленным.
- Имя с явным префиксом (
test_), чтобы их нельзя было спутать с людьми ни в журнале регистрации, ни в отчётах. - Пароли живут в вашем хранилище секретов, а не в файле рядом с обработкой и не в тексте задачи.
- После приёмки двойники отключаются (не удаляются - они понадобятся при следующем изменении прав, а их создание заново стоит времени).
- Двойники не должны попадать в бизнес-отчётность: проверьте, что они не входят в списки ответственных и рассылки.
Отдельно про лицензии: одновременный запуск двадцати сеансов под двойниками съедает двадцать лицензий. На проде это делается в окно, когда людей нет, либо срез снимается последовательно - по одному сеансу за раз.
Шаг 4. Съём среза: не глазами, а хешем
Ручная проверка “открыл, посмотрел, вроде правильно” не масштабируется дальше десяти строк и не воспроизводится. Она ещё и обманчива: отчёт может выйти пустым не потому, что данные скрыли, а потому что компоновка молча выбросила поле без права просмотра - глазами эти два случая неразличимы. Поэтому срез снимается кодом и сворачивается в контрольную сумму.
Обработка, которая запускается под тестовым пользователем и по списку сценариев считает хеш видимой выборки:
// Срез видимости под ТЕКУЩИМ пользователем сеанса.
// Ограничения доступа применяются платформой автоматически:
// именно поэтому обработка запускается в сеансе двойника, а не администратора.
Функция ХешСценария(ТекстЗапроса, Параметры) Экспорт
Запрос = Новый Запрос(ТекстЗапроса);
Для Каждого КлючЗначение Из Параметры Цикл
Запрос.УстановитьПараметр(КлючЗначение.Ключ, КлючЗначение.Значение);
КонецЦикла;
Таблица = Запрос.Выполнить().Выгрузить();
// Нормализация: порядок строк и колонок не должен влиять на результат
Колонки = Новый Массив;
Для Каждого Колонка Из Таблица.Колонки Цикл
Колонки.Добавить(Колонка.Имя);
КонецЦикла;
Колонки.Сортировать();
Таблица.Сортировать(СтрСоединить(Колонки, ","));
Хеш = Новый ХешированиеДанных(ХешФункция.SHA256);
Для Каждого Строка Из Таблица Цикл
Значения = Новый Массив;
Для Каждого ИмяКолонки Из Колонки Цикл
Значения.Добавить(XMLСтрока(Строка[ИмяКолонки]));
КонецЦикла;
Хеш.Добавить(СтрСоединить(Значения, "|") + Символы.ПС);
КонецЦикла;
Возврат Новый Структура("Строк, Хеш",
Таблица.Количество(),
ПолучитьHexСтрокуИзДвоичныхДанных(Хеш.ХешСумма));
КонецФункции
Две детали, без которых хеш врёт:
- Нормализация обязательна. Порядок строк в результате запроса без явной сортировки не гарантирован - два одинаковых по составу среза дадут разные хеши, и вы будете искать несуществующее расхождение. Сортируем и строки, и колонки.
XMLСтрока, а неСтрока. Представление значения зависит от локали и настроек пользователя: дата в одном сеансе “03.08.2026”, в другом “2026-08-03”.XMLСтрокадаёт стабильное представление, не зависящее от того, кто смотрит.
Запуск под двойником - пакетный, без участия человека:
"C:\Program Files\1cv8\common\1cestart.exe" ENTERPRISE ^
/S"srv-app\trade_base" /N"test_kassir_01" /P"***" ^
/Execute "C:\audit\SnyatSrez.epf" ^
/C"scenarios=C:\audit\scenarios.json;out=C:\audit\before\kassir_01.json" ^
/DisableStartupMessages /UC"audit"
На выходе - файл вида “сценарий → количество строк → хеш” по каждому двойнику. Это и есть колонка “до”. После изменений тот же запуск пишет в папку after, и сравнение сводится к посимвольному сличению двух наборов JSON - операции, которая не зависит от внимательности человека в три часа ночи.
Подробно про сам приём сверки контрольной суммой - в отдельной статье: SHA-256 вместо “на глаз похоже”.
Шаг 5. Разбор расхождений
Расхождение - это не “ошибка теста”, это стоп-сигнал. Порядок разбора:
- Совпадает количество строк, различается хеш. Изменился состав или содержимое видимых записей. Самый опасный вариант: одну запись потеряли, другую получили - суммарно ноль, фактически утечка. Разбирается сравнением самих выгрузок построчно.
- Стало больше строк. Пользователь видит лишнее. Останавливаемся, не выпускаем изменение в прод. Никаких “ну там всего две записи”.
- Стало меньше строк. Пользователь потерял доступ. Менее опасно для безопасности, но это гарантированный простой людей утром.
- Совпало всё, кроме порядка - значит, нормализация неполная; чините её, а не матрицу.
Отдельный класс - “расхождения, которые объясняются данными”: между двумя замерами кто-то провёл новый документ, и он честно попал во второй срез. Лечится тем, что срезы снимаются на замороженном контуре или на копии базы, а не на живой системе в рабочее время. Если это невозможно - сценарии ограничиваются закрытым периодом (например, “чеки за прошлый месяц”), в который новые данные не приходят.
Шаг 6. Приёмка
Матрица - не наш внутренний документ, а основание для приёмки, и подписывать её должен заказчик. У нас процесс выглядел так: после переключения представители заказчика под ограниченными правами (кассир, менеджер) прошли по чекам, отчётам, остаткам и разграничению доступа - реальными руками, по своим рабочим сценариям, а не по нашим.
Мы вообще считаем, что приёмка глазами исполнителя - оксюморон: исполнитель смотрит туда, где ожидает увидеть успех. Автоматический замер даёт объективность, живая приёмка - уверенность, что автоматика проверяла именно то, что людям нужно. Нужны обе.
Частые вопросы
Чем матрица эквивалентности отличается от обычного тестирования ролей?
Тестирование ролей отвечает на вопрос “правильно ли настроены права” - это про соответствие политике доступа, и делается один раз при внедрении. Матрица эквивалентности отвечает на вопрос “изменилась ли видимость после правки” - это про регресс, и делается при каждом массовом изменении. У первого критерий субъективный (правильно - это как?), у второго объективный: совпало с эталоном или нет.
Сколько строк должно быть в матрице?
Столько, сколько даёт произведение “живые профили × применимые сценарии”, за вычетом бессмысленных комбинаций. У нас вышло 329. Для средней базы это 100-400 строк. Если получается меньше пятидесяти - вы, скорее всего, забыли негативные проверки и формы подбора; если больше тысячи - в набор попали дубли сценариев, отличающиеся только периодом.
Можно ли обойтись сравнением настроек прав вместо замеров?
Нет, и это главная ловушка. Совпадение настроек (те же роли, те же профили) не гарантирует совпадения видимости: результат зависит ещё от данных - состава групп доступа, значений в справочниках-измерениях ограничений, состояния кэша ключей. Мы дважды за один проект видели, как формальный признак расходился с фактом: “уволенные” пользователи с живыми транзакциями и “выключенный” RLS с 208 активными ролями. Проверять надо факт видимости, а не декларацию о ней.
Как проверять права после обновления конфигурации?
Тот же алгоритм: срез “до” снимается на копии базы перед обновлением, обновление накатывается на ту же копию, снимается срез “после”. Разница в том, что при обновлении типовых ролей расхождения ожидаемы - вендор мог осознанно изменить состав прав. Задача матрицы здесь не “добиться нулей”, а предъявить полный список изменений видимости и по каждому принять явное решение: принимаем как есть или компенсируем настройкой.
Нужна ли матрица, если RLS не используется?
В усечённом виде - да. Без ограничений на уровне записей остаются права на объекты (чтение/изменение справочников и документов) и функциональные опции. Сценариев меньше, съём проще (достаточно ПравоДоступа и проверки доступности объектов), но сама логика “эталон до → замер после → нулевое расхождение” не меняется. А для разведки перед изменением - понять, кто что может сейчас и какие роли дублируют друг друга, - удобнее другой заход: аудит прав доступа через нейросеть, где та же ПравоДоступа собирает матрицу в файл для чат-модели.
Как часто пересобирать матрицу?
Набор сценариев живёт долго - его пересматривают, когда появляются новые рабочие места или отчёты. А вот эталонные срезы устаревают всегда: они привязаны к данным. Поэтому эталон снимается заново перед каждым изменением прав, а не берётся из прошлого проекта.
Итог
Матрица эквивалентности - самая скучная таблица проекта. Её долго собирать, её неинтересно заполнять, и в отчёте она выглядит как строка “проверено 329 комбинаций, расхождений 0”. Ровно в этом и ценность: скучная таблица заметно приятнее интересного инцидента.
Три принципа, если забирать из статьи только их:
- Эталон снимается до первой правки - потом его взять негде.
- Сравнивает машина, а не человек - нормализованная выгрузка и хеш вместо “выглядит похоже”.
- Ноль расхождений или стоп - “всего две лишние записи” это и есть утечка доступа, просто маленькая.
Как этот инструмент работал на реальном переключении механизма прав на 200 кассах - в кейсе обрезки базы 4,2 ТБ и в техническом разборе той же ночи. Про выбор между универсальным и классическим RLS, из-за которого всё и затевалось, - отдельно. А если менять права предстоит вам, но брать на себя этот контур не хочется, - мы делаем оптимизацию и переработку механизмов доступа как услугу, с матрицей и протоколом приёмки на выходе.