GET /me дополнен role_details [{code, name}] (аддитивно), фронт показывает
названия ролей, при их отсутствии — коды. Требования внесены в спеки
authn-authz и consolidations-ui, change 184-user-role удалён.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7.9 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": "<причина по-русски>"}.
Отказы в аутентификации (401 при предъявленном токене, 403 «не найден / отключён / неоднозначен») и вход SHALL
фиксироваться в журнале по требованию «События входа» спеки audit-trail (LOGIN_FAILED / LOGIN).
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), role_details (те же активные роли списком {code, name}, name — role.name,
порядок как в roles), permissions (отсортированный список code). Это единственный маршрут
Среза 0, доступный любому аутентифицированному пользователю без require.
Scenario: Ответ для dev-пользователя
- WHEN
ka_rf@armka.local(рольKA_RF) запрашивает/api/v1/me - THEN ответ 200 содержит
roles = ["KA_RF"],role_detailsс одним элементом{code: "KA_RF", name: <непустое название>}и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
Requirement: Прод-конфигурация HTTP-поверхности
Документация API (/docs, /redoc, /openapi.json) SHALL существовать только при DEBUG=true; при DEBUG=false
эти пути MUST отвечать 404 (не схемой и не страницей SPA) (OQ-66). CORS при DEBUG=true SHALL разрешать любой источник
* без allow_credentials; при DEBUG=false — только источники из CORS_ORIGINS (JSON-список или строка через
запятую), пустой список SHALL запрещать кросс-доменные запросы, allow_credentials — только для явного списка без *
(OQ-65). Под /api/v1 SPA MUST NOT отвечать: неверный метод на существующий маршрут SHALL давать 405 с заголовком
Allow и detail по-русски, несуществующий путь (в том числе dev-маршрут при выключенных флагах) — 404 (OQ-61);
GET / SHALL отдавать index.html SPA без редиректа при любом ROOT_PATH (OQ-62).
Scenario: Документация в проде
- WHEN
DEBUG=falseи запрошен/openapi.json - THEN ответ 404
Scenario: Кросс-доменный запрос в проде без настроенных источников
- WHEN
DEBUG=false,CORS_ORIGINSпуст, браузер шлёт preflight сOrigin: https://other.example - THEN в ответе нет
Access-Control-Allow-Origin
Scenario: Неверный метод
- WHEN выполнен
POST /api/v1/me - THEN ответ 405, заголовок
Allow: GET
Scenario: Несуществующий путь API
- WHEN выполнен
GET /api/v1/no-such-route - THEN ответ 404, не
index.html
Scenario: Корень приложения
- WHEN при
ROOT_PATH=/выполненGET / - THEN ответ 200 с
index.html, без редиректа на//