docs: «нет заголовка Authorization или схема не Bearer» — не попытка входа (ревью R-7, #9)
Формулировка уточнена в докстрингах `services/session_audit.py` и `api/dependencies/auth.py`, README «Безопасность dev-режима» и CLAUDE.md; тест событий входа дополнен запросом со схемой Basic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
e8d14edfbb
commit
7b28554817
@ -75,7 +75,8 @@
|
||||
- **Аутентификация** — `core/auth.py` (проверка JWT), `src/api/dependencies/auth.py` (`current_user`, `require(code)`);
|
||||
dev-эмитент (`POST /api/v1/dev/token`) существует только при `AUTH_DEV_ISSUER=true` **и** `DEBUG=true`.
|
||||
Аудит входа — `services/session_audit.py` (`app.state.session_audit`): `LOGIN` (dev-токен; первый запрос после старта),
|
||||
`LOGIN_FAILED` (`AuthenticationFailedError` 401/403 → обработчик пишет в отдельной транзакции `session_transaction`).
|
||||
`LOGIN_FAILED` (`AuthenticationFailedError` 401/403 → обработчик пишет в отдельной транзакции `session_transaction`);
|
||||
нет заголовка `Authorization` или схема не `Bearer`, 403 «недостаточно прав» — не события входа.
|
||||
- **Seed** — `python -m scripts.seed [--dev]` (`make seed|seed-dev`), идемпотентен (матрица `role_permission` — целиком только в
|
||||
пустую таблицу, в заполненную — лишь пары впервые созданных ролей/прав); `--dev` — только для разработки (CLI отказывает с кодом 2 при `DEBUG=false`).
|
||||
**Сверка ORM↔DBML** — `scripts/check_dbml_vs_orm.py` (`make dbml-check`).
|
||||
|
||||
@ -604,7 +604,8 @@ Dockerfile только для локального compose
|
||||
- `LOGIN_FAILED` — 401 (токен не принят: `user_id` пуст, в `description` причина, сам токен не пишется) и 403
|
||||
аутентификации (пользователь не зарегистрирован, отключён или неоднозначен: email в `description`, `user_id` —
|
||||
если пользователь найден). Запись делается в отдельной транзакции и остаётся, хотя транзакция запроса откатилась.
|
||||
- Не события входа: запрос без заголовка `Authorization` и 403 «недостаточно прав». Ошибка записи аудита входа
|
||||
- Не события входа: запрос без заголовка `Authorization` или со схемой не `Bearer` (401 «требуется заголовок…») и
|
||||
403 «недостаточно прав». Ошибка записи аудита входа
|
||||
ответ не меняет (только лог). Выход (`LOGOUT`) не пишется: сессий на сервере нет.
|
||||
- Поля, которые присылает клиент, обрезаются: `user_agent` — до 512 символов, `description` — до 1000.
|
||||
- **`audit_event.ip_address` достоверен только за доверенным прокси.** `X-Forwarded-For` принимается лишь от адресов
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
|
||||
Аудит входа (OQ-64, `services/session_audit.py`): отказ токена и 403 аутентификации — `AuthenticationFailedError`,
|
||||
обработчик пишет LOGIN_FAILED в отдельной транзакции; первый успешный `current_user` пользователя после старта процесса
|
||||
пишет LOGIN. Нет заголовка `Authorization` и 403 «недостаточно прав» — не события входа.
|
||||
пишет LOGIN. Нет заголовка `Authorization` или схема не `Bearer` (`parse_bearer`) и 403 «недостаточно прав» — не
|
||||
события входа.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
@ -6,7 +6,8 @@
|
||||
и сбрасывается при рестарте, у каждого экземпляра приложения — своё);
|
||||
- LOGIN_FAILED — 401 (токен не принят: `user_id` пуст, в `description` причина без самого токена) и 403 аутентификации
|
||||
(пользователь не найден, отключён, неоднозначен: email в `description`, `user_id` — если пользователь найден).
|
||||
Запрос без заголовка `Authorization` попыткой входа не считается; 403 «недостаточно прав» — авторизация, не вход.
|
||||
Запрос без заголовка `Authorization` или со схемой не `Bearer` попыткой входа не считается (токен не предъявлен);
|
||||
403 «недостаточно прав» — авторизация, не вход.
|
||||
|
||||
Раздел `SESSION`, `object_type = "app_user"`, `object_id = user_id`; IP и user-agent — из запроса. Поля, которые
|
||||
присылает клиент, обрезаются: `user_agent` — до 512 символов, `description` — до 1000.
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
|
||||
LOGIN — успешная выдача dev-токена и первый успешный запрос пользователя с токеном после старта процесса;
|
||||
LOGIN_FAILED — 401 (токен не принят) и 403 аутентификации (пользователь не найден, отключён, неоднозначен).
|
||||
Запрос без заголовка `Authorization` и 403 «недостаточно прав» входом не считаются. LOGIN_FAILED пишется в отдельной
|
||||
транзакции и переживает откат транзакции запроса — проверяется на `committed_app` (сессия запроса как в проде).
|
||||
Запрос без заголовка `Authorization` или со схемой не `Bearer` и 403 «недостаточно прав» входом не считаются.
|
||||
LOGIN_FAILED пишется в отдельной транзакции и переживает откат транзакции запроса — проверяется на `committed_app`
|
||||
(сессия запроса как в проде).
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
@ -170,12 +171,14 @@ async def test_disabled_user_records_login_failed_with_user_id(app: FastAPI, see
|
||||
async def test_missing_header_and_missing_permission_are_not_login_events(
|
||||
committed_app: FastAPI, committed_sessionmaker: async_sessionmaker[AsyncSession]
|
||||
) -> None:
|
||||
"""Без заголовка — не попытка входа; 403 «недостаточно прав» — авторизация, не аутентификация.
|
||||
"""Без заголовка или со схемой не Bearer — не попытка входа; 403 «недостаточно прав» — авторизация, не вход.
|
||||
|
||||
LOGIN первого запроса expert записан, хотя сам запрос (403 по праву) откатил свою транзакцию.
|
||||
"""
|
||||
async with _client(committed_app) as client:
|
||||
assert (await client.get("/api/v1/me")).status_code == HTTP_UNAUTHORIZED
|
||||
basic = {"Authorization": "Basic dXNlcjpwYXNz"}
|
||||
assert (await client.get("/api/v1/me", headers=basic)).status_code == HTTP_UNAUTHORIZED
|
||||
expert = _bearer(_token(EXPERT))
|
||||
assert (await client.get("/api/v1/admin/users", headers=expert)).status_code == HTTP_FORBIDDEN
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user