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>
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; ручной сценарий в браузере (создание, редактирование, отключение, вход отключённым, удаление).