Andrey Epifancev db7df33517 chore(openspec): архив change slice-0-skeleton
Срез 0 реализован и влит (PR #3), change перенесён в openspec/changes/archive, спеки — в openspec/specs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-04 19:44:37 +04:00

5.2 KiB
Raw Blame History

authn-authz Specification

Purpose

Вход в ядро по JWT внешнего SSO (РАИСА): пользователь находится по email в app_user, его эффективные права — объединение прав его ролей из БД; любой защищённый маршрут объявляет требуемое право одной зависимостью. Для разработки — эмитент токенов за флагом.

Requirements

Requirement: Личность из JWT, пользователь из БД

Защищённый маршрут SHALL требовать заголовок Authorization: Bearer <jwt>. Ядро SHALL проверить подпись и срок токена настроенным проверяющим (dev HS256 при AUTH_DEV_ISSUER=true, иначе JWKS/публичный ключ по AUTH_*), извлечь email из claim AUTH_EMAIL_CLAIM (по умолчанию email) и найти app_user с этим email и is_active = true. Нет или невалидный токен → 401; пользователь не найден или неактивен → 403. Тело ошибки — {"detail": "<причина по-русски>"}.

Scenario: Валидный токен активного пользователя

  • WHEN запрос несёт корректный токен с email = ka_rf@armka.local, и такой активный пользователь есть
  • THEN маршрут выполняется от имени этого app_user

Scenario: Пользователя нет в системе

  • WHEN токен валиден, но app_user с таким email отсутствует
  • THEN ответ 403, detail сообщает, что пользователь не зарегистрирован в АРМ

Scenario: Истёкший или подделанный токен

  • WHEN срок токена истёк или подпись не проходит
  • THEN ответ 401, тело {"detail": …}

Requirement: Эффективные права и зависимость require

effective_permissions(user) SHALL быть объединением permission.code по всем ролям пользователя (user_role → role_permission → permission); код роли в проверках MUST NOT использоваться. Зависимость require("<PERMISSION_CODE>") SHALL возвращать 403, если кода нет в эффективных правах, и MUST NOT обращаться к БД повторно в рамках одного запроса.

Scenario: Право есть

  • WHEN пользователь с ролью KA_GO вызывает маршрут с require("KFO_VERIFY")
  • THEN маршрут выполняется

Scenario: Права нет

  • WHEN пользователь с ролью EXPERT вызывает маршрут с require("DOCUMENT_EDIT")
  • THEN ответ 403, detail называет недостающее право

Scenario: Две роли

  • WHEN пользователю назначены роли KA_RF и CURATOR_GO
  • THEN его эффективные права — объединение прав обеих ролей без дубликатов

Requirement: GET /api/v1/me

Маршрут SHALL возвращать текущего пользователя: id, email, full_name, regional_office {id, name, is_head_office}, business_segment, roles (список code), permissions (отсортированный список code). Это единственный маршрут Среза 0, доступный любому аутентифицированному пользователю без require.

Scenario: Ответ для dev-пользователя

  • WHEN ka_rf@armka.local (роль KA_RF) запрашивает /api/v1/me
  • THEN ответ 200 содержит roles = ["KA_RF"] и permissions, равные правам KA_RF из матрицы §10

Requirement: Dev-эмитент токенов только в режиме разработки

При AUTH_DEV_ISSUER=true и DEBUG=true ядро SHALL предоставлять POST /api/v1/dev/token с телом {"email"}, возвращающий HS256 JWT (срок 8 часов) для существующего активного пользователя. При любом другом сочетании флагов маршрут MUST NOT существовать (404), а dev-проверяющий MUST NOT принимать токены.

Scenario: Получение dev-токена

  • WHEN AUTH_DEV_ISSUER=true, DEBUG=true и запрошен токен для admin@armka.local
  • THEN ответ 200 {access_token, token_type: "bearer"}, токен принимается /api/v1/me

Scenario: Прод-конфигурация

  • WHEN DEBUG=false
  • THEN POST /api/v1/dev/token возвращает 404, токен, подписанный dev-секретом, отвергается с 401