# 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: expired # пробный доступ закончился 2026-06-27 — см. NoAccessScreen variant="trial" admintest: admin # temp QA 2026-05-26 pilottest: pilot # temp QA 2026-05-26 analysttest: analyst # temp QA 2026-06-07 (#962)