-- ============================================================================= -- 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 -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; REVOKE DELETE ON tenant_subscriptions FROM crm_admin_user; -- ============================================================================= -- 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; ALTER TABLE webhook_log OWNER TO crm_migrator; 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; GRANT crm_migrator TO crm_supplier_worker WITH INHERIT TRUE;