# Итог: ролевая модель с привязкой к организации и суперадминистратором (изменение 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` (значение enum `superadmin` через `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` в записях журнала для уже удалённых на момент миграции сущностей — сознательно не выполнялась (план, раздел «Вне объёма»).