-- ============================================================= -- ARCHIVO: 99-rls-erp-modules.sql -- DESCRIPCION: Row Level Security (RLS) Policies para modulos ERP -- VERSION: 1.0.0 -- PROYECTO: ERP-Core V2 -- FECHA: 2026-01-24 -- CREADO POR: TASK-028 Remediacion RLS -- ============================================================= -- ===================================================== -- PRODUCTS MODULE - RLS Policies -- ===================================================== -- product_categories ALTER TABLE products.product_categories ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_product_categories ON products.product_categories; CREATE POLICY tenant_isolation_product_categories ON products.product_categories USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- products ALTER TABLE products.products ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_products ON products.products; CREATE POLICY tenant_isolation_products ON products.products USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- ===================================================== -- INVENTORY MODULE - RLS Policies -- ===================================================== -- warehouses ALTER TABLE inventory.warehouses ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_warehouses ON inventory.warehouses; CREATE POLICY tenant_isolation_warehouses ON inventory.warehouses USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- stock_levels ALTER TABLE inventory.stock_levels ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_stock_levels ON inventory.stock_levels; CREATE POLICY tenant_isolation_stock_levels ON inventory.stock_levels USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- stock_movements ALTER TABLE inventory.stock_movements ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_stock_movements ON inventory.stock_movements; CREATE POLICY tenant_isolation_stock_movements ON inventory.stock_movements USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- inventory_counts ALTER TABLE inventory.inventory_counts ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_inventory_counts ON inventory.inventory_counts; CREATE POLICY tenant_isolation_inventory_counts ON inventory.inventory_counts USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- transfer_orders ALTER TABLE inventory.transfer_orders ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_transfer_orders ON inventory.transfer_orders; CREATE POLICY tenant_isolation_transfer_orders ON inventory.transfer_orders USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- lots ALTER TABLE inventory.lots ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_lots ON inventory.lots; CREATE POLICY tenant_isolation_lots ON inventory.lots USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- pickings ALTER TABLE inventory.pickings ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_pickings ON inventory.pickings; CREATE POLICY tenant_isolation_pickings ON inventory.pickings USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- ===================================================== -- PARTNERS MODULE - RLS Policies -- ===================================================== -- partners ALTER TABLE partners.partners ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_partners ON partners.partners; CREATE POLICY tenant_isolation_partners ON partners.partners USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- partner_segments ALTER TABLE partners.partner_segments ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_partner_segments ON partners.partner_segments; CREATE POLICY tenant_isolation_partner_segments ON partners.partner_segments USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- ===================================================== -- SALES MODULE - RLS Policies -- ===================================================== -- quotations ALTER TABLE sales.quotations ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_quotations ON sales.quotations; CREATE POLICY tenant_isolation_quotations ON sales.quotations USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- sales_orders ALTER TABLE sales.sales_orders ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_sales_orders ON sales.sales_orders; CREATE POLICY tenant_isolation_sales_orders ON sales.sales_orders USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- ===================================================== -- PURCHASES MODULE - RLS Policies -- ===================================================== -- purchase_orders ALTER TABLE purchases.purchase_orders ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_purchase_orders ON purchases.purchase_orders; CREATE POLICY tenant_isolation_purchase_orders ON purchases.purchase_orders USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- purchase_receipts ALTER TABLE purchases.purchase_receipts ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_purchase_receipts ON purchases.purchase_receipts; CREATE POLICY tenant_isolation_purchase_receipts ON purchases.purchase_receipts USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- ===================================================== -- BILLING MODULE - RLS Policies -- ===================================================== -- invoices (billing schema) ALTER TABLE billing.invoices ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_billing_invoices ON billing.invoices; CREATE POLICY tenant_isolation_billing_invoices ON billing.invoices USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- payments (billing schema) ALTER TABLE billing.payments ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_billing_payments ON billing.payments; CREATE POLICY tenant_isolation_billing_payments ON billing.payments USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- ===================================================== -- FINANCIAL MODULE - RLS Policies -- ===================================================== -- accounts ALTER TABLE financial.accounts ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_accounts ON financial.accounts; CREATE POLICY tenant_isolation_accounts ON financial.accounts USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- account_mappings ALTER TABLE financial.account_mappings ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_account_mappings ON financial.account_mappings; CREATE POLICY tenant_isolation_account_mappings ON financial.account_mappings USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- fiscal_years ALTER TABLE financial.fiscal_years ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_fiscal_years ON financial.fiscal_years; CREATE POLICY tenant_isolation_fiscal_years ON financial.fiscal_years USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- fiscal_periods ALTER TABLE financial.fiscal_periods ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_fiscal_periods ON financial.fiscal_periods; CREATE POLICY tenant_isolation_fiscal_periods ON financial.fiscal_periods USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- journals ALTER TABLE financial.journals ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_journals ON financial.journals; CREATE POLICY tenant_isolation_journals ON financial.journals USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- journal_entries ALTER TABLE financial.journal_entries ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_journal_entries ON financial.journal_entries; CREATE POLICY tenant_isolation_journal_entries ON financial.journal_entries USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- journal_entry_lines ALTER TABLE financial.journal_entry_lines ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_journal_entry_lines ON financial.journal_entry_lines; CREATE POLICY tenant_isolation_journal_entry_lines ON financial.journal_entry_lines USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- invoices (financial schema) ALTER TABLE financial.invoices ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_financial_invoices ON financial.invoices; CREATE POLICY tenant_isolation_financial_invoices ON financial.invoices USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- invoice_lines (financial schema) ALTER TABLE financial.invoice_lines ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_invoice_lines ON financial.invoice_lines; CREATE POLICY tenant_isolation_invoice_lines ON financial.invoice_lines USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- payments (financial schema) ALTER TABLE financial.payments ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_financial_payments ON financial.payments; CREATE POLICY tenant_isolation_financial_payments ON financial.payments USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- taxes ALTER TABLE financial.taxes ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_taxes ON financial.taxes; CREATE POLICY tenant_isolation_taxes ON financial.taxes USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- tax_groups ALTER TABLE financial.tax_groups ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_tax_groups ON financial.tax_groups; CREATE POLICY tenant_isolation_tax_groups ON financial.tax_groups USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- bank_statements ALTER TABLE financial.bank_statements ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_bank_statements ON financial.bank_statements; CREATE POLICY tenant_isolation_bank_statements ON financial.bank_statements USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- bank_statement_lines ALTER TABLE financial.bank_statement_lines ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_bank_statement_lines ON financial.bank_statement_lines; CREATE POLICY tenant_isolation_bank_statement_lines ON financial.bank_statement_lines USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- bank_reconciliation_rules ALTER TABLE financial.bank_reconciliation_rules ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_bank_reconciliation_rules ON financial.bank_reconciliation_rules; CREATE POLICY tenant_isolation_bank_reconciliation_rules ON financial.bank_reconciliation_rules USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- ===================================================== -- PROJECTS MODULE - RLS Policies -- ===================================================== -- projects ALTER TABLE projects.projects ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_projects ON projects.projects; CREATE POLICY tenant_isolation_projects ON projects.projects USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- project_stages ALTER TABLE projects.project_stages ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_project_stages ON projects.project_stages; CREATE POLICY tenant_isolation_project_stages ON projects.project_stages USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- tasks ALTER TABLE projects.tasks ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_tasks ON projects.tasks; CREATE POLICY tenant_isolation_tasks ON projects.tasks USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- milestones ALTER TABLE projects.milestones ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_milestones ON projects.milestones; CREATE POLICY tenant_isolation_milestones ON projects.milestones USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- timesheets ALTER TABLE projects.timesheets ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_timesheets ON projects.timesheets; CREATE POLICY tenant_isolation_timesheets ON projects.timesheets USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- project_members ALTER TABLE projects.project_members ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_project_members ON projects.project_members; CREATE POLICY tenant_isolation_project_members ON projects.project_members USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- ===================================================== -- COMENTARIOS -- ===================================================== COMMENT ON POLICY tenant_isolation_products ON products.products IS 'Aislamiento multi-tenant para productos'; COMMENT ON POLICY tenant_isolation_warehouses ON inventory.warehouses IS 'Aislamiento multi-tenant para almacenes'; COMMENT ON POLICY tenant_isolation_partners ON partners.partners IS 'Aislamiento multi-tenant para socios comerciales'; COMMENT ON POLICY tenant_isolation_journal_entries ON financial.journal_entries IS 'Aislamiento multi-tenant para polizas contables'; COMMENT ON POLICY tenant_isolation_projects ON projects.projects IS 'Aislamiento multi-tenant para proyectos'; -- ===================================================== -- NOTAS DE IMPLEMENTACION -- ===================================================== -- -- 1. Las tablas hijas (ej: invoice_lines, order_items) heredan seguridad -- via CASCADE DELETE en sus FK a tablas padre -- -- 2. Tablas de catalogo sistema (account_types) no tienen tenant_id -- porque son compartidas entre todos los tenants -- -- 3. El setting 'app.current_tenant_id' debe configurarse en cada -- sesion de BD via: SET app.current_tenant_id = 'uuid-here'; -- -- 4. El backend debe configurar este setting antes de cada query -- Ver: backend/src/middleware/tenant.middleware.ts -- -- =====================================================