Срез 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
5.2 KiB
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