From c6be70ce365e952bcc4d47ae4cce0c2fc9c63b04 Mon Sep 17 00:00:00 2001 From: viktot14-ai Date: Fri, 25 Sep 2026 09:58:23 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=BA=D0=B8=20B23:=20README=20(010?= =?UTF-8?q?,=20password=5Fhistory,=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=D0=BB=D0=B8=D1=82=D0=B8=D0=BA=D0=B8=20?= =?UTF-8?q?=D1=81=20=D0=BA=D0=BE=D0=BB=D0=BE=D0=BD=D0=BA=D0=BE=D0=B9=20?= =?UTF-8?q?=C2=AB=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=C2=BB),=20SETUP=5FGUIDE=20(cron=20=D0=B1=D1=8D=D0=BA?= =?UTF-8?q?=D0=B0=D0=BF=D0=B0+purge,=20alembic=20010,=20=D0=B4=D0=B8=D0=B0?= =?UTF-8?q?=D0=B3=D0=BD=D0=BE=D1=81=D1=82=D0=B8=D0=BA=D0=B0=20400-=D0=BF?= =?UTF-8?q?=D0=B0=D1=80=D0=BE=D0=BB=D1=8C),=20=D1=87=D0=B5=D0=BA-=D0=BB?= =?UTF-8?q?=D0=B8=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 40 ++++++++++++++++++++++++---------------- SETUP_GUIDE.md | 21 ++++++++++++++++++--- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 5ae00bf..808fd25 100644 --- a/README.md +++ b/README.md @@ -143,17 +143,19 @@ vector/ ├── backend/ # FastAPI-приложение │ ├── main.py # Сборка приложения (12 роутеров) -│ ├── models.py # 21 таблица (SQLAlchemy) +│ ├── models.py # 22 таблицы (SQLAlchemy, +password_history) │ ├── schemas.py # Pydantic-схемы │ ├── database.py # Репозиторий кейсов + статистика │ ├── audit.py # Журнал аудита + гео-скоуп +│ ├── password_policy.py # B23: исполняемая политика паролей +│ ├── purge_logs.py # B23: ретенция журналов (DELETE, cron) │ ├── rbac.py # Роли/права/политика безопасности -│ ├── alembic/ # Миграции (006…009) +│ ├── alembic/ # Миграции (006…010) │ ├── seed_users.py # Сид первых пользователей │ ├── seed_units.py # Сид подразделений (РЦУ РЧС → ОУМЧС → Г(Р)ОЧС, 142 юнита) │ ├── routers/ # 12 модулей API (см. раздел 8) │ ├── services/ # Адаптеры расчёта -│ └── tests/ # 231 тест (pytest) +│ └── tests/ # 263 теста (pytest) ├── services/ # Математика (чистые функции) │ ├── search_engine.py # SearchInput/SearchModel — ядро расчёта │ ├── distance_service.py # Максимальный радиус (КМЧ × коэффициенты) @@ -187,7 +189,7 @@ vector/ ## 6. База данных -**PostgreSQL 16 + PostGIS 3.6**, БД `vector_mchs`. Схема управления — Alembic (`backend/alembic/versions/`), актуальная ревизия **009_e3_operations**. +**PostgreSQL 16 + PostGIS 3.6**, БД `vector_mchs`. Схема управления — Alembic (`backend/alembic/versions/`), актуальная ревизия **010_b23_policy**. ### Основные таблицы @@ -199,25 +201,30 @@ vector/ | `search_teams`, `field_observations`, `areas_checked`, `found_events` | Оперативные слои (состав групп, наблюдения, проверенные квадраты, находки) | | `reference_priors` | Слой 1: справочные приоритеты | | `users`, `mchs_units` | Пользователи; иерархия РЦУ РЧС → 6 ОУМЧС → 135 Г(Р)ОЧС | +| `password_history` | История паролей (bcrypt-хэши; проверка переиспользования, B23) | | `roles`, `permissions`, `role_permissions`, `user_roles` | RBAC: 4 роли, 9 прав, матрица связей | | `sessions`, `auth_events` | Server-side сессии (sha256-токены), журнал входов | -| `security_settings` | Политика безопасности (10 параметров) | +| `security_settings` | Политика безопасности (10 параметров, **исполняется кодом с B23**) | | `search_operations` | Поисковые операции (статусная машина, привязка к кейсу и КОНТУРу) | | `audit_events`, `audit_changes` | Неизменяемый журнал аудита, field-level изменения (ретенция 90 дней) | | `planet_osm_*` | Импорт OSM Беларуси (osm2pgsql, SRID 4326/3857): дороги, леса, вода, НП | ### Политика безопасности (`security_settings`, меняется в админке) -| Параметр | Значение | -|----------|----------| -| Минимальная длина пароля | 8 | -| Сложность пароля | требуются буквы+цифры | -| Срок действия пароля | 90 дней | -| История паролей | 5 (нельзя переиспользовать) | -| Блокировка после неудачных входов | 5 (на 15 минут) | -| Тайм-аут сессии / неактивности | 24 ч / 60 мин | -| Макс. параллельных сессий | 3 (старые отзываются) | -| Ретенция журнала аудита | 90 дней | +| Параметр | Значение | Исполнение (B23) | +|----------|----------|------------------| +| Минимальная длина пароля | 8 | ✅ при смене/сбросе пароля | +| Сложность пароля | требуются буквы+цифры | ✅ при смене/сбросе пароля | +| Срок действия пароля | 90 дней | ✅ `enforce_expiry` на каждом запросе → `must_change_password` | +| История паролей | 5 (нельзя переиспользовать) | ✅ таблица `password_history` (5 последних старых хэшей + текущий) | +| Блокировка после неудачных входов | 5 (на 15 минут) | ✅ (с B21) | +| Тайм-аут сессии | 24 ч | ✅ проверка `expires_at` при каждом запросе | +| Тайм-аут неактивности | 60 мин | ⚠️ `last_seen_at` пишется, но не проверяется | +| Макс. параллельных сессий | 3 (старые отзываются) | ✅ (с B21) | +| Ретенция журнала аудита | 90 дней | ✅ реальный DELETE: cron `python -m backend.purge_logs` (03:10) или POST `/admin/users/maintenance/purge-logs` | + +Семантика истории: `password_history_count = N` хранит N последних *старых* хэшей; +вместе с текущим паролем защищено N+1 последних значений. --- @@ -401,7 +408,8 @@ SPA на react-scripts (CRA). Роутинг: - Прикладное логирование `vector.*`: события анализа, входов, КОНТУРа, аудита (README §13а) - Интеграция с КОНТУР: отправка зон (контракт B20) - Журнал аудита пользователей: входы, изменения, отправки -- 239 автотестов (pytest), 5 skipped +- 263 автотеста (pytest), 5 skipped +- B23: исполняемая политика паролей (срок/сложность/история), ретенция журналов реальным DELETE + cron ### В планах diff --git a/SETUP_GUIDE.md b/SETUP_GUIDE.md index 5439b46..f03e96d 100644 --- a/SETUP_GUIDE.md +++ b/SETUP_GUIDE.md @@ -153,7 +153,7 @@ docker compose ps # все 4 контейнера Up/healthy ```bash docker exec vector-backend sh -c 'cd /app/backend && alembic upgrade head' -docker exec vector-backend sh -c 'cd /app/backend && alembic current' # 009_e3_operations +docker exec vector-backend sh -c 'cd /app/backend && alembic current' # 010_b23_policy ``` Миграции идемпотентны на чистой БД. **Если** БД уже была инициализирована через create_all и `alembic upgrade` падает `DuplicateTable` — применить стемп-скрипт: @@ -330,6 +330,18 @@ docker exec vector-postgres pg_dump -U postgres -d vector_mchs \ Рекомендация: ежедневный cron прикладного дампа (он небольшой) + еженедельный полный. Хранить копии вне сервера. +### Cron (рекомендуемый на сервере) + +```cron +# 03:10 — ретенция журналов (удаление audit_events/auth_events старше audit_retention_days) +10 3 * * * docker exec vector-backend python -m backend.purge_logs >> /var/log/vector-purge.log 2>&1 +# 03:00 — прикладной дамп (planet_osm_* исключены; OSM восстанавливается b16-import.sh) +0 3 * * * docker exec vector-postgres pg_dump -U postgres -d vector_mchs --exclude-table="planet_osm_*" -F c -f /tmp/vector_app_$(date +\%F).dump && docker cp vector-postgres:/tmp/vector_app_$(date +\%F).dump /root/backups/ && find /root/backups -name vector_app_*.dump -mtime +14 -delete >> /var/log/vector-backup.log 2>&1 +``` + +Проверка разового прогона: `docker exec vector-backend python -m backend.purge_logs` +→ `purge: {'audit_events': N, 'auth_events': N}` (0 — норма). + **Docker volume — не backup.** Если сервер умер вместе с диском, volume умер вместе с ним. Что подлежит резервному копированию: ```text @@ -412,6 +424,8 @@ df -h && free -h # диск/память ### Диагностика по цепочке (начинать с места проблемы, не с перезапусков) +**Смена пароля даёт 400 «Пароль должен содержать буквы и цифры» / «совпадает с ранее использованным»** — это B23-политика (`security_settings`), не ошибка: с 25.09.2026 требования сложности и истории исполняются. Пароль, возвращённый ранее, отвергается, пока он в последних `password_history_count` записях. + **Бэкенд не подключается к БД** — цепочка `backend → docker network → postgres:5432 → authentication → vector_mchs`: ```bash @@ -490,7 +504,7 @@ docker exec vector-postgres psql -U postgres -d vector_mchs -c \ - [ ] прикладные логи `vector.*` доступны (`docker compose logs backend`, файл в контейнере) - [ ] аудит 90 дней читается (вкладка «Аудит», право `view_audit`) -- [ ] ретенция настроена в `security_settings` +- [ ] ретенция **исполняется**: cron purge-логов установлен, `python -m backend.purge_logs` даёт счётчики ### Backup @@ -506,7 +520,8 @@ docker exec vector-postgres psql -U postgres -d vector_mchs -c \ - [ ] Код получен (`/root/vector`), `.env` создан из `.env.example` - [ ] `JWT_SECRET` заменён на случайный; пароль БД сменён (compose + DATABASE_URL) - [ ] `docker compose up -d` — 4 контейнера Up -- [ ] `alembic current` = `009_e3_operations` +- [ ] `alembic current` = `010_b23_policy` +- [ ] cron бэкапа И cron purge-логов настроены (`crontab -l`), первый дамп лежит в /root/backups - [ ] `seed_users.py` выполнен, пароль админа сменён при первом входе - [ ] `seed_units.py` выполнен (142 подразделения в селектах) - [ ] PostGIS установлен в контейнере БД, extension создан