Files
ayurishchevandClaude Sonnet 5 cd09ef0805 Задачи 006-010: пользователи, исправление удаления, журнал отказов, групповые операции, автовыделение префиксов
006 Пользователи: API /users (CRUD, смена своего пароля), раздел UI «Пользователи»,
    события журнала user.*, защита от отключения/удаления себя и последнего админа.
007 Исправление удаления организации: VRF удаляются явным DELETE до организации
    (без relationship() порядок DELETE не гарантирован → ложный 409).
008 Журнал фиксирует отказы в удалении (<entity>.delete_blocked) со списком
    мешающих объектов в «Данных»: организация, VRF, тип устройства, префикс, пользователь.
009 Выбор строк чекбоксами и групповые операции в UI (удаление, смена типа устройств,
    статус префиксов и адресов, доступ пользователей); цикл запросов из UI, итог и список отказов.
010 Автовыделение следующего вложенного префикса: POST/GET /prefixes/{id}/subnets/next,
    первый свободный выровненный блок; пункт «Добавить вложенный (авто)» в меню префикса.

Тесты: 14 (добавлены сценарии для 006, 007/008, 010); исправлена нестабильность
тестов журнала (IPv6-группы с ведущими нулями нормализуются PostgreSQL).
Документация: README.md, docs/changes/006-010 (планы и итоги).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 13:25:33 +03:00

6.1 KiB

Раздел интерфейса «Пользователи» (изменение 006)

Context

Роли admin (запись) и viewer (чтение) заявлены в README и в UI (журнал: настройки и очистка скрыты для не-админа), но назначить роль негде: таблица users заполняется только записью администратора при первом старте (seed() в app/main.py), эндпоинтов управления пользователями нет, сменить пароль — тоже нельзя. Нужен раздел интерфейса, где администратор заводит учётные записи, меняет им роль/пароль и отключает доступ, а любой пользователь может сменить свой пароль.

Модель (без изменения схемы БД)

Таблица users (миграция 0001) уже содержит всё необходимое: id, username (уникальный), password_hash (argon2), role (user_role: admin/viewer), is_active. Миграция не требуется. Пароли не хранятся и не пишутся в журнал в открытом виде.

API (/api/v1/users, все — под авторизацией)

Метод Кто Назначение
GET /users admin список: логин (поиск q), роль, признак активности, limit/offset
POST /users admin создание: username, password (≥ 8), role, is_active
PATCH /users/{id} admin роль, is_active, необязательный новый password
DELETE /users/{id} admin удаление учётной записи
POST /users/me/password любой смена своего пароля с подтверждением текущего

Правила (иначе 409/422, состояние не меняется):

  • логин: 3–100 символов, [A-Za-z0-9._-], уникален; зарезервированные system и anonymous запрещены — журнал различает служебные события по этим именам (actor_of в app/services.py);
  • нельзя удалить или отключить/понизить свою учётную запись (иначе текущая сессия теряет доступ);
  • нельзя отключить, понизить или удалить единственного активного администратора (система осталась бы без прав записи);
  • неверный текущий пароль при смене своего пароля → 403.

Аудит: user.created, user.updated, user.password_reset (пароль администратором и своя смена), user.deleted; в entity_label — логин, в diff — только изменённые поля (password отмечен как «изменён», без значения). Сущность user добавлена в словарь человекочитаемых сообщений (_NOUNS в app/services.py).

UI (web/app.js)

  • Пункт навигации «Пользователи» — только для администратора (NAV фильтруется в shell()); при ручном вводе #/users не-админ видит заглушку «Раздел доступен только администратору».
  • Экран: счётчик записей, поиск по логину, таблица «Логин · Роль · Статус · Действия»; строка кликабельна (паттерн изменения 005), в меню «⋯» — «Редактировать», «Отключить/Активировать», «Удалить».
  • Окно создания/редактирования: логин (при создании), роль, флажок «Доступ разрешён», необязательное поле «Новый пароль» (пустое — пароль не меняется); окно смены своего пароля — в шапке («Сменить пароль»).
  • В журнале: подпись сущности «Пользователь», тип события в фильтре типов появляется автоматически (/audit/facets).

Ограничения (осознанные)

  • JWT без списка отзыва: после смены пароля уже выданные токены продолжают работать до истечения JWT_TTL_MINUTES; отключение учётной записи действует немедленно (токен проверяется по users.is_active в current_user).
  • Логин менять нельзя (он же sub в токене) — вместо этого создаётся новая учётная запись.
  • Нет саморегистрации, восстановления пароля по e-mail, групп прав и журналирования последнего входа.

Проверка

  • Интеграционный тест tests/test_users.py (один сценарий, против контейнеров): создание, дубль логина → 409, запрет служебного логина → 422, вход новым пользователем, запрет записи для viewer → 403, отключение → вход 401, сброс пароля → вход с новым паролем, смена своего пароля и вход с ним, удаление своей записи → 409.
  • Синтаксическая проверка Python и JS; ручной сценарий в браузере (создание, редактирование, отключение, вход отключённым, удаление).