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>
5.8 KiB
5.8 KiB
Итог: раздел интерфейса «Пользователи» (изменение 006)
Что сделано
- API
/api/v1/users(app/api/v1/users.py, роутер подключён вapp/main.py):GET /users(поиск по логину,limit/offset),POST /users,PATCH /users/{id},DELETE /users/{id}— толькоadmin;POST /users/me/password— смена своего пароля любой ролью с подтверждением текущего. - Схемы (
app/schemas.py):UserIn,UserUpdate,PasswordChange,UserOut(id,username,role,is_active); валидатор логина (Login, 3–100 символов,[A-Za-z0-9._-]) и запрет служебных логиновsystem/anonymous. - Журнал: сущность
userдобавлена в словарь сообщений (app/services.py), событияuser.created,user.updated,user.password_reset(«пароль изменён администратором» / «пароль изменён пользователем»),user.deleted; вdiff— только изменённые поля, для пароля — пометка «изменён» без значения. В UI — подпись «Пользователь» и цвет бейджа дляpassword_reset. - UI (
web/app.js): пункт навигации «Пользователи» (только у администратора), экран со счётчиком, поиском и таблицей «Логин · Роль · Статус» (строка кликабельна, меню «⋯»: редактировать, отключить/разрешить доступ, удалить), окно создания и редактирования (логин после создания не редактируется, необязательный «Новый пароль», флажок «Доступ разрешён»), кнопка «Сменить пароль» в шапке, заглушка для не-администратора при ручном вводе#/users. - Тест
tests/test_users.py— один сквозной сценарий. - Документация: обновлён
README.md, план и итог вdocs/changes/006-users-management/.
Поведение
| Ситуация | Ответ |
|---|---|
| Логин занят (в т. ч. в другом регистре) | 409 «Пользователь с таким логином уже существует» |
Логин system/anonymous, короче 3 символов, пароль короче 8 |
422 с указанием поля |
viewer читает /users или что-либо изменяет |
403 |
PATCH/DELETE своей учётной записи (роль, доступ, удаление) |
409 |
| Отключение или удаление последнего активного администратора | 409 |
| Неверный текущий пароль при смене своего пароля | 403 |
| Новый пароль совпадает с текущим | 422 |
Действие ролей
admin— полный доступ, включая управление пользователями и запись данных.viewer— чтение всех справочников, реестра, журнала и обзора; запись (403) и раздел «Пользователи» недоступны.- Отключение учётной записи действует немедленно: токен проверяется по
users.is_activeвcurrent_user. - Смена пароля не отзывает уже выданные токены (JWT без списка отзыва) — они действуют до истечения
JWT_TTL_MINUTES.
Инварианты
- Схема БД не менялась: таблица
users(миграция 0001) уже содержитusername,password_hash,role,is_active. - Пароли хранятся только в виде argon2-хэша; значение пароля никогда не попадает в журнал.
- Логин не меняется: он же
subв JWT; переименование = создание новой записи. - Логин уникален, в том числе без учёта регистра; служебные акторы журнала зарезервированы.
- Перед каждым изменением проверяется, что в системе останется хотя бы один активный администратор.
Проверка
- Python:
python3 -m py_compileпо изменённым модулям — без ошибок; автоматическая проверка после правок — успешно. - Интеграционный тест:
docker compose up -d --build && venv/bin/python -m pytest tests/test_users.py -q(создание viewer, запреты 403/409/422, вход, отключение с немедленным отзывом токена, сброс пароля администратором, смена своего пароля, защита своей записи и последнего администратора, удаление, записи в журнале). - Ручной сценарий в UI: вход администратором → «Пользователи» → создание viewer → вход в другом окне → отключение (вход и текущая сессия отбиваются) → сброс пароля → вход с новым паролем → «Сменить пароль» в шапке.