Skip to content

Row Level Security (RLS) Policies

Sport Tech Club - Isolamento Multi-tenant com PostgreSQL RLS

Visão Geral

Este documento define as políticas de Row Level Security (RLS) para garantir isolamento completo de dados entre tenants na plataforma Sport Tech Club.


1. Arquitetura de Segurança

1.1 Modelo de Multi-tenancy

┌─────────────────────────────────────────────────────────────────┐
│                        Application Layer                         │
├─────────────────────────────────────────────────────────────────┤
│                     Connection Pool (PgBouncer)                  │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐  │
│  │   Schema:       │  │   Schema:       │  │   Schema:       │  │
│  │   identity      │  │   arena         │  │   booking       │  │
│  │                 │  │                 │  │                 │  │
│  │  - tenants      │  │  - arenas       │  │  - bookings     │  │
│  │  - users        │  │  - courts       │  │  - payments     │  │
│  │  - roles        │  │  - products     │  │  - check_ins    │  │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘  │
├─────────────────────────────────────────────────────────────────┤
│                    RLS Policies (tenant_id)                      │
├─────────────────────────────────────────────────────────────────┤
│                        PostgreSQL 15+                            │
└─────────────────────────────────────────────────────────────────┘

1.2 Variáveis de Sessão

sql
-- Variáveis de contexto para RLS
-- Definidas no início de cada transação

-- Tenant ID do usuário autenticado
SET LOCAL app.current_tenant_id = 'uuid-do-tenant';

-- User ID do usuário autenticado
SET LOCAL app.current_user_id = 'uuid-do-usuario';

-- Roles do usuário (JSON array)
SET LOCAL app.current_user_roles = '["ARENA_OWNER", "PLAYER"]';

-- Arena ID atual (para operações com escopo de arena)
SET LOCAL app.current_arena_id = 'uuid-da-arena';

2. Funções Auxiliares

2.1 Funções de Contexto

sql
-- Obtém o tenant_id da sessão atual
CREATE OR REPLACE FUNCTION current_tenant_id()
RETURNS UUID AS $$
BEGIN
  RETURN NULLIF(current_setting('app.current_tenant_id', true), '')::UUID;
EXCEPTION
  WHEN OTHERS THEN
    RETURN NULL;
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

-- Obtém o user_id da sessão atual
CREATE OR REPLACE FUNCTION current_user_id()
RETURNS UUID AS $$
BEGIN
  RETURN NULLIF(current_setting('app.current_user_id', true), '')::UUID;
EXCEPTION
  WHEN OTHERS THEN
    RETURN NULL;
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

-- Obtém as roles do usuário atual
CREATE OR REPLACE FUNCTION current_user_roles()
RETURNS TEXT[] AS $$
DECLARE
  roles_json TEXT;
BEGIN
  roles_json := current_setting('app.current_user_roles', true);
  IF roles_json IS NULL OR roles_json = '' THEN
    RETURN ARRAY[]::TEXT[];
  END IF;
  RETURN ARRAY(SELECT jsonb_array_elements_text(roles_json::JSONB));
EXCEPTION
  WHEN OTHERS THEN
    RETURN ARRAY[]::TEXT[];
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

-- Obtém o arena_id da sessão atual
CREATE OR REPLACE FUNCTION current_arena_id()
RETURNS UUID AS $$
BEGIN
  RETURN NULLIF(current_setting('app.current_arena_id', true), '')::UUID;
EXCEPTION
  WHEN OTHERS THEN
    RETURN NULL;
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

-- Verifica se usuário tem uma role específica
CREATE OR REPLACE FUNCTION has_role(role_name TEXT)
RETURNS BOOLEAN AS $$
BEGIN
  RETURN role_name = ANY(current_user_roles());
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

-- Verifica se é super admin (acesso irrestrito)
CREATE OR REPLACE FUNCTION is_super_admin()
RETURNS BOOLEAN AS $$
BEGIN
  RETURN has_role('SUPER_ADMIN');
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

2.2 Funções de Verificação

sql
-- Verifica se usuário pertence ao tenant
CREATE OR REPLACE FUNCTION user_belongs_to_tenant(check_tenant_id UUID)
RETURNS BOOLEAN AS $$
BEGIN
  -- Super admin tem acesso a todos os tenants
  IF is_super_admin() THEN
    RETURN TRUE;
  END IF;

  RETURN check_tenant_id = current_tenant_id();
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

-- Verifica se usuário tem acesso à arena
CREATE OR REPLACE FUNCTION user_has_arena_access(check_arena_id UUID)
RETURNS BOOLEAN AS $$
DECLARE
  arena_tenant_id UUID;
BEGIN
  -- Super admin tem acesso a todas as arenas
  IF is_super_admin() THEN
    RETURN TRUE;
  END IF;

  -- Busca o tenant da arena
  SELECT tenant_id INTO arena_tenant_id
  FROM arena.arenas
  WHERE id = check_arena_id;

  -- Verifica se o tenant da arena é o mesmo do usuário
  RETURN arena_tenant_id = current_tenant_id();
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

-- Verifica se usuário é dono do recurso
CREATE OR REPLACE FUNCTION is_resource_owner(resource_user_id UUID)
RETURNS BOOLEAN AS $$
BEGIN
  RETURN resource_user_id = current_user_id();
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;

3. Políticas por Tabela

3.1 Schema: Identity

Tabela: tenants

sql
-- Habilita RLS
ALTER TABLE identity.tenants ENABLE ROW LEVEL SECURITY;

-- Força RLS mesmo para o owner da tabela
ALTER TABLE identity.tenants FORCE ROW LEVEL SECURITY;

-- SELECT: Usuário só vê seu próprio tenant (ou todos se super admin)
CREATE POLICY tenants_select_policy ON identity.tenants
  FOR SELECT
  USING (
    is_super_admin() OR id = current_tenant_id()
  );

-- INSERT: Apenas super admin pode criar tenants
CREATE POLICY tenants_insert_policy ON identity.tenants
  FOR INSERT
  WITH CHECK (is_super_admin());

-- UPDATE: Tenant owner ou super admin
CREATE POLICY tenants_update_policy ON identity.tenants
  FOR UPDATE
  USING (
    is_super_admin() OR (
      id = current_tenant_id() AND has_role('ARENA_OWNER')
    )
  );

-- DELETE: Apenas super admin (soft delete na prática)
CREATE POLICY tenants_delete_policy ON identity.tenants
  FOR DELETE
  USING (is_super_admin());

Tabela: users

sql
ALTER TABLE identity.users ENABLE ROW LEVEL SECURITY;
ALTER TABLE identity.users FORCE ROW LEVEL SECURITY;

-- SELECT: Usuários do mesmo tenant ou o próprio usuário
CREATE POLICY users_select_policy ON identity.users
  FOR SELECT
  USING (
    is_super_admin()
    OR tenant_id = current_tenant_id()
    OR id = current_user_id()
  );

-- INSERT: Arena owner/manager do tenant ou super admin
CREATE POLICY users_insert_policy ON identity.users
  FOR INSERT
  WITH CHECK (
    is_super_admin()
    OR (
      tenant_id = current_tenant_id()
      AND (has_role('ARENA_OWNER') OR has_role('ARENA_MANAGER'))
    )
  );

-- UPDATE: O próprio usuário ou admin do tenant
CREATE POLICY users_update_policy ON identity.users
  FOR UPDATE
  USING (
    is_super_admin()
    OR id = current_user_id()
    OR (
      tenant_id = current_tenant_id()
      AND (has_role('ARENA_OWNER') OR has_role('ARENA_MANAGER'))
    )
  );

-- DELETE: Admin do tenant ou super admin
CREATE POLICY users_delete_policy ON identity.users
  FOR DELETE
  USING (
    is_super_admin()
    OR (
      tenant_id = current_tenant_id()
      AND has_role('ARENA_OWNER')
      AND id != current_user_id() -- Não pode deletar a si mesmo
    )
  );

Tabela: user_roles

sql
ALTER TABLE identity.user_roles ENABLE ROW LEVEL SECURITY;
ALTER TABLE identity.user_roles FORCE ROW LEVEL SECURITY;

-- SELECT: Ver roles de usuários do mesmo tenant
CREATE POLICY user_roles_select_policy ON identity.user_roles
  FOR SELECT
  USING (
    is_super_admin()
    OR EXISTS (
      SELECT 1 FROM identity.users u
      WHERE u.id = user_roles.user_id
      AND u.tenant_id = current_tenant_id()
    )
  );

-- INSERT: Arena owner pode atribuir roles (exceto SUPER_ADMIN)
CREATE POLICY user_roles_insert_policy ON identity.user_roles
  FOR INSERT
  WITH CHECK (
    is_super_admin()
    OR (
      has_role('ARENA_OWNER')
      AND role != 'SUPER_ADMIN'
      AND EXISTS (
        SELECT 1 FROM identity.users u
        WHERE u.id = user_roles.user_id
        AND u.tenant_id = current_tenant_id()
      )
    )
  );

-- DELETE: Arena owner pode remover roles
CREATE POLICY user_roles_delete_policy ON identity.user_roles
  FOR DELETE
  USING (
    is_super_admin()
    OR (
      has_role('ARENA_OWNER')
      AND role != 'SUPER_ADMIN'
      AND EXISTS (
        SELECT 1 FROM identity.users u
        WHERE u.id = user_roles.user_id
        AND u.tenant_id = current_tenant_id()
      )
    )
  );

3.2 Schema: Arena

Tabela: arenas

sql
ALTER TABLE arena.arenas ENABLE ROW LEVEL SECURITY;
ALTER TABLE arena.arenas FORCE ROW LEVEL SECURITY;

-- SELECT: Arenas do tenant (públicas para busca) ou super admin
CREATE POLICY arenas_select_policy ON arena.arenas
  FOR SELECT
  USING (
    is_super_admin()
    OR tenant_id = current_tenant_id()
    OR status = 'ACTIVE' -- Arenas ativas são públicas para busca
  );

-- INSERT: Arena owner do tenant
CREATE POLICY arenas_insert_policy ON arena.arenas
  FOR INSERT
  WITH CHECK (
    is_super_admin()
    OR (
      tenant_id = current_tenant_id()
      AND has_role('ARENA_OWNER')
    )
  );

-- UPDATE: Arena owner/manager do tenant
CREATE POLICY arenas_update_policy ON arena.arenas
  FOR UPDATE
  USING (
    is_super_admin()
    OR (
      tenant_id = current_tenant_id()
      AND (has_role('ARENA_OWNER') OR has_role('ARENA_MANAGER'))
    )
  );

-- DELETE: Arena owner do tenant
CREATE POLICY arenas_delete_policy ON arena.arenas
  FOR DELETE
  USING (
    is_super_admin()
    OR (
      tenant_id = current_tenant_id()
      AND has_role('ARENA_OWNER')
    )
  );

Tabela: courts

sql
ALTER TABLE arena.courts ENABLE ROW LEVEL SECURITY;
ALTER TABLE arena.courts FORCE ROW LEVEL SECURITY;

-- SELECT: Quadras de arenas acessíveis
CREATE POLICY courts_select_policy ON arena.courts
  FOR SELECT
  USING (
    is_super_admin()
    OR EXISTS (
      SELECT 1 FROM arena.arenas a
      WHERE a.id = courts.arena_id
      AND (a.tenant_id = current_tenant_id() OR a.status = 'ACTIVE')
    )
  );

-- INSERT: Manager da arena
CREATE POLICY courts_insert_policy ON arena.courts
  FOR INSERT
  WITH CHECK (
    is_super_admin()
    OR (
      user_has_arena_access(arena_id)
      AND (has_role('ARENA_OWNER') OR has_role('ARENA_MANAGER'))
    )
  );

-- UPDATE: Manager da arena
CREATE POLICY courts_update_policy ON arena.courts
  FOR UPDATE
  USING (
    is_super_admin()
    OR (
      user_has_arena_access(arena_id)
      AND (has_role('ARENA_OWNER') OR has_role('ARENA_MANAGER'))
    )
  );

-- DELETE: Owner da arena
CREATE POLICY courts_delete_policy ON arena.courts
  FOR DELETE
  USING (
    is_super_admin()
    OR (
      user_has_arena_access(arena_id)
      AND has_role('ARENA_OWNER')
    )
  );

Tabela: pricing_rules

sql
ALTER TABLE arena.pricing_rules ENABLE ROW LEVEL SECURITY;
ALTER TABLE arena.pricing_rules FORCE ROW LEVEL SECURITY;

-- SELECT: Regras de preço das arenas acessíveis
CREATE POLICY pricing_rules_select_policy ON arena.pricing_rules
  FOR SELECT
  USING (
    is_super_admin()
    OR user_has_arena_access(arena_id)
  );

-- INSERT/UPDATE/DELETE: Manager da arena
CREATE POLICY pricing_rules_modify_policy ON arena.pricing_rules
  FOR ALL
  USING (
    is_super_admin()
    OR (
      user_has_arena_access(arena_id)
      AND (has_role('ARENA_OWNER') OR has_role('ARENA_MANAGER'))
    )
  );

3.3 Schema: Booking

Tabela: bookings

sql
ALTER TABLE booking.bookings ENABLE ROW LEVEL SECURITY;
ALTER TABLE booking.bookings FORCE ROW LEVEL SECURITY;

-- SELECT: Próprias reservas ou reservas da arena (para staff)
CREATE POLICY bookings_select_policy ON booking.bookings
  FOR SELECT
  USING (
    is_super_admin()
    -- Próprias reservas
    OR user_id = current_user_id()
    -- Staff da arena vê todas as reservas
    OR (
      user_has_arena_access(arena_id)
      AND (
        has_role('ARENA_OWNER')
        OR has_role('ARENA_MANAGER')
        OR has_role('RECEPTIONIST')
      )
    )
  );

-- INSERT: Usuário autenticado pode criar reservas
CREATE POLICY bookings_insert_policy ON booking.bookings
  FOR INSERT
  WITH CHECK (
    is_super_admin()
    OR (
      -- Deve ser para uma arena acessível
      EXISTS (
        SELECT 1 FROM arena.arenas a
        WHERE a.id = bookings.arena_id
        AND a.status = 'ACTIVE'
      )
      -- E o usuário criador deve ser o atual
      AND created_by_id = current_user_id()
    )
  );

-- UPDATE: Própria reserva ou staff da arena
CREATE POLICY bookings_update_policy ON booking.bookings
  FOR UPDATE
  USING (
    is_super_admin()
    -- Própria reserva (apenas antes do horário)
    OR (
      user_id = current_user_id()
      AND start_time > NOW()
      AND status NOT IN ('COMPLETED', 'CANCELLED')
    )
    -- Staff da arena
    OR (
      user_has_arena_access(arena_id)
      AND (
        has_role('ARENA_OWNER')
        OR has_role('ARENA_MANAGER')
        OR has_role('RECEPTIONIST')
      )
    )
  );

-- DELETE: Apenas soft delete via UPDATE (status = CANCELLED)
-- Sem política de DELETE real

Tabela: payments

sql
ALTER TABLE booking.payments ENABLE ROW LEVEL SECURITY;
ALTER TABLE booking.payments FORCE ROW LEVEL SECURITY;

-- SELECT: Ver pagamentos de reservas acessíveis
CREATE POLICY payments_select_policy ON booking.payments
  FOR SELECT
  USING (
    is_super_admin()
    OR EXISTS (
      SELECT 1 FROM booking.bookings b
      WHERE b.id = payments.booking_id
      AND (
        b.user_id = current_user_id()
        OR (
          user_has_arena_access(b.arena_id)
          AND (has_role('ARENA_OWNER') OR has_role('ARENA_MANAGER'))
        )
      )
    )
  );

-- INSERT: Sistema ou manager da arena
CREATE POLICY payments_insert_policy ON booking.payments
  FOR INSERT
  WITH CHECK (
    is_super_admin()
    OR EXISTS (
      SELECT 1 FROM booking.bookings b
      WHERE b.id = payments.booking_id
      AND user_has_arena_access(b.arena_id)
      AND (has_role('ARENA_OWNER') OR has_role('ARENA_MANAGER'))
    )
  );

-- UPDATE: Apenas sistema (gateway callbacks)
CREATE POLICY payments_update_policy ON booking.payments
  FOR UPDATE
  USING (is_super_admin());

3.4 Schema: Public

Tabela: audit_logs

sql
ALTER TABLE public.audit_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.audit_logs FORCE ROW LEVEL SECURITY;

-- SELECT: Ver logs do próprio tenant ou super admin
CREATE POLICY audit_logs_select_policy ON public.audit_logs
  FOR SELECT
  USING (
    is_super_admin()
    OR tenant_id = current_tenant_id()
  );

-- INSERT: Sistema pode inserir logs
CREATE POLICY audit_logs_insert_policy ON public.audit_logs
  FOR INSERT
  WITH CHECK (
    is_super_admin()
    OR tenant_id = current_tenant_id()
  );

-- UPDATE/DELETE: Ninguém pode modificar audit logs
-- (Sem políticas = bloqueia por padrão)

4. Bypass para Serviços

4.1 Role de Serviço

sql
-- Cria role para serviços internos (workers, migrations)
CREATE ROLE app_service_role NOLOGIN;

-- Concede permissões completas
GRANT ALL ON ALL TABLES IN SCHEMA identity TO app_service_role;
GRANT ALL ON ALL TABLES IN SCHEMA arena TO app_service_role;
GRANT ALL ON ALL TABLES IN SCHEMA booking TO app_service_role;
GRANT ALL ON ALL TABLES IN SCHEMA public TO app_service_role;

-- Bypass RLS para serviços
ALTER ROLE app_service_role BYPASSRLS;

-- Cria usuário de serviço
CREATE USER app_worker WITH PASSWORD 'secure-password';
GRANT app_service_role TO app_worker;

4.2 Função de Contexto para Serviços

sql
-- Função para executar como outro tenant (apenas serviços)
CREATE OR REPLACE FUNCTION execute_as_tenant(
  p_tenant_id UUID,
  p_query TEXT
)
RETURNS VOID AS $$
BEGIN
  -- Apenas app_service_role pode usar esta função
  IF NOT pg_has_role(current_user, 'app_service_role', 'MEMBER') THEN
    RAISE EXCEPTION 'Permission denied';
  END IF;

  -- Define o contexto do tenant
  PERFORM set_config('app.current_tenant_id', p_tenant_id::TEXT, true);

  -- Executa a query
  EXECUTE p_query;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

5. Middleware de Aplicação

5.1 NestJS Interceptor

typescript
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { PrismaService } from './prisma.service';

@Injectable()
export class TenantContextInterceptor implements NestInterceptor {
  constructor(private prisma: PrismaService) {}

  async intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Promise<Observable<any>> {
    const request = context.switchToHttp().getRequest();
    const user = request.user;

    if (user) {
      // Define variáveis de sessão no PostgreSQL
      await this.prisma.$executeRawUnsafe(`
        SET LOCAL app.current_tenant_id = '${user.tenantId}';
        SET LOCAL app.current_user_id = '${user.id}';
        SET LOCAL app.current_user_roles = '${JSON.stringify(user.roles)}';
      `);
    }

    return next.handle();
  }
}

5.2 Prisma Middleware

typescript
import { Prisma } from '@prisma/client';

export function tenantMiddleware(): Prisma.Middleware {
  return async (params, next) => {
    // Adiciona tenant_id automaticamente em creates
    if (params.action === 'create' || params.action === 'createMany') {
      const tenantId = getCurrentTenantId();

      if (tenantId && hasTenantIdField(params.model)) {
        if (params.action === 'create') {
          params.args.data.tenantId = tenantId;
        } else {
          params.args.data = params.args.data.map((d: any) => ({
            ...d,
            tenantId,
          }));
        }
      }
    }

    // Adiciona filtro de tenant automaticamente em reads
    if (['findMany', 'findFirst', 'findUnique', 'count'].includes(params.action)) {
      const tenantId = getCurrentTenantId();

      if (tenantId && hasTenantIdField(params.model)) {
        params.args.where = {
          ...params.args.where,
          tenantId,
        };
      }
    }

    return next(params);
  };
}

6. Testes de Segurança

6.1 Testes de Isolamento

sql
-- Teste: Usuário do Tenant A não pode ver dados do Tenant B
DO $$
DECLARE
  tenant_a_id UUID := 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
  tenant_b_id UUID := 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
  count_result INT;
BEGIN
  -- Setup: Simula usuário do Tenant A
  PERFORM set_config('app.current_tenant_id', tenant_a_id::TEXT, true);
  PERFORM set_config('app.current_user_roles', '["ARENA_OWNER"]', true);

  -- Tenta contar arenas do Tenant B
  SELECT COUNT(*) INTO count_result
  FROM arena.arenas
  WHERE tenant_id = tenant_b_id;

  -- Deve retornar 0 (RLS bloqueia)
  IF count_result > 0 THEN
    RAISE EXCEPTION 'SECURITY VIOLATION: Tenant A can see Tenant B data!';
  END IF;

  RAISE NOTICE 'TEST PASSED: Tenant isolation working correctly';
END $$;

6.2 Testes de Roles

sql
-- Teste: Receptionist não pode deletar quadras
DO $$
DECLARE
  test_court_id UUID := 'cccccccc-cccc-cccc-cccc-cccccccccccc';
BEGIN
  -- Setup: Simula receptionist
  PERFORM set_config('app.current_user_roles', '["RECEPTIONIST"]', true);

  -- Tenta deletar quadra
  BEGIN
    DELETE FROM arena.courts WHERE id = test_court_id;
    RAISE EXCEPTION 'SECURITY VIOLATION: Receptionist could delete court!';
  EXCEPTION
    WHEN insufficient_privilege THEN
      RAISE NOTICE 'TEST PASSED: Receptionist correctly blocked from delete';
  END;
END $$;

7. Monitoramento

7.1 Query para Auditoria de RLS

sql
-- Verifica políticas ativas
SELECT
  schemaname,
  tablename,
  policyname,
  permissive,
  roles,
  cmd,
  qual,
  with_check
FROM pg_policies
WHERE schemaname IN ('identity', 'arena', 'booking', 'public')
ORDER BY schemaname, tablename, policyname;

7.2 Alertas de Violação

sql
-- Log de tentativas de acesso bloqueadas
CREATE OR REPLACE FUNCTION log_rls_violation()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO public.security_logs (
    event_type,
    user_id,
    tenant_id,
    table_name,
    attempted_action,
    details,
    created_at
  ) VALUES (
    'RLS_VIOLATION',
    current_user_id(),
    current_tenant_id(),
    TG_TABLE_NAME,
    TG_OP,
    jsonb_build_object(
      'row_id', COALESCE(NEW.id, OLD.id)
    ),
    NOW()
  );

  RETURN NULL; -- Bloqueia a operação
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

8. Manutenção

8.1 Script de Verificação

bash
#!/bin/bash
# verify-rls-policies.sh

echo "Verificando políticas RLS..."

psql -d sporttechclub -c "
SELECT
  schemaname || '.' || tablename as table,
  CASE WHEN rowsecurity THEN 'ENABLED' ELSE 'DISABLED' END as rls_status,
  COUNT(policyname) as policy_count
FROM pg_tables t
LEFT JOIN pg_policies p ON t.tablename = p.tablename
WHERE t.schemaname IN ('identity', 'arena', 'booking')
GROUP BY schemaname, tablename, rowsecurity
ORDER BY schemaname, tablename;
"

8.2 Rollback de Políticas

sql
-- Em caso de emergência: desabilita RLS temporariamente
-- ATENÇÃO: Use apenas em situações críticas!

-- Desabilita RLS em todas as tabelas
DO $$
DECLARE
  r RECORD;
BEGIN
  FOR r IN SELECT schemaname, tablename
           FROM pg_tables
           WHERE schemaname IN ('identity', 'arena', 'booking')
  LOOP
    EXECUTE format('ALTER TABLE %I.%I DISABLE ROW LEVEL SECURITY',
                   r.schemaname, r.tablename);
  END LOOP;
END $$;

-- Para reabilitar:
DO $$
DECLARE
  r RECORD;
BEGIN
  FOR r IN SELECT schemaname, tablename
           FROM pg_tables
           WHERE schemaname IN ('identity', 'arena', 'booking')
  LOOP
    EXECUTE format('ALTER TABLE %I.%I ENABLE ROW LEVEL SECURITY',
                   r.schemaname, r.tablename);
  END LOOP;
END $$;

9. Checklist de Implementação

  • [ ] Funções de contexto criadas
  • [ ] RLS habilitado em todas as tabelas
  • [ ] Políticas SELECT implementadas
  • [ ] Políticas INSERT implementadas
  • [ ] Políticas UPDATE implementadas
  • [ ] Políticas DELETE implementadas
  • [ ] Role de serviço configurada
  • [ ] Middleware de aplicação implementado
  • [ ] Testes de isolamento executados
  • [ ] Testes de roles executados
  • [ ] Monitoramento configurado
  • [ ] Documentação atualizada