1b5316b2c8
- anon 1.3.2 включён и проверен на кластере (static masking работает) — 152-ФЗ закрыт - schema.sql v8.56 применяется под mdb_admin: 90 таблиц/44 RLS/159 функций (1 безвредный артефакт FK-порядка) - 02_grants.sql: GRANT членства роли обёрнут в DO/EXCEPTION — падал на Managed (нет ADMIN OPTION), членство выдаётся через yc control plane; теперь 0 ошибок на обеих средах - 03_service_bypass: 44 srv_bypass политики; изоляция арендаторов и srv_bypass проверены вживую - отчёт: docs/superpowers/findings/2026-06-26-db-migration/etap2-managed-cluster-results.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
186 lines
12 KiB
SQL
186 lines
12 KiB
SQL
-- =============================================================================
|
||
-- 02_grants.sql — GRANT/REVOKE для 4 ролей Лидерры
|
||
-- =============================================================================
|
||
-- Версия: 1.0 (08.05.2026, фаза 1 backend multi-tenant фундамент)
|
||
-- Источник: schema.sql v8.5 §13 «Роли БД (CTO-5)» + §14 «АУДИТ APPEND-ONLY»
|
||
-- =============================================================================
|
||
--
|
||
-- НАЗНАЧЕНИЕ: deployment-скрипт для production. Запускается ПОСЛЕ:
|
||
-- 1. db/00_create_roles.sql — роли созданы
|
||
-- 2. db/schema.sql — таблицы/индексы/RLS-политики созданы (через `php artisan migrate`)
|
||
--
|
||
-- ЗАПУСК:
|
||
-- psql -U postgres -h <host> -d liderra -f db/02_grants.sql
|
||
-- (требуется superuser, т.к. GRANT/REVOKE на чужие объекты)
|
||
--
|
||
-- ВНИМАНИЕ: на **dev-машине** этот файл НЕ запускается — schema.sql §13 разрешает
|
||
-- использовать суперпользователя `postgres` для разработки. RLS-политики работают
|
||
-- через `current_setting('app.current_tenant_id')::bigint` — независимо от роли.
|
||
-- =============================================================================
|
||
|
||
-- =============================================================================
|
||
-- 1. crm_app_user — tenant-уровень приложения (RLS активна)
|
||
-- =============================================================================
|
||
|
||
GRANT USAGE ON SCHEMA public TO crm_app_user;
|
||
|
||
-- SELECT/INSERT/UPDATE/DELETE на ВСЕ таблицы (RLS-политики ограничат доступ
|
||
-- к чужим tenant-данным; saas-таблицы дополнительно закрыты REVOKE ниже)
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO crm_app_user;
|
||
|
||
-- USAGE на sequences (для BIGSERIAL nextval)
|
||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO crm_app_user;
|
||
|
||
-- Default privileges для будущих таблиц/sequences (после миграций)
|
||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO crm_app_user;
|
||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||
GRANT USAGE, SELECT ON SEQUENCES TO crm_app_user;
|
||
|
||
-- v8.5 (OPEN-И-14) defense-in-depth: REVOKE ALL на 6 saas-таблицах.
|
||
-- К этим таблицам tenant-приложение НЕ должно иметь доступа даже теоретически
|
||
-- (RLS + REVOKE = 2 барьера).
|
||
REVOKE ALL ON saas_admin_users FROM crm_app_user;
|
||
REVOKE ALL ON saas_admin_sessions FROM crm_app_user;
|
||
REVOKE ALL ON saas_admin_audit_log FROM crm_app_user;
|
||
REVOKE ALL ON incidents_log FROM crm_app_user;
|
||
REVOKE ALL ON pd_subject_requests FROM crm_app_user;
|
||
REVOKE ALL ON impersonation_tokens FROM crm_app_user;
|
||
|
||
-- =============================================================================
|
||
-- 2. crm_admin_user — администрирование SaaS (BYPASSRLS)
|
||
-- =============================================================================
|
||
|
||
GRANT USAGE ON SCHEMA public TO crm_admin_user;
|
||
|
||
-- ВСЕ права crm_app_user + доступ к saas_admin_*, supplier_*, system_settings,
|
||
-- tariff_plans, legal_entities, payment_gateways
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO crm_admin_user;
|
||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO crm_admin_user;
|
||
|
||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO crm_admin_user;
|
||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||
GRANT USAGE, SELECT ON SEQUENCES TO crm_admin_user;
|
||
|
||
-- Запрет DELETE на финансовых таблицах (только soft markers через UPDATE):
|
||
REVOKE DELETE ON balance_transactions FROM crm_admin_user;
|
||
REVOKE DELETE ON supplier_invoices FROM crm_admin_user;
|
||
REVOKE DELETE ON supplier_lead_costs FROM crm_admin_user;
|
||
-- (tenant_subscriptions удалена из продукта — REVOKE убран, шов E 26.06.2026)
|
||
|
||
-- =============================================================================
|
||
-- 3. crm_audit_writer — append-only audit (INSERT-only)
|
||
-- =============================================================================
|
||
-- UPDATE/DELETE дополнительно блокируются триггерами audit_block_mutation
|
||
-- (schema.sql §14). Двухслойная защита от tampering.
|
||
|
||
GRANT USAGE ON SCHEMA public TO crm_audit_writer;
|
||
|
||
GRANT INSERT ON auth_log TO crm_audit_writer;
|
||
GRANT INSERT ON activity_log TO crm_audit_writer;
|
||
GRANT INSERT ON pd_processing_log TO crm_audit_writer;
|
||
GRANT INSERT ON saas_admin_audit_log TO crm_audit_writer;
|
||
GRANT INSERT ON balance_transactions TO crm_audit_writer;
|
||
|
||
GRANT USAGE ON SEQUENCE auth_log_id_seq TO crm_audit_writer;
|
||
GRANT USAGE ON SEQUENCE activity_log_id_seq TO crm_audit_writer;
|
||
GRANT USAGE ON SEQUENCE pd_processing_log_id_seq TO crm_audit_writer;
|
||
GRANT USAGE ON SEQUENCE saas_admin_audit_log_id_seq TO crm_audit_writer;
|
||
GRANT USAGE ON SEQUENCE balance_transactions_id_seq TO crm_audit_writer;
|
||
|
||
-- crm_audit_writer НЕ имеет SELECT/UPDATE/DELETE/TRUNCATE — это специально.
|
||
-- Явно отзываем дефолтные привилегии PUBLIC, если такие есть:
|
||
REVOKE SELECT, UPDATE, DELETE, TRUNCATE ON auth_log FROM crm_audit_writer;
|
||
REVOKE SELECT, UPDATE, DELETE, TRUNCATE ON activity_log FROM crm_audit_writer;
|
||
REVOKE SELECT, UPDATE, DELETE, TRUNCATE ON pd_processing_log FROM crm_audit_writer;
|
||
REVOKE SELECT, UPDATE, DELETE, TRUNCATE ON saas_admin_audit_log FROM crm_audit_writer;
|
||
REVOKE SELECT, UPDATE, DELETE, TRUNCATE ON balance_transactions FROM crm_audit_writer;
|
||
|
||
-- =============================================================================
|
||
-- 4. crm_migrator — runtime миграций (BYPASSRLS, CREATEDB)
|
||
-- =============================================================================
|
||
-- Для запуска `php artisan migrate` в production без ослабления RLS.
|
||
|
||
GRANT USAGE, CREATE ON SCHEMA public TO crm_migrator;
|
||
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO crm_migrator;
|
||
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO crm_migrator;
|
||
|
||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||
GRANT ALL PRIVILEGES ON TABLES TO crm_migrator;
|
||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||
GRANT ALL PRIVILEGES ON SEQUENCES TO crm_migrator;
|
||
|
||
-- =============================================================================
|
||
-- 5. crm_supplier_worker — backend queue worker (BYPASSRLS) — Plan 2.6 fix #iv
|
||
-- =============================================================================
|
||
-- Для запуска `php artisan queue:work` под отдельным .env (отдельным от web).
|
||
-- Cross-tenant операции: sharing-flow webhook routing (RouteSupplierLeadJob),
|
||
-- global crons (projects:reset-delivered-today, supplier:check-webhook-secret).
|
||
-- Web worker остаётся под crm_app_user (RLS-enforce). WHERE(tenant_id=) фильтры
|
||
-- в коде сохраняются как defense-in-depth даже под BYPASSRLS-ролью.
|
||
--
|
||
-- Brainstorm decision (10.05.2026 поздняя ночь): вариант C из 3 опций
|
||
-- (A=elevated DB-connection / B=RLS WITH-CHECK exception / C=BYPASSRLS-роль).
|
||
|
||
GRANT USAGE ON SCHEMA public TO crm_supplier_worker;
|
||
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO crm_supplier_worker;
|
||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO crm_supplier_worker;
|
||
|
||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO crm_supplier_worker;
|
||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||
GRANT USAGE, SELECT ON SEQUENCES TO crm_supplier_worker;
|
||
|
||
-- =============================================================================
|
||
-- v8.19 (Plan 4): supplier_csv_reconcile_log — SaaS-уровневый журнал CSV recon.
|
||
-- Используется CsvReconcileJob под crm_supplier_worker (BYPASSRLS).
|
||
-- =============================================================================
|
||
|
||
GRANT SELECT, INSERT, UPDATE ON TABLE supplier_csv_reconcile_log TO crm_supplier_worker;
|
||
GRANT USAGE, SELECT ON SEQUENCE supplier_csv_reconcile_log_id_seq TO crm_supplier_worker;
|
||
|
||
-- =============================================================================
|
||
-- 23.05.2026 (post hole #2): partition-maintenance privilege model
|
||
-- =============================================================================
|
||
-- `partitions:create-months` / `partitions:drop-expired` теперь идут через
|
||
-- `pgsql_supplier` connection (MonthlyPartitionManager::DDL_CONNECTION). Чтобы
|
||
-- `crm_supplier_worker` мог CREATE/DROP партиции партиционированных родителей,
|
||
-- нужны два условия:
|
||
-- 1. Единый владелец всех 9 партиционированных родителей — `crm_migrator`.
|
||
-- По умолчанию `schema.sql` создаёт audit-таблицы под `postgres` (load
|
||
-- выполняется суперпользователем при `migrate:fresh`); явный ALTER OWNER
|
||
-- ниже выравнивает прод (где это уже сделано вживую 23.05.2026) и
|
||
-- гарантирует, что fresh-deploy не разойдётся.
|
||
-- 2. `crm_supplier_worker` — член `crm_migrator` (INHERIT TRUE), чтобы
|
||
-- ownership-операции (CREATE TABLE ... PARTITION OF, DROP TABLE) проходили
|
||
-- проверку владельца. Web-роль `crm_app_user` остаётся least-privilege —
|
||
-- она НЕ получает crm_migrator-членство и НЕ может делать partition DDL.
|
||
--
|
||
-- Идемпотентно — повторный запуск 02_grants.sql после первого применения
|
||
-- безопасен.
|
||
-- =============================================================================
|
||
|
||
ALTER TABLE auth_log OWNER TO crm_migrator;
|
||
ALTER TABLE activity_log OWNER TO crm_migrator;
|
||
ALTER TABLE tenant_operations_log OWNER TO crm_migrator;
|
||
-- (webhook_log удалена в v8.35 legacy-webhook removal — ALTER OWNER убран, шов E 26.06.2026)
|
||
ALTER TABLE balance_transactions OWNER TO crm_migrator;
|
||
ALTER TABLE pd_processing_log OWNER TO crm_migrator;
|
||
ALTER TABLE saas_admin_audit_log OWNER TO crm_migrator;
|
||
|
||
-- NB (Путь А, Managed PG, проверено вживую 26.06.2026): на УПРАВЛЯЕМОЙ базе Yandex
|
||
-- этот GRANT членства в роли падает с 'permission denied to grant role "crm_migrator"'
|
||
-- — даже владелец-mdb_admin не имеет ADMIN OPTION на кастомную роль. На Managed членство
|
||
-- выдаётся через control plane (выполняется привилегированно самим Yandex):
|
||
-- yc managed-postgresql user update crm_supplier_worker --cluster-id <id> --grants crm_migrator
|
||
-- DO-обёртка делает скрипт безопасным на ОБЕИХ средах: self-managed prod (postgres superuser)
|
||
-- проходит штатно; на Managed — мягко пропускается с NOTICE (членство уже выдано через yc).
|
||
DO $$
|
||
BEGIN
|
||
EXECUTE 'GRANT crm_migrator TO crm_supplier_worker WITH INHERIT TRUE';
|
||
EXCEPTION WHEN insufficient_privilege THEN
|
||
RAISE NOTICE 'GRANT crm_migrator TO crm_supplier_worker пропущен (Managed PG: выдать через yc ... --grants crm_migrator)';
|
||
END$$;
|