Files
ayurishchevandClaude Opus 5.5 744a025960 Задачи 032-033: ролевая модель с привязкой к организации, исправления по ревью
Пентест (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>
2026-09-27 11:26:57 +03:00

61 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Итог: ролевая модель с привязкой к организации и суперадминистратором (изменение 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` в записях журнала для уже удалённых на момент миграции сущностей —
сознательно не выполнялась (план, раздел «Вне объёма»).