feat(auth): отдельная БД auth — фундамент единого входа «Меры» и «Птицы» [PR-1/6] #2597
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#2597
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/auth-db-foundation"
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?
Первый PR эпика: вся авторизация переезжает на одну нейтральную форму входа для «Меры» (
/trade-in) и «Птицы» (раздел Site Finder), браузерный popup (Caddy basic_auth) убирается.Этот PR — только фундамент. Прод работает ровно как сейчас: в БД
gendesignничего не меняется, новая БД создаётся и наполняется логинами без паролей, читать её пока некому.Почему отдельная БД
Хранилище доступов не должно принадлежать продукту, из которого аккаунты выносятся. Сервер — существующий
gendesign-postgres, новый контейнер не заводим. Замерено на проде: оба бэкенда сидят в сетиgendesign_sharedи TCP-достают до него.Состав
data/sql/auth/001-003— схема (users,sessions), роль приложения, сид 13 логинов.password_hash = NULLу всех — plaintext и bcrypt-хеши в git запрещены (hard-tripwire), пароли проставляются отдельно на проде. Прецедент — tradein м.193.ops/db-bootstrap/create_auth_db.sql—CREATE DATABASEчерез\gexec. Не миграцией:CREATE DATABASEзапрещён в транзакции, а миграции обязаны быть транзакционными.ops/db-bootstrap/set_auth_app_password.sql— пароль роли из env, зеркалоset_tradein_fdw_password.sql(GUC +\o /dev/null+%L, строго через stdin::'pw'не интерполируется внутри$$...$$, на этом падал деплой 2026-05-24).Схема в подкаталоге
data/sql/auth/намеренно: основной цикл деплоя используетls -1 data/sql/*.sql, который в подкаталоги не рекурсирует → эти файлы физически не могут примениться в БДgendesign. Защита не на дисциплине, а на глобе. Триггерdata/sql/**подкаталог при этом покрывает.Права
Владелец БД — суперюзер, а не
auth_app(иначе гранты были бы декорацией).users— только SELECT+UPDATE: INSERT не выдан, потому что создания аккаунтов в этом PR нет, а снять грант, на который уже опирается прод-код, сложнее, чем выдать.REVOKE ALL ON DATABASE FROM PUBLICпродублирован в bootstrap и в миграции намеренно — bootstrap гоняется каждый деплой (переприменяемость), миграция однократно (самодостаточность); перекрёстные комментарии в обоих файлах.Проверено исполнением
Не по комментариям, а прогоном на
postgis/postgis:16-3.4— том же образе, что на проде:auth_appконнектится и не может INSERT/DELETE вusersиCREATE TABLE; посторонняя login-роль →permission denied for database "auth";_schema_migrations— деплой прервётся до подъёма контейнеров (блок стоит передcompose up -d);%Lи не печатается в stdout;GRANT ALL ... TO PUBLIC(эквивалент восстановления из дампа мимо миграций) → повторный bootstrap возвращаетpublic_connect = f.Тест
backend/tests/sql/test_auth_sql_migrations.py— имена, транзакционность, наличие wiring вdeploy.yml, и детектор паролей/хешей, покрывающий иdata/sql/auth, иops/db-bootstrap(единственное место в репе сALTER ROLE ... PASSWORD;detect-private-keybcrypt не ловит, а репо-wide grep невозможен —caddy/users.caddy.snippetлегально содержит хеши). Детектор проверен на живучесть: подложенный bcrypt-хеш роняет тест, при этом проверки имён/BEGIN-COMMIT на bootstrap-файлы не распространяются (они намеренно без номеров и без транзакций).Открытые развилки (зафиксированы комментариями в коде, решаются в PR-2/3)
tradein_users/tradein_sessions— после этого PR в проде два одинаковых по схеме хранилища сессий в разных БД. Пока никто не читает новое — безопасно; но до того, как в новую БД проставят первые хеши, надо решить: read-through или вывод из эксплуатации.expired!=disabled— сид кодируетuser2какis_active=false, но сегодня вroles.yamlу негоrole=expired, и семантика другая: такой юзер доходит до фронта и видит trial-экран. Булев флаг это состояние не выражает; при переключении trial-экран пропадёт молча, без падения тестов.Требует ручного шага на VPS
Переменную
AUTH_DB_PASSWORDзавести в runtime-env бэкенда. Пока пусто — шагALTER ROLEпропускается с warning'ом, деплой не падает.Дальше по эпику
PR-2 backend (сервис сессий + dual-mode guard в обоих бэкендах) → PR-3 нейтральная форма входа → PR-4 canary → PR-5 cutover (снятие popup) → PR-6 уборка. Инвариант: гейт снимается последним, только после того, как замена доказана в проде.