Пентест (docs/reviews/2026-09-27-pentest.md) и план 031 (Swagger, TLS) — план, не реализован.
032 Роль superadmin (без организации) и привязка admin/viewer к одной организации:
users.organization_id + CHECK, audit_log.organization_id (миграции 0010-0012);
require_org/scope_org во всех чтениях и записях, журнал и «Обзор» в границах
организации; пользователи, организации, типы устройств, настройки журнала — только superadmin.
033 Исправление находок ревью 032 (docs/reviews/2026-09-27-changes-032-review.md,
docs/reviews/2026-09-27-codebase-review.md):
- FK audit_log.organization_id ON DELETE SET NULL (миграция 0013) — удаление организаций;
- проверка организации в предпросмотре подсети;
- инвариант «роль — организация» по итоговому состоянию (повышение снимает организацию,
понижение требует её), 422/404 вместо обезличенных 409;
- одинаковый 404 для чужих и несуществующих объектов (VRF, устройство, parent_id, оператор);
- отказы удаления в журнале организации, счётчики типов в пределах организации;
- UI: живое поле «Организация» в диалоге пользователя, бейдж superadmin; род в текстах 404.
README актуализирован под ролевую модель.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.9 KiB
Итог: ролевая модель с привязкой к организации и суперадминистратором (изменение 032)
План: docs/changes/032-role-model-org-scope/PLAN.md. Ревью реализации — docs/reviews/2026-09-27-changes-032-review.md
(находки № 1–8) и docs/reviews/2026-09-27-codebase-review.md (находки № 9–15); все они исправлены в изменении 033
(docs/changes/033-review-fixes-032/SUMMARY.md) — этот файл описывает исходную реализацию 032 в части модели данных
и объёма прав, без деталей самих находок ревью.
Модель данных
app/models.py:Role.superadmin;User.organization_id(nullable,FK organizations.id, индекс) сCheckConstraint("is_active = false OR (role = 'superadmin') = (organization_id IS NULL)")(имяck_users_role_org_scopeв модели — с 033);AuditLog.organization_id(nullable, индекс,FK organizations.id).- Миграции:
0010_user_role_superadmin.py(значение enumsuperadminчерезautocommit_block()),0011_users_organization_scope.py(колонкаusers.organization_id, backfill ролей, CHECK после backfill),0012_audit_log_organization.py(колонкаaudit_log.organization_id, backfill поentity_idдляorganization/vrf/device/isp/prefix; для удалённых сущностей и системных типов (user/journal/session/device_type) остаётсяNULL— видно толькоsuperadmin). - Backfill ролей (решение 4 плана): бывшие
admin→superadmin,organization_id=NULL; бывшиеviewer→organization_id=NULL,is_active=false(заблокированы до явного назначения организацииsuperadmin). - Удаление организации (
delete_org) получило блокиратор"users"— организация с привязанными пользователями не удаляется.
Права доступа
Реализовано по сводной таблице плана: superadmin управляет пользователями, организациями (создание/удаление) и
типами устройств (запись); admin/viewer работают только в своей организации (VRF, префиксы, адреса, устройства,
операторы, карточка организации, журнал, «Обзор»); типы устройств читают все роли.
app/security.py:admin_userтеперь пропускает роль ≠viewer(то естьadminиsuperadmin); новая зависимостьsuperadmin_user— строгоRole.superadmin.app/services.py: хелперыrequire_org(user, organization_id, what)(404 при чужой организации,superadmin— без ограничений) иscope_org(stmt, column, user)(фильтр списков);audit()получил параметрorganization_idдля простановки на журнальных записях (refuse_delete()— с 033).app/api/v1/users.py— все маршруты наsuperadmin_user; инвариант «нельзя обезвредить последнего активного администратора» переведён наsuperadmin.app/api/v1/refs.py,app/api/v1/prefixes.py,app/api/v1/journal.py,app/api/v1/overview.py—require_org/scope_orgво всех точках чтения и записи, разделение прав на организации/типы устройств/журнал по сводной таблице.app/main.py:seed()создаёт пользователя-бутстрап сrole=Role.superadmin,organization_id=None.
UI (web/app.js, web/styles.css)
ROLE_RUдополнен «Суперадминистратор»;isAdmin()= роль ≠viewer, новаяisSuperadmin().- Пункт навигации «Пользователи» — только
isSuperadmin().orgSwitcher()интерактивен только дляsuperadmin; дляadmin/viewer— статичное название их организации. - Экран «Организации»: «Добавить организацию» и «Удалить» — только
superadmin. - Экран «Пользователи»: диалог создания/правки получил выбор организации (скрывается для роли «Суперадминистратор»).
- Диалог типов устройств: управление — только
superadmin, список виден всем.
Отклонения от плана
Без отклонений в объёме прав и модели данных. Первый проход реализации содержал несколько дефектов (сломанное
удаление организации, невалидируемый инвариант «роль — организация» при отсутствии поля в теле запроса, нерабочая
смена роли на/с superadmin, невидимость *.delete_blocked в журнале организации, оракулы существования чужих
объектов, глобальные счётчики типов устройств и мелкие UI-огрехи) — все исправлены отдельным циклом, изменение 033.
Проверено
import app.main,node --check web/app.js— чисто.- Миграции 0010–0012 применяются на стенде;
alembic checkбез расхождений (после 033 — включая 0013). - Изоляция чтения/записи по организациям, роли
admin/viewer/superadmin, управление типами устройств и пользователями — проверено на стенде (см. протокол сценариев вdocs/reviews/2026-09-27-changes-032-review.mdи итоговые проверки вdocs/changes/033-review-fixes-032/SUMMARY.md).
Не проверено
- Работоспособность 032 отдельно от 033 — не актуально (033 доводит 032 до готовности, коммитятся вместе).
- Полная историческая реконструкция
organization_idв записях журнала для уже удалённых на момент миграции сущностей — сознательно не выполнялась (план, раздел «Вне объёма»).