fix(tradein/rbac): скрыть «Доля в продаже» и «Кэш» от клиентских аккаунтов #2587
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#2587
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-hide-saleshare-cache-from-clients"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Проблема
Аккаунт
praktika(DB-рольmanager) видел в топбаре на/trade-in/teamпункты «Доля в продаже» и «Кэш». Это внутренние инструменты — аналитика рынка и состояние кэшей/скраперов, — клиентские аккаунты их видеть не должны.Замерено на проде до правки (
/meдля praktika):scopePath этих пунктов (
/trade-in/sale-share,/trade-in/cache) матчится/trade-in/**и проходит фильтр. Скрыты только те пункты, что намеренно замаплены на admin-путь (скрапперы/аудит/активность).Почему правка в backend, а не в Topbar
Один и тот же матчер обслуживает три места: пункт меню (Topbar по scopePath из
/me), сама страница (RouteGuard) и серверные ручки (rbac_guard). Правка только фронта спрятала бы пункт, оставив прямой URL и API открытыми — ровно тот caveat, что уже задокументирован для скрапперов. Поэтому deny заводится один раз в роли и закрывает всё сразу.Закрыто для
employee/manager(DB_ROLE_PATHS) и для legacypilot(auth/roles.yaml):У cache-stats глоб, а не точный путь — намеренно: точный паттерн это строгое равенство, и его обходит обычный трейлинг-слэш (
…/cache-stats/→allowed=True). Спасал бы только Starletteredirect_slashes, т.е. защита висела бы на роутере, а не на RBAC. После правки замерено:/,//,%2f,./,../— везде 403, утечек нет.Основной продукт не задет
app/api/v1/buildings.pyобслуживает только sale-share (3 ручки),BuildingListingsDrawerимпортируется единственной страницей/sale-share.useEstimatePlacementHistory/useSalesVsListings), а не buildings-API.adminиanalystсознательно вне deny — запиннено тестом, иначе «синхронизация» deny-списков между ролями закрыла бы их молча.Заодно: починен красный тест на main
backend/tests/test_rbac.py::test_get_role_known_usersпадал на main — ждалpilotу всехuser1..user10, ноuser2(«Брусника») сталexpired2026-07-30. CI это пропустил:auth/roles.yamlне входит в paths-filterbackend/**, поэтому сьют Site Finder не бежал. Тест приведён к реальности с явной константой и объяснением, что он ловит именно рассинхрон roles.yaml.Проверено
pytesttradein (rbac + auth_session + auth_api + team_api + support) → 153 passedpytestsite-finder (backend/tests/test_rbac.py) → 24 passed (было 23 passed + 1 failed)rbac_guardпо session-ветке (именно ею ходит praktika — раньше через guard тестировалась только легаси trusted-header ветка), пинdeny_pathsв выдаче/me, границы глоба (соседиcache-statistics/cache-stats/sale-share-reportостаются доступны), regression-guard на core-путиruff check+formatчисто по изменённым файлам (3 RUF059 вtest_estimator_pure_units.py— pre-existing)tsc --noEmit+next build— зелёныеТребует внимания
/sale-share— отдельный продукт «Поиск домов» с vanity-URLgendsgn.ru/sale-shareи своим<title>. После этого PR он admin-only, и механизма выдать его конкретному клиенту нет — скоуп только ролевой. Если раздел остаётся продаваемым, нужен per-account carve-out. Отметил это прямо в докстринге страницы.auth/roles.yamlне входит ни в один CI paths-filter — правка этого файла не запускает ни backend-сьют, ни tradein-сьют. Именно так на main уехал красный тест. Фикс (добавитьauth/**в фильтры) — изменение правил пайплайна, не делаю автономно; выношу отдельно.Caddyfileпро/sale-share(«Доступ: pilot + admin») стал неточным. Не трогал: правка Caddyfile триггерит полный деплой Site Finder с пересозданием caddy — несоразмерно ради комментария, лучше приложить к следующей правке того файла.Аккаунт praktika (DB-роль manager) видел оба пункта в топбаре на /trade-in/team. Это внутренние инструменты — аналитика рынка и состояние кэшей/скраперов, — клиентские аккаунты их видеть не должны (решение владельца продукта). Гейт один — deny-список роли, потому что все три места сверяются с ним через общий матчер: пункт меню (Topbar по scopePath из /me), страница (RouteGuard) и серверные ручки (rbac_guard). Правка только фронта спрятала бы пункт, оставив прямой URL и API открытыми. Закрыто для employee/manager (DB_ROLE_PATHS) и для legacy pilot (roles.yaml): /trade-in/sale-share/** /trade-in/cache/** /trade-in/api/v1/buildings/** /trade-in/api/v1/trade-in/cache-stats/** У cache-stats ГЛОБ, а не точный путь: точный паттерн — строгое равенство, его обходит трейлинг-слэш ('…/cache-stats/' → allowed=True), и защита держалась бы на Starlette redirect_slashes, а не на RBAC. Замерено после правки: все варианты (слэш, %2f, ./, ../) дают 403, утечек нет. Основной продукт не задет: buildings.py обслуживает ТОЛЬКО sale-share, секция «Продажи в доме» на экране оценки питается estimate-хендлерами. admin и analyst сознательно вне deny — запиннено тестом, иначе «синхронизация» списков закрыла бы их молча. Заодно починен КРАСНЫЙ pre-existing тест главного бэкенда: backend/tests/test_rbac.py::test_get_role_known_users ждал pilot у всех user1..user10, но user2 («Брусника») стал expired 2026-07-30. CI это пропустил — auth/roles.yaml не входит в paths-filter backend/**, из-за чего сьют не бежал. Тесты: 153 passed (tradein) + 24 passed (site-finder, было 23+1 failed). Новые — e2e через реальный rbac_guard по session-ветке (именно ею ходит praktika), пин deny_paths в выдаче /me, границы глоба и regression-guard'ы. Проверены снятием deny: 7 тестов краснеют, т.е. не тавтологии.