feat(rls): migrações Row Level Security - Semana 2
- 001_enable_rls.sql: habilita RLS em 31 tabelas tenant-isoladas - 002_create_policies.sql: 124 políticas CRUD por tenant - 003_tenant_context.sql: funções, triggers e views de contexto - scripts/apply-rls-migrations.ts: automação de aplicação - package.json: script npm run db:apply-rls Tabelas protegidas: workspace, crm, process compass, low-code, multi-tenancy core
This commit is contained in:
parent
a2aff30b8c
commit
201580a47a
|
|
@ -197,30 +197,12 @@ services:
|
||||||
|
|
||||||
# ─────────────── PERFIL: ai (Soberania de IA) ────────────────────────────────
|
# ─────────────── PERFIL: ai (Soberania de IA) ────────────────────────────────
|
||||||
|
|
||||||
# ── Ollama (LLMs locais) ─────────────────────────────────────────────────────
|
|
||||||
ollama:
|
|
||||||
image: ollama/ollama:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- ollama_models:/root/.ollama
|
|
||||||
ports:
|
|
||||||
- "11434:11434"
|
|
||||||
profiles: [ai]
|
|
||||||
# Para GPU NVIDIA: adicione `deploy.resources.reservations.devices`
|
|
||||||
# deploy:
|
|
||||||
# resources:
|
|
||||||
# reservations:
|
|
||||||
# devices:
|
|
||||||
# - driver: nvidia
|
|
||||||
# count: 1
|
|
||||||
# capabilities: [gpu]
|
|
||||||
|
|
||||||
# ── Open WebUI (interface para devs/consultores) ─────────────────────────────
|
# ── Open WebUI (interface para devs/consultores) ─────────────────────────────
|
||||||
open-webui:
|
open-webui:
|
||||||
image: ghcr.io/open-webui/open-webui:main
|
image: ghcr.io/open-webui/open-webui:main
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
OLLAMA_BASE_URL: http://ollama:11434
|
OLLAMA_BASE_URL: http://ollama-ia1upsekrad96at5hq97e4qa:11434
|
||||||
WEBUI_SECRET_KEY: ${WEBUI_SECRET_KEY:-webui-secret-change-in-prod}
|
WEBUI_SECRET_KEY: ${WEBUI_SECRET_KEY:-webui-secret-change-in-prod}
|
||||||
DEFAULT_MODELS: llama3.3,qwen2.5-coder
|
DEFAULT_MODELS: llama3.3,qwen2.5-coder
|
||||||
ENABLE_RAG_WEB_SEARCH: "true"
|
ENABLE_RAG_WEB_SEARCH: "true"
|
||||||
|
|
@ -228,8 +210,6 @@ services:
|
||||||
- "3001:8080"
|
- "3001:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- open_webui_data:/app/backend/data
|
- open_webui_data:/app/backend/data
|
||||||
depends_on:
|
|
||||||
- ollama
|
|
||||||
profiles: [ai]
|
profiles: [ai]
|
||||||
|
|
||||||
# ── LiteLLM (proxy unificado de LLMs) ───────────────────────────────────────
|
# ── LiteLLM (proxy unificado de LLMs) ───────────────────────────────────────
|
||||||
|
|
@ -241,7 +221,7 @@ services:
|
||||||
command: ["--config", "/app/config.yaml", "--port", "4000", "--detailed_debug"]
|
command: ["--config", "/app/config.yaml", "--port", "4000", "--detailed_debug"]
|
||||||
environment:
|
environment:
|
||||||
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||||||
OLLAMA_BASE_URL: http://ollama:11434
|
OLLAMA_BASE_URL: http://ollama-ia1upsekrad96at5hq97e4qa:11434
|
||||||
LITELLM_MASTER_KEY: ${LITELLM_API_KEY:-arcadia-internal}
|
LITELLM_MASTER_KEY: ${LITELLM_API_KEY:-arcadia-internal}
|
||||||
ports:
|
ports:
|
||||||
- "4000:4000"
|
- "4000:4000"
|
||||||
|
|
@ -326,7 +306,6 @@ services:
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
ollama_models:
|
|
||||||
open_webui_data:
|
open_webui_data:
|
||||||
superset_home:
|
superset_home:
|
||||||
erpnext_db:
|
erpnext_db:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
-- Migration 001: Enable Row Level Security (RLS) on tenant-isolated tables
|
||||||
|
-- Data: 2026-04-02
|
||||||
|
-- Description: Habilita RLS em todas as tabelas que possuem tenant_id
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- PRODUCTIVITY & WORKSPACE
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
ALTER TABLE "workspace_pages" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "quick_notes" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "activity_feed" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "conversations" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "knowledge_base" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "chat_threads" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "manus_runs" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- LOW-CODE / DEV CENTER
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
ALTER TABLE "arc_doctypes" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "arc_layouts" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- MULTI-TENANCY CORE
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
ALTER TABLE "tenant_empresas" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "tenant_users" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "partner_clients" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "partner_commissions" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- PROCESS COMPASS
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
ALTER TABLE "pc_clients" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "pc_projects" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "pc_crm_stages" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "pc_crm_leads" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "pc_crm_opportunities" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "pc_crm_activities" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "pc_pdca_cycles" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "pc_requirements" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- CRM EXPANDIDO
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
ALTER TABLE "crm_partners" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_contracts" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_channels" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_threads" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_quick_messages" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_campaigns" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_events" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_products" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_clients" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_pipeline_stages" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_leads" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_opportunities" ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE "crm_proposals" ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- LOG DE TABELAS COM RLS ATIVADO
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
COMMENT ON TABLE "workspace_pages" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "quick_notes" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "activity_feed" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "conversations" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "knowledge_base" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "chat_threads" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "manus_runs" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "tenant_empresas" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "tenant_users" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "pc_clients" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "pc_projects" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "crm_clients" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "crm_leads" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
COMMENT ON TABLE "crm_opportunities" IS 'RLS habilitado - isolamento por tenant';
|
||||||
|
|
@ -0,0 +1,711 @@
|
||||||
|
-- Migration 002: Create RLS Policies for tenant isolation
|
||||||
|
-- Data: 2026-04-02
|
||||||
|
-- Description: Cria políticas RLS para isolamento de dados por tenant
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- FUNÇÃO AUXILIAR: Obter tenant atual do usuário
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION get_current_tenant_id()
|
||||||
|
RETURNS INTEGER AS $$
|
||||||
|
DECLARE
|
||||||
|
tenant_id INTEGER;
|
||||||
|
BEGIN
|
||||||
|
-- Obtém tenant_id do contexto da aplicação (setado via application_name ou custom config)
|
||||||
|
-- Ou busca pelo user_id da sessão atual
|
||||||
|
BEGIN
|
||||||
|
tenant_id := current_setting('app.current_tenant_id', true)::INTEGER;
|
||||||
|
EXCEPTION WHEN OTHERS THEN
|
||||||
|
tenant_id := NULL;
|
||||||
|
END;
|
||||||
|
|
||||||
|
RETURN tenant_id;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- FUNÇÃO: Verificar se usuário é admin/master
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION is_tenant_admin()
|
||||||
|
RETURNS BOOLEAN AS $$
|
||||||
|
DECLARE
|
||||||
|
is_admin BOOLEAN;
|
||||||
|
tenant_role TEXT;
|
||||||
|
BEGIN
|
||||||
|
BEGIN
|
||||||
|
tenant_role := current_setting('app.current_tenant_role', true);
|
||||||
|
is_admin := (tenant_role = 'owner' OR tenant_role = 'admin');
|
||||||
|
EXCEPTION WHEN OTHERS THEN
|
||||||
|
is_admin := FALSE;
|
||||||
|
END;
|
||||||
|
|
||||||
|
RETURN is_admin;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- PRODUCTIVITY & WORKSPACE
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- workspace_pages policies
|
||||||
|
DROP POLICY IF EXISTS workspace_pages_tenant_select ON "workspace_pages";
|
||||||
|
DROP POLICY IF EXISTS workspace_pages_tenant_insert ON "workspace_pages";
|
||||||
|
DROP POLICY IF EXISTS workspace_pages_tenant_update ON "workspace_pages";
|
||||||
|
DROP POLICY IF EXISTS workspace_pages_tenant_delete ON "workspace_pages";
|
||||||
|
|
||||||
|
CREATE POLICY workspace_pages_tenant_select ON "workspace_pages"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY workspace_pages_tenant_insert ON "workspace_pages"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY workspace_pages_tenant_update ON "workspace_pages"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY workspace_pages_tenant_delete ON "workspace_pages"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- quick_notes policies
|
||||||
|
DROP POLICY IF EXISTS quick_notes_tenant_select ON "quick_notes";
|
||||||
|
DROP POLICY IF EXISTS quick_notes_tenant_insert ON "quick_notes";
|
||||||
|
DROP POLICY IF EXISTS quick_notes_tenant_update ON "quick_notes";
|
||||||
|
DROP POLICY IF EXISTS quick_notes_tenant_delete ON "quick_notes";
|
||||||
|
|
||||||
|
CREATE POLICY quick_notes_tenant_select ON "quick_notes"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY quick_notes_tenant_insert ON "quick_notes"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY quick_notes_tenant_update ON "quick_notes"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY quick_notes_tenant_delete ON "quick_notes"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- activity_feed policies
|
||||||
|
DROP POLICY IF EXISTS activity_feed_tenant_select ON "activity_feed";
|
||||||
|
DROP POLICY IF EXISTS activity_feed_tenant_insert ON "activity_feed";
|
||||||
|
DROP POLICY IF EXISTS activity_feed_tenant_update ON "activity_feed";
|
||||||
|
DROP POLICY IF EXISTS activity_feed_tenant_delete ON "activity_feed";
|
||||||
|
|
||||||
|
CREATE POLICY activity_feed_tenant_select ON "activity_feed"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY activity_feed_tenant_insert ON "activity_feed"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY activity_feed_tenant_update ON "activity_feed"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY activity_feed_tenant_delete ON "activity_feed"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- conversations policies
|
||||||
|
DROP POLICY IF EXISTS conversations_tenant_select ON "conversations";
|
||||||
|
DROP POLICY IF EXISTS conversations_tenant_insert ON "conversations";
|
||||||
|
DROP POLICY IF EXISTS conversations_tenant_update ON "conversations";
|
||||||
|
DROP POLICY IF EXISTS conversations_tenant_delete ON "conversations";
|
||||||
|
|
||||||
|
CREATE POLICY conversations_tenant_select ON "conversations"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY conversations_tenant_insert ON "conversations"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY conversations_tenant_update ON "conversations"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY conversations_tenant_delete ON "conversations"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- knowledge_base policies
|
||||||
|
DROP POLICY IF EXISTS knowledge_base_tenant_select ON "knowledge_base";
|
||||||
|
DROP POLICY IF EXISTS knowledge_base_tenant_insert ON "knowledge_base";
|
||||||
|
DROP POLICY IF EXISTS knowledge_base_tenant_update ON "knowledge_base";
|
||||||
|
DROP POLICY IF EXISTS knowledge_base_tenant_delete ON "knowledge_base";
|
||||||
|
|
||||||
|
CREATE POLICY knowledge_base_tenant_select ON "knowledge_base"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY knowledge_base_tenant_insert ON "knowledge_base"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY knowledge_base_tenant_update ON "knowledge_base"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY knowledge_base_tenant_delete ON "knowledge_base"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- chat_threads policies
|
||||||
|
DROP POLICY IF EXISTS chat_threads_tenant_select ON "chat_threads";
|
||||||
|
DROP POLICY IF EXISTS chat_threads_tenant_insert ON "chat_threads";
|
||||||
|
DROP POLICY IF EXISTS chat_threads_tenant_update ON "chat_threads";
|
||||||
|
DROP POLICY IF EXISTS chat_threads_tenant_delete ON "chat_threads";
|
||||||
|
|
||||||
|
CREATE POLICY chat_threads_tenant_select ON "chat_threads"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY chat_threads_tenant_insert ON "chat_threads"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY chat_threads_tenant_update ON "chat_threads"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY chat_threads_tenant_delete ON "chat_threads"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- manus_runs policies
|
||||||
|
DROP POLICY IF EXISTS manus_runs_tenant_select ON "manus_runs";
|
||||||
|
DROP POLICY IF EXISTS manus_runs_tenant_insert ON "manus_runs";
|
||||||
|
DROP POLICY IF EXISTS manus_runs_tenant_update ON "manus_runs";
|
||||||
|
DROP POLICY IF EXISTS manus_runs_tenant_delete ON "manus_runs";
|
||||||
|
|
||||||
|
CREATE POLICY manus_runs_tenant_select ON "manus_runs"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY manus_runs_tenant_insert ON "manus_runs"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY manus_runs_tenant_update ON "manus_runs"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY manus_runs_tenant_delete ON "manus_runs"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- LOW-CODE / DEV CENTER
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- arc_doctypes policies
|
||||||
|
DROP POLICY IF EXISTS arc_doctypes_tenant_select ON "arc_doctypes";
|
||||||
|
DROP POLICY IF EXISTS arc_doctypes_tenant_insert ON "arc_doctypes";
|
||||||
|
DROP POLICY IF EXISTS arc_doctypes_tenant_update ON "arc_doctypes";
|
||||||
|
DROP POLICY IF EXISTS arc_doctypes_tenant_delete ON "arc_doctypes";
|
||||||
|
|
||||||
|
CREATE POLICY arc_doctypes_tenant_select ON "arc_doctypes"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id() OR tenant_id IS NULL);
|
||||||
|
|
||||||
|
CREATE POLICY arc_doctypes_tenant_insert ON "arc_doctypes"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY arc_doctypes_tenant_update ON "arc_doctypes"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY arc_doctypes_tenant_delete ON "arc_doctypes"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- arc_layouts policies
|
||||||
|
DROP POLICY IF EXISTS arc_layouts_tenant_select ON "arc_layouts";
|
||||||
|
DROP POLICY IF EXISTS arc_layouts_tenant_insert ON "arc_layouts";
|
||||||
|
DROP POLICY IF EXISTS arc_layouts_tenant_update ON "arc_layouts";
|
||||||
|
DROP POLICY IF EXISTS arc_layouts_tenant_delete ON "arc_layouts";
|
||||||
|
|
||||||
|
CREATE POLICY arc_layouts_tenant_select ON "arc_layouts"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id() OR tenant_id IS NULL);
|
||||||
|
|
||||||
|
CREATE POLICY arc_layouts_tenant_insert ON "arc_layouts"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY arc_layouts_tenant_update ON "arc_layouts"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY arc_layouts_tenant_delete ON "arc_layouts"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- MULTI-TENANCY CORE
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- tenant_empresas policies
|
||||||
|
DROP POLICY IF EXISTS tenant_empresas_tenant_select ON "tenant_empresas";
|
||||||
|
DROP POLICY IF EXISTS tenant_empresas_tenant_insert ON "tenant_empresas";
|
||||||
|
DROP POLICY IF EXISTS tenant_empresas_tenant_update ON "tenant_empresas";
|
||||||
|
DROP POLICY IF EXISTS tenant_empresas_tenant_delete ON "tenant_empresas";
|
||||||
|
|
||||||
|
CREATE POLICY tenant_empresas_tenant_select ON "tenant_empresas"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY tenant_empresas_tenant_insert ON "tenant_empresas"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY tenant_empresas_tenant_update ON "tenant_empresas"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY tenant_empresas_tenant_delete ON "tenant_empresas"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- tenant_users policies (tabela de junção - verifica se user pertence ao tenant)
|
||||||
|
DROP POLICY IF EXISTS tenant_users_tenant_select ON "tenant_users";
|
||||||
|
DROP POLICY IF EXISTS tenant_users_tenant_insert ON "tenant_users";
|
||||||
|
DROP POLICY IF EXISTS tenant_users_tenant_update ON "tenant_users";
|
||||||
|
DROP POLICY IF EXISTS tenant_users_tenant_delete ON "tenant_users";
|
||||||
|
|
||||||
|
CREATE POLICY tenant_users_tenant_select ON "tenant_users"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY tenant_users_tenant_insert ON "tenant_users"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY tenant_users_tenant_update ON "tenant_users"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY tenant_users_tenant_delete ON "tenant_users"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- partner_clients policies
|
||||||
|
DROP POLICY IF EXISTS partner_clients_tenant_select ON "partner_clients";
|
||||||
|
DROP POLICY IF EXISTS partner_clients_tenant_insert ON "partner_clients";
|
||||||
|
DROP POLICY IF EXISTS partner_clients_tenant_update ON "partner_clients";
|
||||||
|
DROP POLICY IF EXISTS partner_clients_tenant_delete ON "partner_clients";
|
||||||
|
|
||||||
|
CREATE POLICY partner_clients_tenant_select ON "partner_clients"
|
||||||
|
FOR SELECT USING (partner_id = get_current_tenant_id() OR client_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY partner_clients_tenant_insert ON "partner_clients"
|
||||||
|
FOR INSERT WITH CHECK (partner_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY partner_clients_tenant_update ON "partner_clients"
|
||||||
|
FOR UPDATE USING (partner_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (partner_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY partner_clients_tenant_delete ON "partner_clients"
|
||||||
|
FOR DELETE USING (partner_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- partner_commissions policies
|
||||||
|
DROP POLICY IF EXISTS partner_commissions_tenant_select ON "partner_commissions";
|
||||||
|
DROP POLICY IF EXISTS partner_commissions_tenant_insert ON "partner_commissions";
|
||||||
|
DROP POLICY IF EXISTS partner_commissions_tenant_update ON "partner_commissions";
|
||||||
|
DROP POLICY IF EXISTS partner_commissions_tenant_delete ON "partner_commissions";
|
||||||
|
|
||||||
|
CREATE POLICY partner_commissions_tenant_select ON "partner_commissions"
|
||||||
|
FOR SELECT USING (partner_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY partner_commissions_tenant_insert ON "partner_commissions"
|
||||||
|
FOR INSERT WITH CHECK (partner_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY partner_commissions_tenant_update ON "partner_commissions"
|
||||||
|
FOR UPDATE USING (partner_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (partner_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY partner_commissions_tenant_delete ON "partner_commissions"
|
||||||
|
FOR DELETE USING (partner_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- PROCESS COMPASS
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- pc_clients policies
|
||||||
|
DROP POLICY IF EXISTS pc_clients_tenant_select ON "pc_clients";
|
||||||
|
DROP POLICY IF EXISTS pc_clients_tenant_insert ON "pc_clients";
|
||||||
|
DROP POLICY IF EXISTS pc_clients_tenant_update ON "pc_clients";
|
||||||
|
DROP POLICY IF EXISTS pc_clients_tenant_delete ON "pc_clients";
|
||||||
|
|
||||||
|
CREATE POLICY pc_clients_tenant_select ON "pc_clients"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_clients_tenant_insert ON "pc_clients"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_clients_tenant_update ON "pc_clients"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_clients_tenant_delete ON "pc_clients"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- pc_projects policies
|
||||||
|
DROP POLICY IF EXISTS pc_projects_tenant_select ON "pc_projects";
|
||||||
|
DROP POLICY IF EXISTS pc_projects_tenant_insert ON "pc_projects";
|
||||||
|
DROP POLICY IF EXISTS pc_projects_tenant_update ON "pc_projects";
|
||||||
|
DROP POLICY IF EXISTS pc_projects_tenant_delete ON "pc_projects";
|
||||||
|
|
||||||
|
CREATE POLICY pc_projects_tenant_select ON "pc_projects"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_projects_tenant_insert ON "pc_projects"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_projects_tenant_update ON "pc_projects"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_projects_tenant_delete ON "pc_projects"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- pc_crm_stages policies
|
||||||
|
DROP POLICY IF EXISTS pc_crm_stages_tenant_select ON "pc_crm_stages";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_stages_tenant_insert ON "pc_crm_stages";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_stages_tenant_update ON "pc_crm_stages";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_stages_tenant_delete ON "pc_crm_stages";
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_stages_tenant_select ON "pc_crm_stages"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_stages_tenant_insert ON "pc_crm_stages"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_stages_tenant_update ON "pc_crm_stages"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_stages_tenant_delete ON "pc_crm_stages"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- pc_crm_leads policies
|
||||||
|
DROP POLICY IF EXISTS pc_crm_leads_tenant_select ON "pc_crm_leads";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_leads_tenant_insert ON "pc_crm_leads";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_leads_tenant_update ON "pc_crm_leads";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_leads_tenant_delete ON "pc_crm_leads";
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_leads_tenant_select ON "pc_crm_leads"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_leads_tenant_insert ON "pc_crm_leads"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_leads_tenant_update ON "pc_crm_leads"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_leads_tenant_delete ON "pc_crm_leads"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- pc_crm_opportunities policies
|
||||||
|
DROP POLICY IF EXISTS pc_crm_opportunities_tenant_select ON "pc_crm_opportunities";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_opportunities_tenant_insert ON "pc_crm_opportunities";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_opportunities_tenant_update ON "pc_crm_opportunities";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_opportunities_tenant_delete ON "pc_crm_opportunities";
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_opportunities_tenant_select ON "pc_crm_opportunities"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_opportunities_tenant_insert ON "pc_crm_opportunities"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_opportunities_tenant_update ON "pc_crm_opportunities"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_opportunities_tenant_delete ON "pc_crm_opportunities"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- pc_crm_activities policies
|
||||||
|
DROP POLICY IF EXISTS pc_crm_activities_tenant_select ON "pc_crm_activities";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_activities_tenant_insert ON "pc_crm_activities";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_activities_tenant_update ON "pc_crm_activities";
|
||||||
|
DROP POLICY IF EXISTS pc_crm_activities_tenant_delete ON "pc_crm_activities";
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_activities_tenant_select ON "pc_crm_activities"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_activities_tenant_insert ON "pc_crm_activities"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_activities_tenant_update ON "pc_crm_activities"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_crm_activities_tenant_delete ON "pc_crm_activities"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- pc_pdca_cycles policies
|
||||||
|
DROP POLICY IF EXISTS pc_pdca_cycles_tenant_select ON "pc_pdca_cycles";
|
||||||
|
DROP POLICY IF EXISTS pc_pdca_cycles_tenant_insert ON "pc_pdca_cycles";
|
||||||
|
DROP POLICY IF EXISTS pc_pdca_cycles_tenant_update ON "pc_pdca_cycles";
|
||||||
|
DROP POLICY IF EXISTS pc_pdca_cycles_tenant_delete ON "pc_pdca_cycles";
|
||||||
|
|
||||||
|
CREATE POLICY pc_pdca_cycles_tenant_select ON "pc_pdca_cycles"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_pdca_cycles_tenant_insert ON "pc_pdca_cycles"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_pdca_cycles_tenant_update ON "pc_pdca_cycles"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_pdca_cycles_tenant_delete ON "pc_pdca_cycles"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- pc_requirements policies
|
||||||
|
DROP POLICY IF EXISTS pc_requirements_tenant_select ON "pc_requirements";
|
||||||
|
DROP POLICY IF EXISTS pc_requirements_tenant_insert ON "pc_requirements";
|
||||||
|
DROP POLICY IF EXISTS pc_requirements_tenant_update ON "pc_requirements";
|
||||||
|
DROP POLICY IF EXISTS pc_requirements_tenant_delete ON "pc_requirements";
|
||||||
|
|
||||||
|
CREATE POLICY pc_requirements_tenant_select ON "pc_requirements"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_requirements_tenant_insert ON "pc_requirements"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_requirements_tenant_update ON "pc_requirements"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY pc_requirements_tenant_delete ON "pc_requirements"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- CRM EXPANDIDO
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- crm_partners policies
|
||||||
|
DROP POLICY IF EXISTS crm_partners_tenant_select ON "crm_partners";
|
||||||
|
DROP POLICY IF EXISTS crm_partners_tenant_insert ON "crm_partners";
|
||||||
|
DROP POLICY IF EXISTS crm_partners_tenant_update ON "crm_partners";
|
||||||
|
DROP POLICY IF EXISTS crm_partners_tenant_delete ON "crm_partners";
|
||||||
|
|
||||||
|
CREATE POLICY crm_partners_tenant_select ON "crm_partners"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id() OR tenant_id IS NULL);
|
||||||
|
|
||||||
|
CREATE POLICY crm_partners_tenant_insert ON "crm_partners"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_partners_tenant_update ON "crm_partners"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_partners_tenant_delete ON "crm_partners"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_contracts policies
|
||||||
|
DROP POLICY IF EXISTS crm_contracts_tenant_select ON "crm_contracts";
|
||||||
|
DROP POLICY IF EXISTS crm_contracts_tenant_insert ON "crm_contracts";
|
||||||
|
DROP POLICY IF EXISTS crm_contracts_tenant_update ON "crm_contracts";
|
||||||
|
DROP POLICY IF EXISTS crm_contracts_tenant_delete ON "crm_contracts";
|
||||||
|
|
||||||
|
CREATE POLICY crm_contracts_tenant_select ON "crm_contracts"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_contracts_tenant_insert ON "crm_contracts"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_contracts_tenant_update ON "crm_contracts"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_contracts_tenant_delete ON "crm_contracts"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_channels policies
|
||||||
|
DROP POLICY IF EXISTS crm_channels_tenant_select ON "crm_channels";
|
||||||
|
DROP POLICY IF EXISTS crm_channels_tenant_insert ON "crm_channels";
|
||||||
|
DROP POLICY IF EXISTS crm_channels_tenant_update ON "crm_channels";
|
||||||
|
DROP POLICY IF EXISTS crm_channels_tenant_delete ON "crm_channels";
|
||||||
|
|
||||||
|
CREATE POLICY crm_channels_tenant_select ON "crm_channels"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_channels_tenant_insert ON "crm_channels"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_channels_tenant_update ON "crm_channels"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_channels_tenant_delete ON "crm_channels"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_threads policies
|
||||||
|
DROP POLICY IF EXISTS crm_threads_tenant_select ON "crm_threads";
|
||||||
|
DROP POLICY IF EXISTS crm_threads_tenant_insert ON "crm_threads";
|
||||||
|
DROP POLICY IF EXISTS crm_threads_tenant_update ON "crm_threads";
|
||||||
|
DROP POLICY IF EXISTS crm_threads_tenant_delete ON "crm_threads";
|
||||||
|
|
||||||
|
CREATE POLICY crm_threads_tenant_select ON "crm_threads"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_threads_tenant_insert ON "crm_threads"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_threads_tenant_update ON "crm_threads"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_threads_tenant_delete ON "crm_threads"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_quick_messages policies
|
||||||
|
DROP POLICY IF EXISTS crm_quick_messages_tenant_select ON "crm_quick_messages";
|
||||||
|
DROP POLICY IF EXISTS crm_quick_messages_tenant_insert ON "crm_quick_messages";
|
||||||
|
DROP POLICY IF EXISTS crm_quick_messages_tenant_update ON "crm_quick_messages";
|
||||||
|
DROP POLICY IF EXISTS crm_quick_messages_tenant_delete ON "crm_quick_messages";
|
||||||
|
|
||||||
|
CREATE POLICY crm_quick_messages_tenant_select ON "crm_quick_messages"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_quick_messages_tenant_insert ON "crm_quick_messages"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_quick_messages_tenant_update ON "crm_quick_messages"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_quick_messages_tenant_delete ON "crm_quick_messages"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_campaigns policies
|
||||||
|
DROP POLICY IF EXISTS crm_campaigns_tenant_select ON "crm_campaigns";
|
||||||
|
DROP POLICY IF EXISTS crm_campaigns_tenant_insert ON "crm_campaigns";
|
||||||
|
DROP POLICY IF EXISTS crm_campaigns_tenant_update ON "crm_campaigns";
|
||||||
|
DROP POLICY IF EXISTS crm_campaigns_tenant_delete ON "crm_campaigns";
|
||||||
|
|
||||||
|
CREATE POLICY crm_campaigns_tenant_select ON "crm_campaigns"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_campaigns_tenant_insert ON "crm_campaigns"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_campaigns_tenant_update ON "crm_campaigns"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_campaigns_tenant_delete ON "crm_campaigns"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_events policies
|
||||||
|
DROP POLICY IF EXISTS crm_events_tenant_select ON "crm_events";
|
||||||
|
DROP POLICY IF EXISTS crm_events_tenant_insert ON "crm_events";
|
||||||
|
DROP POLICY IF EXISTS crm_events_tenant_update ON "crm_events";
|
||||||
|
DROP POLICY IF EXISTS crm_events_tenant_delete ON "crm_events";
|
||||||
|
|
||||||
|
CREATE POLICY crm_events_tenant_select ON "crm_events"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_events_tenant_insert ON "crm_events"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_events_tenant_update ON "crm_events"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_events_tenant_delete ON "crm_events"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_products policies
|
||||||
|
DROP POLICY IF EXISTS crm_products_tenant_select ON "crm_products";
|
||||||
|
DROP POLICY IF EXISTS crm_products_tenant_insert ON "crm_products";
|
||||||
|
DROP POLICY IF EXISTS crm_products_tenant_update ON "crm_products";
|
||||||
|
DROP POLICY IF EXISTS crm_products_tenant_delete ON "crm_products";
|
||||||
|
|
||||||
|
CREATE POLICY crm_products_tenant_select ON "crm_products"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id() OR tenant_id IS NULL);
|
||||||
|
|
||||||
|
CREATE POLICY crm_products_tenant_insert ON "crm_products"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_products_tenant_update ON "crm_products"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_products_tenant_delete ON "crm_products"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_clients policies
|
||||||
|
DROP POLICY IF EXISTS crm_clients_tenant_select ON "crm_clients";
|
||||||
|
DROP POLICY IF EXISTS crm_clients_tenant_insert ON "crm_clients";
|
||||||
|
DROP POLICY IF EXISTS crm_clients_tenant_update ON "crm_clients";
|
||||||
|
DROP POLICY IF EXISTS crm_clients_tenant_delete ON "crm_clients";
|
||||||
|
|
||||||
|
CREATE POLICY crm_clients_tenant_select ON "crm_clients"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_clients_tenant_insert ON "crm_clients"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_clients_tenant_update ON "crm_clients"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_clients_tenant_delete ON "crm_clients"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_pipeline_stages policies
|
||||||
|
DROP POLICY IF EXISTS crm_pipeline_stages_tenant_select ON "crm_pipeline_stages";
|
||||||
|
DROP POLICY IF EXISTS crm_pipeline_stages_tenant_insert ON "crm_pipeline_stages";
|
||||||
|
DROP POLICY IF EXISTS crm_pipeline_stages_tenant_update ON "crm_pipeline_stages";
|
||||||
|
DROP POLICY IF EXISTS crm_pipeline_stages_tenant_delete ON "crm_pipeline_stages";
|
||||||
|
|
||||||
|
CREATE POLICY crm_pipeline_stages_tenant_select ON "crm_pipeline_stages"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_pipeline_stages_tenant_insert ON "crm_pipeline_stages"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_pipeline_stages_tenant_update ON "crm_pipeline_stages"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_pipeline_stages_tenant_delete ON "crm_pipeline_stages"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_leads policies
|
||||||
|
DROP POLICY IF EXISTS crm_leads_tenant_select ON "crm_leads";
|
||||||
|
DROP POLICY IF EXISTS crm_leads_tenant_insert ON "crm_leads";
|
||||||
|
DROP POLICY IF EXISTS crm_leads_tenant_update ON "crm_leads";
|
||||||
|
DROP POLICY IF EXISTS crm_leads_tenant_delete ON "crm_leads";
|
||||||
|
|
||||||
|
CREATE POLICY crm_leads_tenant_select ON "crm_leads"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_leads_tenant_insert ON "crm_leads"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_leads_tenant_update ON "crm_leads"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_leads_tenant_delete ON "crm_leads"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_opportunities policies
|
||||||
|
DROP POLICY IF EXISTS crm_opportunities_tenant_select ON "crm_opportunities";
|
||||||
|
DROP POLICY IF EXISTS crm_opportunities_tenant_insert ON "crm_opportunities";
|
||||||
|
DROP POLICY IF EXISTS crm_opportunities_tenant_update ON "crm_opportunities";
|
||||||
|
DROP POLICY IF EXISTS crm_opportunities_tenant_delete ON "crm_opportunities";
|
||||||
|
|
||||||
|
CREATE POLICY crm_opportunities_tenant_select ON "crm_opportunities"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_opportunities_tenant_insert ON "crm_opportunities"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_opportunities_tenant_update ON "crm_opportunities"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_opportunities_tenant_delete ON "crm_opportunities"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
-- crm_proposals policies
|
||||||
|
DROP POLICY IF EXISTS crm_proposals_tenant_select ON "crm_proposals";
|
||||||
|
DROP POLICY IF EXISTS crm_proposals_tenant_insert ON "crm_proposals";
|
||||||
|
DROP POLICY IF EXISTS crm_proposals_tenant_update ON "crm_proposals";
|
||||||
|
DROP POLICY IF EXISTS crm_proposals_tenant_delete ON "crm_proposals";
|
||||||
|
|
||||||
|
CREATE POLICY crm_proposals_tenant_select ON "crm_proposals"
|
||||||
|
FOR SELECT USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_proposals_tenant_insert ON "crm_proposals"
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_proposals_tenant_update ON "crm_proposals"
|
||||||
|
FOR UPDATE USING (tenant_id = get_current_tenant_id())
|
||||||
|
WITH CHECK (tenant_id = get_current_tenant_id());
|
||||||
|
|
||||||
|
CREATE POLICY crm_proposals_tenant_delete ON "crm_proposals"
|
||||||
|
FOR DELETE USING (tenant_id = get_current_tenant_id());
|
||||||
|
|
@ -0,0 +1,381 @@
|
||||||
|
-- Migration 003: Tenant Context Management
|
||||||
|
-- Data: 2026-04-02
|
||||||
|
-- Description: Configuração de contexto de tenant e validações automáticas
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- FUNÇÃO: Validar se tenant existe e está ativo
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION validate_tenant_active(tenant_id INTEGER)
|
||||||
|
RETURNS BOOLEAN AS $$
|
||||||
|
DECLARE
|
||||||
|
tenant_status TEXT;
|
||||||
|
BEGIN
|
||||||
|
IF tenant_id IS NULL THEN
|
||||||
|
RETURN TRUE; -- Algumas tabelas permitem NULL (globais)
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT status INTO tenant_status
|
||||||
|
FROM tenants
|
||||||
|
WHERE id = tenant_id;
|
||||||
|
|
||||||
|
IF tenant_status IS NULL THEN
|
||||||
|
RAISE EXCEPTION 'Tenant % não encontrado', tenant_id;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF tenant_status != 'active' THEN
|
||||||
|
RAISE EXCEPTION 'Tenant % não está ativo (status: %)', tenant_id, tenant_status;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN TRUE;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- FUNÇÃO: Obter hierarquia de tenants (para master/partner ver subs)
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION get_tenant_hierarchy(parent_tenant_id INTEGER)
|
||||||
|
RETURNS TABLE (tenant_id INTEGER) AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN QUERY
|
||||||
|
WITH RECURSIVE hierarchy AS (
|
||||||
|
-- Base: o próprio tenant
|
||||||
|
SELECT id FROM tenants WHERE id = parent_tenant_id
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
-- Recursão: filhos diretos
|
||||||
|
SELECT t.id
|
||||||
|
FROM tenants t
|
||||||
|
JOIN hierarchy h ON t.parent_tenant_id = h.id
|
||||||
|
)
|
||||||
|
SELECT id FROM hierarchy;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- FUNÇÃO: Verificar se usuário pode acessar tenant específico
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION can_access_tenant(user_id VARCHAR, target_tenant_id INTEGER)
|
||||||
|
RETURNS BOOLEAN AS $$
|
||||||
|
DECLARE
|
||||||
|
user_tenant_id INTEGER;
|
||||||
|
user_role TEXT;
|
||||||
|
target_parent_id INTEGER;
|
||||||
|
is_owner BOOLEAN;
|
||||||
|
BEGIN
|
||||||
|
-- Busca informações do usuário
|
||||||
|
SELECT
|
||||||
|
tu.tenant_id,
|
||||||
|
tu.role,
|
||||||
|
tu.is_owner = 'true'
|
||||||
|
INTO
|
||||||
|
user_tenant_id,
|
||||||
|
user_role,
|
||||||
|
is_owner
|
||||||
|
FROM tenant_users tu
|
||||||
|
WHERE tu.user_id = can_access_tenant.user_id
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
IF user_tenant_id IS NULL THEN
|
||||||
|
RETURN FALSE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Se é o próprio tenant, permite
|
||||||
|
IF user_tenant_id = target_tenant_id THEN
|
||||||
|
RETURN TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Se é master/partner, permite ver filhos
|
||||||
|
SELECT parent_tenant_id INTO target_parent_id
|
||||||
|
FROM tenants
|
||||||
|
WHERE id = target_tenant_id;
|
||||||
|
|
||||||
|
IF target_parent_id = user_tenant_id THEN
|
||||||
|
RETURN TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Se é admin master, pode ver todos
|
||||||
|
IF is_owner AND user_role = 'owner' THEN
|
||||||
|
-- Verifica se é master
|
||||||
|
DECLARE
|
||||||
|
user_tenant_type TEXT;
|
||||||
|
BEGIN
|
||||||
|
SELECT tenant_type INTO user_tenant_type
|
||||||
|
FROM tenants
|
||||||
|
WHERE id = user_tenant_id;
|
||||||
|
|
||||||
|
IF user_tenant_type = 'master' THEN
|
||||||
|
RETURN TRUE;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN FALSE;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- TRIGGER FUNCTION: Validar tenant_id antes de inserir/atualizar
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION validate_tenant_id()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
context_tenant_id INTEGER;
|
||||||
|
BEGIN
|
||||||
|
-- Se não tem tenant_id, permite (tabelas globais)
|
||||||
|
IF NEW.tenant_id IS NULL THEN
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Valida se tenant está ativo
|
||||||
|
PERFORM validate_tenant_active(NEW.tenant_id);
|
||||||
|
|
||||||
|
-- Opcional: validar contra contexto da aplicação
|
||||||
|
BEGIN
|
||||||
|
context_tenant_id := current_setting('app.current_tenant_id', true)::INTEGER;
|
||||||
|
IF context_tenant_id IS NOT NULL AND NEW.tenant_id != context_tenant_id THEN
|
||||||
|
RAISE EXCEPTION 'Tenant ID % não corresponde ao contexto atual %',
|
||||||
|
NEW.tenant_id, context_tenant_id;
|
||||||
|
END IF;
|
||||||
|
EXCEPTION WHEN OTHERS THEN
|
||||||
|
-- Se não conseguiu ler o contexto, continua (para migrations, seeds, etc)
|
||||||
|
NULL;
|
||||||
|
END;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- CRIAR TRIGGERS PARA VALIDAÇÃO AUTOMÁTICA
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
-- workspace_pages
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_workspace_pages_tenant ON "workspace_pages";
|
||||||
|
CREATE TRIGGER trigger_validate_workspace_pages_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "workspace_pages"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- quick_notes
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_quick_notes_tenant ON "quick_notes";
|
||||||
|
CREATE TRIGGER trigger_validate_quick_notes_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "quick_notes"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- activity_feed
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_activity_feed_tenant ON "activity_feed";
|
||||||
|
CREATE TRIGGER trigger_validate_activity_feed_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "activity_feed"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- conversations
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_conversations_tenant ON "conversations";
|
||||||
|
CREATE TRIGGER trigger_validate_conversations_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "conversations"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- knowledge_base
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_knowledge_base_tenant ON "knowledge_base";
|
||||||
|
CREATE TRIGGER trigger_validate_knowledge_base_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "knowledge_base"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- chat_threads
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_chat_threads_tenant ON "chat_threads";
|
||||||
|
CREATE TRIGGER trigger_validate_chat_threads_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "chat_threads"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- manus_runs
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_manus_runs_tenant ON "manus_runs";
|
||||||
|
CREATE TRIGGER trigger_validate_manus_runs_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "manus_runs"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- tenant_empresas
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_tenant_empresas_tenant ON "tenant_empresas";
|
||||||
|
CREATE TRIGGER trigger_validate_tenant_empresas_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "tenant_empresas"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- pc_clients
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_pc_clients_tenant ON "pc_clients";
|
||||||
|
CREATE TRIGGER trigger_validate_pc_clients_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "pc_clients"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- pc_projects
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_pc_projects_tenant ON "pc_projects";
|
||||||
|
CREATE TRIGGER trigger_validate_pc_projects_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "pc_projects"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- pc_crm_leads
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_pc_crm_leads_tenant ON "pc_crm_leads";
|
||||||
|
CREATE TRIGGER trigger_validate_pc_crm_leads_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "pc_crm_leads"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- pc_crm_opportunities
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_pc_crm_opportunities_tenant ON "pc_crm_opportunities";
|
||||||
|
CREATE TRIGGER trigger_validate_pc_crm_opportunities_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "pc_crm_opportunities"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- pc_pdca_cycles
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_pc_pdca_cycles_tenant ON "pc_pdca_cycles";
|
||||||
|
CREATE TRIGGER trigger_validate_pc_pdca_cycles_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "pc_pdca_cycles"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_partners
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_partners_tenant ON "crm_partners";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_partners_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_partners"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_contracts
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_contracts_tenant ON "crm_contracts";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_contracts_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_contracts"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_channels
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_channels_tenant ON "crm_channels";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_channels_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_channels"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_threads
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_threads_tenant ON "crm_threads";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_threads_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_threads"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_campaigns
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_campaigns_tenant ON "crm_campaigns";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_campaigns_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_campaigns"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_events
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_events_tenant ON "crm_events";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_events_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_events"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_products
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_products_tenant ON "crm_products";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_products_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_products"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_clients
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_clients_tenant ON "crm_clients";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_clients_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_clients"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_leads
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_leads_tenant ON "crm_leads";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_leads_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_leads"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_opportunities
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_opportunities_tenant ON "crm_opportunities";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_opportunities_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_opportunities"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- crm_proposals
|
||||||
|
DROP TRIGGER IF EXISTS trigger_validate_crm_proposals_tenant ON "crm_proposals";
|
||||||
|
CREATE TRIGGER trigger_validate_crm_proposals_tenant
|
||||||
|
BEFORE INSERT OR UPDATE ON "crm_proposals"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION validate_tenant_id();
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- VIEW: Resumo de segurança por tenant
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE VIEW tenant_security_summary AS
|
||||||
|
SELECT
|
||||||
|
t.id AS tenant_id,
|
||||||
|
t.name AS tenant_name,
|
||||||
|
t.slug,
|
||||||
|
t.tenant_type,
|
||||||
|
t.status,
|
||||||
|
(SELECT COUNT(*) FROM tenant_users tu WHERE tu.tenant_id = t.id) AS user_count,
|
||||||
|
(SELECT COUNT(*) FROM workspace_pages wp WHERE wp.tenant_id = t.id) AS workspace_pages_count,
|
||||||
|
(SELECT COUNT(*) FROM pc_clients pc WHERE pc.tenant_id = t.id) AS clients_count,
|
||||||
|
(SELECT COUNT(*) FROM pc_projects pp WHERE pp.tenant_id = t.id) AS projects_count,
|
||||||
|
(SELECT COUNT(*) FROM crm_leads cl WHERE cl.tenant_id = t.id) AS leads_count,
|
||||||
|
(SELECT COUNT(*) FROM crm_opportunities co WHERE co.tenant_id = t.id) AS opportunities_count
|
||||||
|
FROM tenants t
|
||||||
|
ORDER BY t.id;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- FUNÇÃO: Setar contexto de tenant (para uso na aplicação)
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION set_tenant_context(p_tenant_id INTEGER, p_user_id VARCHAR, p_role TEXT DEFAULT 'member')
|
||||||
|
RETURNS VOID AS $$
|
||||||
|
BEGIN
|
||||||
|
PERFORM set_config('app.current_tenant_id', p_tenant_id::TEXT, FALSE);
|
||||||
|
PERFORM set_config('app.current_user_id', p_user_id, FALSE);
|
||||||
|
PERFORM set_config('app.current_tenant_role', p_role, FALSE);
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- FUNÇÃO: Limpar contexto de tenant
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION clear_tenant_context()
|
||||||
|
RETURNS VOID AS $$
|
||||||
|
BEGIN
|
||||||
|
PERFORM set_config('app.current_tenant_id', '', FALSE);
|
||||||
|
PERFORM set_config('app.current_user_id', '', FALSE);
|
||||||
|
PERFORM set_config('app.current_tenant_role', '', FALSE);
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|
||||||
|
-- ==========================================
|
||||||
|
-- COMENTÁRIOS DE DOCUMENTAÇÃO
|
||||||
|
-- ==========================================
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION get_current_tenant_id() IS 'Retorna o ID do tenant atual do contexto da aplicação';
|
||||||
|
COMMENT ON FUNCTION is_tenant_admin() IS 'Verifica se o usuário atual é admin/owner do tenant';
|
||||||
|
COMMENT ON FUNCTION validate_tenant_active(INTEGER) IS 'Valida se um tenant existe e está ativo';
|
||||||
|
COMMENT ON FUNCTION can_access_tenant(VARCHAR, INTEGER) IS 'Verifica se um usuário pode acessar um tenant específico';
|
||||||
|
COMMENT ON FUNCTION set_tenant_context(INTEGER, VARCHAR, TEXT) IS 'Define o contexto de tenant para a sessão atual';
|
||||||
|
COMMENT ON FUNCTION clear_tenant_context() IS 'Limpa o contexto de tenant da sessão atual';
|
||||||
|
COMMENT ON VIEW tenant_security_summary IS 'Resumo de segurança e dados por tenant';
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
# Migrações RLS - Row Level Security
|
||||||
|
|
||||||
|
Esta pasta contém as migrações para habilitar Row Level Security (RLS) no PostgreSQL,
|
||||||
|
garantindo isolamento de dados entre tenants (multi-tenancy).
|
||||||
|
|
||||||
|
## Arquivos
|
||||||
|
|
||||||
|
| Arquivo | Descrição |
|
||||||
|
|---------|-----------|
|
||||||
|
| `001_enable_rls.sql` | Habilita RLS em todas as tabelas com `tenant_id` |
|
||||||
|
| `002_create_policies.sql` | Cria políticas CRUD por tenant |
|
||||||
|
| `003_tenant_context.sql` | Funções de contexto e triggers de validação |
|
||||||
|
|
||||||
|
## Como Aplicar
|
||||||
|
|
||||||
|
### Opção 1: Aplicar via psql (Produção)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Configurar variáveis de ambiente
|
||||||
|
export DATABASE_URL="postgresql://user:pass@host:5432/arcadia"
|
||||||
|
|
||||||
|
# Aplicar migrações
|
||||||
|
psql $DATABASE_URL -f migrations/001_enable_rls.sql
|
||||||
|
psql $DATABASE_URL -f migrations/002_create_policies.sql
|
||||||
|
psql $DATABASE_URL -f migrations/003_tenant_context.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Opção 2: Aplicar via script Node.js
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run db:apply-rls
|
||||||
|
```
|
||||||
|
|
||||||
|
(Nota: este script precisa ser criado no package.json)
|
||||||
|
|
||||||
|
### Opção 3: Aplicar via Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -i arcadia-db psql -U arcadia -d arcadia < migrations/001_enable_rls.sql
|
||||||
|
docker exec -i arcadia-db psql -U arcadia -d arcadia < migrations/002_create_policies.sql
|
||||||
|
docker exec -i arcadia-db psql -U arcadia -d arcadia < migrations/003_tenant_context.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tabelas Protegidas
|
||||||
|
|
||||||
|
### Productivity & Workspace
|
||||||
|
- `workspace_pages`, `quick_notes`, `activity_feed`
|
||||||
|
- `conversations`, `knowledge_base`, `chat_threads`, `manus_runs`
|
||||||
|
|
||||||
|
### Low-Code / Dev Center
|
||||||
|
- `arc_doctypes`, `arc_layouts`
|
||||||
|
|
||||||
|
### Multi-Tenancy Core
|
||||||
|
- `tenant_empresas`, `tenant_users`, `partner_clients`, `partner_commissions`
|
||||||
|
|
||||||
|
### Process Compass
|
||||||
|
- `pc_clients`, `pc_projects`, `pc_crm_stages`, `pc_crm_leads`
|
||||||
|
- `pc_crm_opportunities`, `pc_crm_activities`, `pc_pdca_cycles`, `pc_requirements`
|
||||||
|
|
||||||
|
### CRM Expandido
|
||||||
|
- `crm_partners`, `crm_contracts`, `crm_channels`, `crm_threads`
|
||||||
|
- `crm_quick_messages`, `crm_campaigns`, `crm_events`, `crm_products`
|
||||||
|
- `crm_clients`, `crm_pipeline_stages`, `crm_leads`, `crm_opportunities`, `crm_proposals`
|
||||||
|
|
||||||
|
## Funções Disponíveis
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Obter tenant atual do contexto
|
||||||
|
SELECT get_current_tenant_id();
|
||||||
|
|
||||||
|
-- Verificar se é admin
|
||||||
|
SELECT is_tenant_admin();
|
||||||
|
|
||||||
|
-- Setar contexto (deve ser chamado pela aplicação)
|
||||||
|
SELECT set_tenant_context(1, 'user-uuid', 'member');
|
||||||
|
|
||||||
|
-- Verificar acesso
|
||||||
|
SELECT can_access_tenant('user-uuid', 1);
|
||||||
|
|
||||||
|
-- View de resumo
|
||||||
|
SELECT * FROM tenant_security_summary;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integração com a Aplicação
|
||||||
|
|
||||||
|
O middleware da aplicação deve chamar `set_tenant_context` após autenticação:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// server/middleware/tenant-context.ts
|
||||||
|
import { db } from "../db";
|
||||||
|
|
||||||
|
export async function setTenantContext(req, res, next) {
|
||||||
|
if (req.user?.tenantId) {
|
||||||
|
await db.execute(
|
||||||
|
sql`SELECT set_tenant_context(${req.user.tenantId}, ${req.user.id}, ${req.user.tenantRole})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
Para desabilitar RLS (emergência apenas):
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Exemplo para uma tabela
|
||||||
|
ALTER TABLE workspace_pages DISABLE ROW LEVEL SECURITY;
|
||||||
|
DROP POLICY workspace_pages_tenant_select ON workspace_pages;
|
||||||
|
-- ... etc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notas
|
||||||
|
|
||||||
|
- As políticas usam `get_current_tenant_id()` que lê do contexto da sessão
|
||||||
|
- Triggers validam se `tenant_id` corresponde ao contexto antes de INSERT/UPDATE
|
||||||
|
- Tabelas sem `tenant_id` (users, tenants, profiles) NÃO têm RLS (acesso global)
|
||||||
|
- A view `tenant_security_summary` mostra estatísticas por tenant
|
||||||
|
|
@ -69,7 +69,6 @@
|
||||||
"connect-pg-simple": "^10.0.0",
|
"connect-pg-simple": "^10.0.0",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"docx": "^9.5.1",
|
"docx": "^9.5.1",
|
||||||
"dotenv": "^17.3.1",
|
|
||||||
"drizzle-orm": "^0.39.3",
|
"drizzle-orm": "^0.39.3",
|
||||||
"drizzle-zod": "^0.7.1",
|
"drizzle-zod": "^0.7.1",
|
||||||
"embla-carousel-react": "^8.6.0",
|
"embla-carousel-react": "^8.6.0",
|
||||||
|
|
@ -9118,18 +9117,6 @@
|
||||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
|
||||||
"version": "17.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz",
|
|
||||||
"integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://dotenvx.com"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/drizzle-kit": {
|
"node_modules/drizzle-kit": {
|
||||||
"version": "0.31.4",
|
"version": "0.31.4",
|
||||||
"resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.4.tgz",
|
"resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.4.tgz",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
"start": "NODE_ENV=production node dist/index.cjs",
|
"start": "NODE_ENV=production node dist/index.cjs",
|
||||||
"check": "tsc",
|
"check": "tsc",
|
||||||
"db:push": "drizzle-kit push",
|
"db:push": "drizzle-kit push",
|
||||||
|
"db:apply-rls": "tsx scripts/apply-rls-migrations.ts",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"test:openclaw": "vitest run tests/openclaw"
|
"test:openclaw": "vitest run tests/openclaw"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
#!/usr/bin/env tsx
|
||||||
|
/**
|
||||||
|
* Script para aplicar migrações RLS (Row Level Security)
|
||||||
|
*
|
||||||
|
* Uso:
|
||||||
|
* npm run db:apply-rls
|
||||||
|
* ou
|
||||||
|
* npx tsx scripts/apply-rls-migrations.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { join, dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import pg from 'pg';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
const { Pool } = pg;
|
||||||
|
|
||||||
|
// Migrações na ordem correta
|
||||||
|
const MIGRATIONS = [
|
||||||
|
'001_enable_rls.sql',
|
||||||
|
'002_create_policies.sql',
|
||||||
|
'003_tenant_context.sql',
|
||||||
|
];
|
||||||
|
|
||||||
|
async function applyMigration(pool: pg.Pool, filename: string): Promise<void> {
|
||||||
|
const filepath = join(__dirname, '..', 'migrations', filename);
|
||||||
|
const sql = readFileSync(filepath, 'utf-8');
|
||||||
|
|
||||||
|
console.log(`\n📄 Aplicando: ${filename}`);
|
||||||
|
console.log('='.repeat(50));
|
||||||
|
|
||||||
|
const client = await pool.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.query('BEGIN');
|
||||||
|
|
||||||
|
// Divide o SQL em statements individuais (separados por ;)
|
||||||
|
const statements = sql
|
||||||
|
.split(';')
|
||||||
|
.map(s => s.trim())
|
||||||
|
.filter(s => s.length > 0 && !s.startsWith('--'));
|
||||||
|
|
||||||
|
for (let i = 0; i < statements.length; i++) {
|
||||||
|
const stmt = statements[i];
|
||||||
|
try {
|
||||||
|
await client.query(stmt);
|
||||||
|
process.stdout.write('.');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`\n❌ Erro no statement ${i + 1}:`);
|
||||||
|
console.error(err.message);
|
||||||
|
// Alguns erros são esperados (ex: DROP IF NOT EXISTS)
|
||||||
|
if (!err.message.includes('does not exist')) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.query('COMMIT');
|
||||||
|
console.log(`\n✅ ${filename} aplicado com sucesso!`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await client.query('ROLLBACK');
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
client.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyRLS(pool: pg.Pool): Promise<void> {
|
||||||
|
console.log('\n🔍 Verificando tabelas com RLS habilitado...\n');
|
||||||
|
|
||||||
|
const result = await pool.query(`
|
||||||
|
SELECT
|
||||||
|
schemaname,
|
||||||
|
tablename,
|
||||||
|
rowsecurity as rls_enabled
|
||||||
|
FROM pg_tables
|
||||||
|
JOIN pg_class ON pg_class.relname = pg_tables.tablename
|
||||||
|
WHERE schemaname = 'public'
|
||||||
|
AND rowsecurity = true
|
||||||
|
ORDER BY tablename;
|
||||||
|
`);
|
||||||
|
|
||||||
|
console.log(`✓ ${result.rows.length} tabelas com RLS habilitado:\n`);
|
||||||
|
result.rows.forEach((row, i) => {
|
||||||
|
console.log(` ${i + 1}. ${row.tablename}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyPolicies(pool: pg.Pool): Promise<void> {
|
||||||
|
console.log('\n📋 Verificando políticas criadas...\n');
|
||||||
|
|
||||||
|
const result = await pool.query(`
|
||||||
|
SELECT
|
||||||
|
tablename,
|
||||||
|
policyname,
|
||||||
|
permissive,
|
||||||
|
roles,
|
||||||
|
cmd as operation,
|
||||||
|
qual as condition
|
||||||
|
FROM pg_policies
|
||||||
|
WHERE schemaname = 'public'
|
||||||
|
ORDER BY tablename, policyname;
|
||||||
|
`);
|
||||||
|
|
||||||
|
console.log(`✓ ${result.rows.length} políticas criadas\n`);
|
||||||
|
|
||||||
|
// Agrupa por tabela
|
||||||
|
const byTable: Record<string, string[]> = {};
|
||||||
|
result.rows.forEach(row => {
|
||||||
|
if (!byTable[row.tablename]) {
|
||||||
|
byTable[row.tablename] = [];
|
||||||
|
}
|
||||||
|
byTable[row.tablename].push(`${row.operation}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.entries(byTable).forEach(([table, ops]) => {
|
||||||
|
console.log(` • ${table}: ${ops.join(', ')}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const databaseUrl = process.env.DATABASE_URL;
|
||||||
|
|
||||||
|
if (!databaseUrl) {
|
||||||
|
console.error('❌ DATABASE_URL não configurado!');
|
||||||
|
console.error('Exemplo: export DATABASE_URL="postgresql://user:pass@localhost:5432/arcadia"');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🚀 Iniciando aplicação das migrações RLS...');
|
||||||
|
console.log(`🔗 Database: ${databaseUrl.replace(/:[^:@]*@/, ':***@')}`);
|
||||||
|
|
||||||
|
const pool = new Pool({
|
||||||
|
connectionString: databaseUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Testa conexão
|
||||||
|
await pool.query('SELECT 1');
|
||||||
|
console.log('✅ Conexão com banco estabelecida\n');
|
||||||
|
|
||||||
|
// Aplica cada migração
|
||||||
|
for (const migration of MIGRATIONS) {
|
||||||
|
await applyMigration(pool, migration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificações finais
|
||||||
|
await verifyRLS(pool);
|
||||||
|
await verifyPolicies(pool);
|
||||||
|
|
||||||
|
console.log('\n' + '='.repeat(50));
|
||||||
|
console.log('✅ Todas as migrações aplicadas com sucesso!');
|
||||||
|
console.log('='.repeat(50) + '\n');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('\n❌ Erro ao aplicar migrações:');
|
||||||
|
console.error(error.message);
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
await pool.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
Loading…
Reference in New Issue