gendesign/auth/roles.yaml
bot-backend 3e4c93c91e
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 56s
fix(tradein/auth): restore praktika access + data-driven unlimited quota grants
praktika (ГК «Практика») role expired -> pilot: доступ восстановлен по решению
владельца продукта. Добавлен expiredtest QA-аккаунт, чтобы test_rbac.py сохранил
regression-покрытие role=expired (praktika больше не годится как fixture).

account_quota.is_unlimited() больше не хардкодит username == 'kopylov': грант
безлимитных оценок теперь читается из account_quota_overrides.unlimited (migration
191) — единый источник правды для всех non-admin unlimited-аккаунтов. kopylov
перенесён в данные той же миграцией (поведение не меняется, деплой применяет SQL
раньше кода); praktika получил такой же грант.

unlimited выбран как явная boolean-колонка, а не sentinel monthly_limit (-1/0),
т.к. 0 неоднозначен ("ноль оценок" vs "без лимита").
2026-07-27 01:49:01 +03:00

91 lines
4.5 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RBAC roles + user → role mapping.
#
# Single source of truth for both main backend (backend/app/core/auth.py) and
# tradein backend (tradein-mvp/backend/app/core/auth.py). Mounted into both
# containers at /app/auth/roles.yaml via docker-compose bind-mount.
#
# Glob semantics (fnmatch-based, see app.core.auth.is_path_allowed):
# - "/**" → matches any path (admin scope).
# - "/foo/**" → matches /foo, /foo/, /foo/bar, /foo/bar/baz/...
# - "/foo" → matches exactly /foo.
#
# A path is allowed iff (1) it matches one of `paths` AND (2) it does NOT match
# any pattern in `deny`. `deny` overrides `paths` (deny-by-default within
# match scope).
#
# Username list MUST match exactly the entries in caddy/users.caddy.snippet
# — Caddy basic_auth sets X-Authenticated-User from {http.auth.user.id}, and
# unknown users hit /me with 403 ("user not in roles config").
#
# Last updated: 2026-05-26 (pilot scope narrowed to /trade-in/** only — decision
# 2026-05-26: pilot аккаунты пилот-программы видят ТОЛЬКО раздел Trade-In;
# Analytics / Site Finder / Concept / landing — admin-only).
roles:
admin:
paths:
- "/**"
deny: []
pilot:
# Pilot имеет доступ ТОЛЬКО к разделу Trade-In (оценка вторички).
# Landing (/), analytics, site-finder, concept, остальной /api/v1/* — закрыты.
# Backend middleware всё равно пропускает known users на все non-admin paths
# (path-level enforcement делает frontend RouteGuard через `allowed_paths`),
# но для UX/security принципов keep deny список explicit.
paths:
- "/trade-in/**"
- "/trade-in/api/v1/**"
deny:
- "/admin/**"
- "/api/v1/admin/**"
- "/trade-in/api/v1/admin/**"
analyst:
# #962 (EPIC18, ТЗ §19): analyst видит ВСЁ (deals, insights, exports,
# site-finder, analytics, concept) КРОМЕ admin/data-management.
# paths "/**" = доступ ко всему; deny = админ-управление (scraper-триггеры,
# sync-таски, data-management, user/role management).
# Backend hard-gate: app/main.py rbac_guard уже блокирует /api/v1/admin/*
# для любого role != "admin" → analyst авто-403 на admin-API без доп. кода.
# deny ниже драйвит фронтовый RouteGuard (deny_paths из /me) для UI-gating
# /admin/** страниц.
paths:
- "/**"
deny:
- "/admin/**"
- "/api/v1/admin/**"
- "/trade-in/api/v1/admin/**"
expired:
# Пробный доступ закончился — нет доступа ни к чему. Аккаунт остаётся в
# caddy/users.caddy.snippet (basic_auth), чтобы дойти до фронта и увидеть
# сообщение; /me отдаёт role=expired → RouteGuard рендерит trial-экран.
paths: []
deny:
- "/**"
# NB: реальные analyst-логины ДОЛЖНЫ быть добавлены и в
# caddy/users.caddy.snippet (Caddy basic_auth) силами devops — иначе Caddy не
# пропустит юзера и не выставит X-Authenticated-User. Здесь только role-mapping;
# Caddy НЕ редактируем из этого таска.
users:
admin: admin
kopylov: pilot
user1: pilot
user2: pilot # «Брусника» — доступ восстановлен 2026-07-13 (снят trial-expire от 2026-07-09)
user3: pilot
user4: pilot
user5: pilot
user6: pilot
user7: pilot
user8: pilot
user9: pilot
user10: pilot
praktika: pilot # ГК «Практика» — доступ восстановлен 2026-07-27 (решение владельца
# продукта; ранее expired с 2026-06-27). Безлимитная квота оценок
# выдана через account_quota_overrides.unlimited (migration 191),
# не через код — см. app.services.account_quota.is_unlimited.
admintest: admin # temp QA 2026-05-26
pilottest: pilot # temp QA 2026-05-26
analysttest: analyst # temp QA 2026-06-07 (#962)
expiredtest: expired # temp QA 2026-07-27 — role=expired regression coverage для
# test_rbac.py (praktika перестал быть expired-фикстурой
# после восстановления доступа)