Files
portal/db/00_create_roles.sql
T
Дмитрий 7899071f4e feat(db): crm_supplier_worker BYPASSRLS-роль для queue worker (Plan 2.6 #iv)
Закрывает CV.11 audit WARN minor #2 + #3 (LeadRouter + ResetDeliveredTodayCommand
под crm_app_user → RLS-policy tenant_isolation отвергает cross-tenant SELECT/UPDATE).

Архитектурное решение (Plan 2.6 brainstorm 10.05.2026 поздняя ночь, вариант C из 3
опций): новая PG-роль crm_supplier_worker с BYPASSRLS — privilege-boundary by design.
Queue worker = backend system process для cross-tenant операций (sharing-webhook
routing, global crons); web worker остаётся под crm_app_user (RLS-enforce).

WHERE(tenant_id=) фильтры в коде сохраняются как defense-in-depth.

Deploy:
  - Роль создаётся через db/00_create_roles.sql при первом deploy
    (psql -v crm_supplier_worker_password='<from-secrets>' ...).
  - GRANT'ы в db/02_grants.sql секция 5.
  - Queue worker .env: DB_USERNAME=crm_supplier_worker (отдельно от web .env).

Inline-warnings обновлены в LeadRouter.php + ResetDeliveredTodayCommand.php
(ссылка на crm_supplier_worker BYPASSRLS на prod, db/00_create_roles.sql).

00_create_roles.sql header bump v1.0 → v1.1 (4 → 5 ролей).

Без TDD-теста на роль (integration-тест требует CREATE ROLE в test DB +
смены connection — overhead не оправдан); smoke-grep verify пройден.

Pest 558/556 (+9 от Plan 2.5 baseline 549/547), Larastan + Pint + squawk green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 23:17:09 +03:00

74 lines
4.0 KiB
SQL

-- =============================================================================
-- 00_create_roles.sql — создание 5 ролей PostgreSQL для Лидерры
-- =============================================================================
-- Версия: 1.1 (10.05.2026, Plan 2.6 fix #iv — добавлена роль crm_supplier_worker
-- BYPASSRLS для backend queue worker, sharing-flow webhook routing
-- + global crons; brainstorm decision вариант C)
-- Версия 1.0 (08.05.2026, фаза 1 backend multi-tenant фундамент) — 4 роли.
-- Источник: schema.sql v8.5 §13 «Роли БД (CTO-5)» (закомментированные DDL)
-- =============================================================================
--
-- НАЗНАЧЕНИЕ: deployment-скрипт для production-окружения (Yandex Managed PG).
-- На **dev-машине** (этот скрипт) роли создавать НЕ требуется — schema.sql §13
-- разрешает использовать суперпользователя `postgres`. RLS-политики используют
-- `current_setting('app.current_tenant_id')::bigint` — работают для любого
-- пользователя БД (включая postgres).
--
-- ЗАПУСК (production):
-- psql -U postgres -h <host> -d postgres \
-- -v crm_app_password='<from-secrets>' \
-- -v crm_admin_password='<from-secrets>' \
-- -v crm_migrator_password='<from-secrets>' \
-- -v crm_audit_writer_password='<from-secrets>' \
-- -v crm_supplier_worker_password='<from-secrets>' \
-- -f db/00_create_roles.sql
--
-- ПОСЛЕ: запустить миграции под `crm_migrator` (BYPASSRLS, CREATEDB):
-- php artisan migrate --database=pgsql_migrator
--
-- ЗАТЕМ: запустить db/02_grants.sql под `crm_admin_user` (BYPASSRLS):
-- psql -U crm_admin_user -h <host> -d liderra -f db/02_grants.sql
--
-- ЗАВЕРШЕНИЕ: переключить .env приложения на `crm_app_user` (без BYPASSRLS).
-- =============================================================================
-- Роль приложения (tenant-уровень, RLS активна)
CREATE ROLE crm_app_user
LOGIN
PASSWORD :'crm_app_password';
-- Роль администрирования SaaS (BYPASSRLS для cross-tenant операций админки)
CREATE ROLE crm_admin_user
LOGIN
PASSWORD :'crm_admin_password'
BYPASSRLS;
-- Роль миграций (BYPASSRLS + CREATEDB для structural changes)
CREATE ROLE crm_migrator
LOGIN
PASSWORD :'crm_migrator_password'
BYPASSRLS
CREATEDB;
-- Роль append-only audit-журналирования (только INSERT, UPDATE/DELETE
-- блокируются триггерами audit_block_mutation, см. schema.sql §14)
CREATE ROLE crm_audit_writer
LOGIN
PASSWORD :'crm_audit_writer_password';
-- Роль backend queue worker для cross-tenant операций (Plan 2.6 fix #iv).
-- BYPASSRLS by design: queue worker = system process, обрабатывает sharing-flow
-- webhook routing (RouteSupplierLeadJob — N tenant'ов параллельно) и global
-- crons (projects:reset-delivered-today, supplier:check-webhook-secret).
-- Web worker остаётся под crm_app_user (RLS-enforce).
-- WHERE(tenant_id=) фильтры в коде сохраняются как defense-in-depth даже под
-- BYPASSRLS-ролью. См. RouteSupplierLeadJob::createDealCopyForProject lines 161-164
-- (lockForUpdate Tenant + явный tenant_id фильтр перед каждым INSERT/UPDATE).
-- Spec: docs/superpowers/specs/2026-05-10-supplier-integration-design.md §6.
-- Brainstorm decision: вариант C из 3 опций (A=elevated DB-connection /
-- B=RLS WITH-CHECK exception / C=BYPASSRLS-роль). См. memory project_supplier_integration.md.
CREATE ROLE crm_supplier_worker
LOGIN
PASSWORD :'crm_supplier_worker_password'
BYPASSRLS;