---
id: db-policy-apply
agent: data-engineer
title: Instalar política RLS numa tabela
inputs: [table, mode, story]
outputs: [política RLS aplicada, casos de teste positivo/negativo, snapshot]
elicit: false
modes: [interactive, yolo]
---

# Instalar política RLS numa tabela

**Objetivo:** habilitar Row Level Security e instalar a política de acesso de uma tabela — no modo
KISS (dono-só) ou granular — de forma idempotente e reversível, rastreando à story/spec.

**Pré-condições:**
- A tabela existe e tem a coluna de posse esperada (ex.: `user_id`/`owner_id`). Se não tiver, **pare**
  e devolva ao design de schema — RLS sem coluna de posse não tem como filtrar.
- Existe camada de auth (Supabase Auth ou equivalente). Sem ela, `auth.uid()` retorna NULL — aviso
  explicitamente que a política vai negar tudo.
- O `mode` é `kiss` (dono lê/escreve só o que é dele) ou `granular` (regras por operação/role). Se
  ambíguo, elicito — não invento a regra de acesso.

## Passos

1. **Snapshot antes de tocar** (`*snapshot policy-{table}`): RLS é mudança de schema; preciso de ponto
   de rollback antes de qualquer `ALTER`.
2. **Confirme a regra de acesso na story/spec.** Quem pode ler? Quem pode escrever? Há leitura pública?
   A regra rastreia a um requisito — não deduzo permissão por conta própria.
3. **Habilite RLS idempotente:** `ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;` (seguro repetir).
   Sem isso a tabela fica aberta mesmo com política definida.
4. **Escreva a(s) política(s)** com `DROP POLICY IF EXISTS ... ; CREATE POLICY ...` para idempotência:
   - **kiss:** uma política `USING (auth.uid() = user_id)` + `WITH CHECK` igual, cobrindo SELECT/
     INSERT/UPDATE/DELETE do dono.
   - **granular:** políticas separadas por comando/role, cada uma justificada pela regra de acesso.
5. **Aplique em transação** (via `*run-sql` ou `*apply-migration`): `BEGIN; ... COMMIT;`. Erro =
   `ROLLBACK` automático, tabela intacta.
6. **Valide com `*test-as-user`** (positivo E negativo): o dono enxerga o dele; um terceiro NÃO enxerga
   nem altera. Política sem teste negativo é política não-comprovada.
7. **Registre** a política e os casos de teste no rastro da story (File List / nota de banco).

## Critério de pronto (DoD)

- [ ] RLS habilitada na tabela e política(s) criada(s) de forma idempotente
- [ ] Teste positivo passa (dono acessa o dele) e teste negativo passa (terceiro é bloqueado)
- [ ] Snapshot criado antes da aplicação; rollback possível
- [ ] Regra de acesso rastreia à story/spec — nada inventado
- [ ] Aplicação rodou em transação (sem estado parcial)

## Falha / recuperação

- **`auth.uid()` é NULL (sem auth)** → política nega tudo; **pare**, sinalize a dependência de auth e
  não declare pronto.
- **Teste negativo falha (terceiro enxerga o dado)** → política está vazando; reverto via snapshot,
  corrijo o predicado e revalido. Não entrego com vazamento.
- **Erro na aplicação** → `ROLLBACK` deixou a tabela intacta; investigo o DDL e repito. Se o snapshot
  for necessário, `*rollback policy-{table}`.
- **Subir a mudança (push/PR)** → delego ao @devops. Eu aplico no banco, mas não faço push.
