docs(rls): Add SQL script and RLS setup guide - Tasks table with Row Level Security
This commit is contained in:
201
backend-api/RLS_SETUP_GUIDE.md
Normal file
201
backend-api/RLS_SETUP_GUIDE.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# 🔐 Implementar RLS no Supabase - Guia Passo a Passo
|
||||
|
||||
## 📌 O que é RLS (Row Level Security)?
|
||||
|
||||
Row Level Security é um mecanismo de segurança no PostgreSQL que permite controlar qual linhas de dados cada usuário pode acessar. No nosso caso, garante que:
|
||||
|
||||
✅ **Usuário A** pode ver/editar apenas suas tarefas
|
||||
✅ **Usuário B** pode ver/editar apenas suas tarefas
|
||||
❌ **Usuário A NÃO** consegue acessar tarefas de **Usuário B**
|
||||
|
||||
## 🚀 Como Implementar no Supabase
|
||||
|
||||
### Passo 1: Acessar o SQL Editor do Supabase
|
||||
|
||||
1. Vá para [https://supabase.com/dashboard](https://supabase.com/dashboard)
|
||||
2. Selecione seu projeto **"todolist-fullstack"**
|
||||
3. No menu lateral, clique em **SQL Editor**
|
||||
4. Clique em **New Query**
|
||||
|
||||
### Passo 2: Copiar o Script SQL
|
||||
|
||||
1. Abra o arquivo: `backend-api/SQL_TASKS_TABLE_RLS.sql`
|
||||
2. Copie todo o conteúdo
|
||||
3. Cole no editor SQL do Supabase
|
||||
|
||||
### Passo 3: Executar o Script
|
||||
|
||||
1. Clique no botão **▶️ RUN** (canto inferior direito)
|
||||
2. Aguarde a execução completar (deve levar 2-5 segundos)
|
||||
3. Verifique se não houve erros (aparecerá "Successfully" em verde)
|
||||
|
||||
### Passo 4: Verificar a Tabela Criada
|
||||
|
||||
1. Vá para **Database** → **Tables** no menu lateral
|
||||
2. Você deve ver a tabela `tasks` listada
|
||||
3. Clique nela para visualizar:
|
||||
- Schema das colunas
|
||||
- Índices criados
|
||||
- Políticas RLS (abas "Row Level Security")
|
||||
|
||||
## 📋 O que o Script Cria
|
||||
|
||||
### 1️⃣ Tabela `tasks`
|
||||
|
||||
| Coluna | Tipo | Descrição |
|
||||
|--------|------|-----------|
|
||||
| `id` | UUID | ID único (auto-gerado) |
|
||||
| `user_id` | UUID | Referência ao usuário (auth.users) |
|
||||
| `title` | VARCHAR(255) | Título obrigatório |
|
||||
| `description` | TEXT | Descrição opcional |
|
||||
| `completed` | BOOLEAN | Status de conclusão |
|
||||
| `due_date` | TIMESTAMP | Data de vencimento |
|
||||
| `category` | VARCHAR(50) | Categoria para organização |
|
||||
| `priority` | VARCHAR(20) | low \| medium \| high |
|
||||
| `created_at` | TIMESTAMP | Auto-preenchido |
|
||||
| `updated_at` | TIMESTAMP | Auto-atualizado |
|
||||
|
||||
### 2️⃣ Índices para Performance
|
||||
|
||||
```sql
|
||||
idx_tasks_user_id -- Buscar tarefas por usuário
|
||||
idx_tasks_completed -- Filtrar concluídas/pendentes
|
||||
idx_tasks_created_at -- Ordenar por data de criação
|
||||
idx_tasks_due_date -- Ordenar por vencimento
|
||||
idx_tasks_user_completed -- Query combinada (comum)
|
||||
```
|
||||
|
||||
### 3️⃣ Row Level Security (RLS) - 4 Políticas
|
||||
|
||||
#### ✅ SELECT - Ver tarefas próprias
|
||||
```sql
|
||||
auth.uid() = user_id
|
||||
```
|
||||
Usuário vê apenas suas tarefas.
|
||||
|
||||
#### ✅ INSERT - Criar tarefas próprias
|
||||
```sql
|
||||
auth.uid() = user_id
|
||||
```
|
||||
Usuário só consegue inserir tarefa com seu `user_id`.
|
||||
|
||||
#### ✅ UPDATE - Editar tarefas próprias
|
||||
```sql
|
||||
auth.uid() = user_id
|
||||
```
|
||||
Usuário só consegue atualizar suas tarefas.
|
||||
|
||||
#### ✅ DELETE - Deletar tarefas próprias
|
||||
```sql
|
||||
auth.uid() = user_id
|
||||
```
|
||||
Usuário só consegue deletar suas tarefas.
|
||||
|
||||
### 4️⃣ Trigger para `updated_at`
|
||||
|
||||
Atualiza o campo `updated_at` automaticamente toda vez que uma tarefa é modificada.
|
||||
|
||||
## 🧪 Testar a Implementação
|
||||
|
||||
### Teste 1: Verificar RLS Ativado
|
||||
|
||||
No SQL Editor, execute:
|
||||
|
||||
```sql
|
||||
SELECT schemaname, tablename, rowsecurity
|
||||
FROM pg_tables
|
||||
WHERE tablename = 'tasks';
|
||||
```
|
||||
|
||||
**Esperado:**
|
||||
```
|
||||
schemaname | tablename | rowsecurity
|
||||
----------|-----------|-----------
|
||||
public | tasks | true
|
||||
```
|
||||
|
||||
### Teste 2: Listar Políticas RLS
|
||||
|
||||
```sql
|
||||
SELECT tablename, policyname
|
||||
FROM pg_policies
|
||||
WHERE tablename = 'tasks'
|
||||
ORDER BY policyname;
|
||||
```
|
||||
|
||||
**Esperado:**
|
||||
```
|
||||
tablename | policyname
|
||||
-----------|------------------------------------------
|
||||
tasks | Users can create their own tasks
|
||||
tasks | Users can delete their own tasks
|
||||
tasks | Users can update their own tasks
|
||||
tasks | Users can view their own tasks
|
||||
```
|
||||
|
||||
### Teste 3: Testar Isolamento (via Supabase Dashboard)
|
||||
|
||||
1. Vá para **Authentication** → **Users** no dashboard
|
||||
2. Crie 2 usuários de teste:
|
||||
- `user1@test.com` / `password123`
|
||||
- `user2@test.com` / `password123`
|
||||
|
||||
3. Autentique como `user1` no frontend
|
||||
4. Crie uma tarefa para `user1`
|
||||
5. Autentique como `user2` no frontend
|
||||
6. Verifique que `user2` NÃO consegue ver a tarefa de `user1`
|
||||
|
||||
## ⚠️ Troubleshooting
|
||||
|
||||
### "Error: relation tasks does not exist"
|
||||
- Verifique se você copiou o script inteiro
|
||||
- Certifique-se de executar o script completo de uma vez
|
||||
|
||||
### "Error: policy already exists"
|
||||
- Você está executando o script duas vezes
|
||||
- Solução: Execute apenas uma vez ou delete as políticas primeiro
|
||||
|
||||
### RLS não está funcionando
|
||||
- Verifique se RLS está **habilitado** (não desabilitado)
|
||||
- Confirme que o `user_id` na tabela corresponde ao `auth.uid()` do usuário autenticado
|
||||
|
||||
## 🔗 Integração com Backend (NestJS)
|
||||
|
||||
O TasksService já está preparado para trabalhar com RLS:
|
||||
|
||||
```typescript
|
||||
// O Supabase valida automaticamente:
|
||||
// - Usuário só acessa suas tarefas (RLS)
|
||||
// - Não consegue inserir tarefa com outro user_id
|
||||
// - Não consegue atualizar/deletar tarefa de outro usuário
|
||||
|
||||
async findAll(userId: string) {
|
||||
const { data, error } = await this.supabaseService
|
||||
.getClient()
|
||||
.from('tasks')
|
||||
.select('*')
|
||||
.eq('user_id', userId); // ← RLS já filtra automaticamente
|
||||
|
||||
return data;
|
||||
}
|
||||
```
|
||||
|
||||
## 📚 Referências Úteis
|
||||
|
||||
- 📖 [Supabase RLS Guide](https://supabase.com/docs/guides/auth/row-level-security)
|
||||
- 🔒 [PostgreSQL Security](https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-RLS)
|
||||
- 🚀 [Best Practices](https://supabase.com/docs/guides/database/basics/best-practices)
|
||||
|
||||
## ✅ Próximos Passos
|
||||
|
||||
Após executar o script SQL:
|
||||
|
||||
1. ✅ Executar SQL no Supabase
|
||||
2. ✅ Verificar tabela criada
|
||||
3. ➡️ **Próximo**: Testar endpoints de tasks via Postman/Insomnia
|
||||
4. ➡️ **Depois**: Implementar Frontend (Next.js)
|
||||
|
||||
---
|
||||
|
||||
**Status**: 🟡 Aguardando execução manual no Supabase
|
||||
**Responsável**: Você (execute o SQL_TASKS_TABLE_RLS.sql no SQL Editor)
|
||||
160
backend-api/SQL_TASKS_TABLE_RLS.sql
Normal file
160
backend-api/SQL_TASKS_TABLE_RLS.sql
Normal file
@@ -0,0 +1,160 @@
|
||||
-- ============================================================================
|
||||
-- 📊 TASK MANAGER - Supabase Database Schema com Row Level Security (RLS)
|
||||
-- ============================================================================
|
||||
-- Este script cria a tabela 'tasks' e implementa RLS para garantir que
|
||||
-- cada usuário acesse apenas suas próprias tarefas.
|
||||
--
|
||||
-- Executar em: Supabase SQL Editor
|
||||
-- Ambiente: Development ou Production (conforme necessário)
|
||||
-- ============================================================================
|
||||
|
||||
-- ============================================================================
|
||||
-- 1️⃣ CRIAR TABELA TASKS
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.tasks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- 🔗 Referência ao usuário
|
||||
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
|
||||
-- 📝 Dados da tarefa
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
completed BOOLEAN DEFAULT FALSE,
|
||||
|
||||
-- 📅 Datas
|
||||
due_date TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 🏷️ Categorização e prioridade
|
||||
category VARCHAR(50),
|
||||
priority VARCHAR(20) DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high')),
|
||||
|
||||
-- 🔍 Índices para performance
|
||||
CONSTRAINT title_not_empty CHECK (char_length(title) >= 3)
|
||||
);
|
||||
|
||||
-- Criar índices para melhor performance
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_user_id ON public.tasks(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_completed ON public.tasks(completed);
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_created_at ON public.tasks(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON public.tasks(due_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_user_completed ON public.tasks(user_id, completed);
|
||||
|
||||
-- ============================================================================
|
||||
-- 2️⃣ HABILITAR RLS (Row Level Security)
|
||||
-- ============================================================================
|
||||
|
||||
-- Ativar RLS na tabela
|
||||
ALTER TABLE public.tasks ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- ============================================================================
|
||||
-- 3️⃣ CRIAR POLÍTICAS DE SEGURANÇA
|
||||
-- ============================================================================
|
||||
|
||||
-- ✅ Política 1: Usuários podem ver apenas suas próprias tarefas (SELECT)
|
||||
CREATE POLICY "Users can view their own tasks"
|
||||
ON public.tasks
|
||||
FOR SELECT
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
-- ✅ Política 2: Usuários podem inserir tarefas apenas para si mesmos (INSERT)
|
||||
CREATE POLICY "Users can create their own tasks"
|
||||
ON public.tasks
|
||||
FOR INSERT
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
-- ✅ Política 3: Usuários podem atualizar apenas suas próprias tarefas (UPDATE)
|
||||
CREATE POLICY "Users can update their own tasks"
|
||||
ON public.tasks
|
||||
FOR UPDATE
|
||||
USING (auth.uid() = user_id)
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
-- ✅ Política 4: Usuários podem deletar apenas suas próprias tarefas (DELETE)
|
||||
CREATE POLICY "Users can delete their own tasks"
|
||||
ON public.tasks
|
||||
FOR DELETE
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- 4️⃣ CRIAR FUNÇÃO PARA ATUALIZAR updated_at (TRIGGER)
|
||||
-- ============================================================================
|
||||
|
||||
-- Criar função que atualiza o campo updated_at automaticamente
|
||||
CREATE OR REPLACE FUNCTION public.update_tasks_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Criar trigger que dispara a função antes de atualizar
|
||||
DROP TRIGGER IF EXISTS trigger_update_tasks_updated_at ON public.tasks;
|
||||
CREATE TRIGGER trigger_update_tasks_updated_at
|
||||
BEFORE UPDATE ON public.tasks
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.update_tasks_updated_at();
|
||||
|
||||
-- ============================================================================
|
||||
-- 5️⃣ CRIAR TIPOS (TypeScript Integration)
|
||||
-- ============================================================================
|
||||
|
||||
-- Comentários para documentação e integração com TypeScript
|
||||
COMMENT ON TABLE public.tasks IS 'Tabela de tarefas com Row Level Security habilitado';
|
||||
COMMENT ON COLUMN public.tasks.id IS 'UUID único da tarefa (auto-gerado)';
|
||||
COMMENT ON COLUMN public.tasks.user_id IS 'UUID do usuário proprietário da tarefa';
|
||||
COMMENT ON COLUMN public.tasks.title IS 'Título da tarefa (obrigatório, 3-255 caracteres)';
|
||||
COMMENT ON COLUMN public.tasks.description IS 'Descrição detalhada (opcional, até 2000 caracteres)';
|
||||
COMMENT ON COLUMN public.tasks.completed IS 'Flag indicando se tarefa está concluída';
|
||||
COMMENT ON COLUMN public.tasks.due_date IS 'Data/hora de vencimento da tarefa';
|
||||
COMMENT ON COLUMN public.tasks.category IS 'Categoria para organização (ex: trabalho, pessoal)';
|
||||
COMMENT ON COLUMN public.tasks.priority IS 'Prioridade: low, medium, high';
|
||||
COMMENT ON COLUMN public.tasks.created_at IS 'Timestamp de criação (auto-preenchido)';
|
||||
COMMENT ON COLUMN public.tasks.updated_at IS 'Timestamp da última atualização (auto-preenchido)';
|
||||
|
||||
-- ============================================================================
|
||||
-- 6️⃣ VERIFICAÇÃO - QUERYS PARA TESTAR
|
||||
-- ============================================================================
|
||||
|
||||
-- 🧪 TESTE 1: Verificar se RLS está ativado
|
||||
-- SELECT schemaname, tablename, rowsecurity
|
||||
-- FROM pg_tables
|
||||
-- WHERE tablename = 'tasks';
|
||||
-- Esperado: rowsecurity = true
|
||||
|
||||
-- 🧪 TESTE 2: Listar todas as políticas
|
||||
-- SELECT tablename, policyname, qual, with_check
|
||||
-- FROM pg_policies
|
||||
-- WHERE tablename = 'tasks';
|
||||
-- Esperado: 4 políticas
|
||||
|
||||
-- 🧪 TESTE 3: Testar isolamento (executar como usuário autenticado)
|
||||
-- SELECT * FROM tasks; -- Mostra apenas tarefas do usuário atual
|
||||
-- INSERT INTO tasks (user_id, title) VALUES (auth.uid(), 'Nova tarefa');
|
||||
-- UPDATE tasks SET completed = true WHERE id = '...';
|
||||
-- DELETE FROM tasks WHERE id = '...';
|
||||
|
||||
-- ============================================================================
|
||||
-- 🎯 RESUMO DAS POLÍTICAS RLS
|
||||
-- ============================================================================
|
||||
-- ┌─────────┬────────────────────────────────────────────┐
|
||||
-- │ AÇÃO │ PERMISSÃO │
|
||||
-- ├─────────┼────────────────────────────────────────────┤
|
||||
-- │ SELECT │ ✅ Ver tarefas próprias │
|
||||
-- │ INSERT │ ✅ Criar tarefas com user_id próprio │
|
||||
-- │ UPDATE │ ✅ Atualizar apenas tarefas próprias │
|
||||
-- │ DELETE │ ✅ Deletar apenas tarefas próprias │
|
||||
-- │ ADMIN │ ✅ Admin pode sem RLS (role: service_role)│
|
||||
-- └─────────┴────────────────────────────────────────────┘
|
||||
|
||||
-- ============================================================================
|
||||
-- 📚 REFERÊNCIAS
|
||||
-- ============================================================================
|
||||
-- - Supabase RLS: https://supabase.com/docs/guides/auth/row-level-security
|
||||
-- - PostgreSQL Triggers: https://www.postgresql.org/docs/current/sql-createtrigger.html
|
||||
-- - JWT Claims: https://supabase.com/docs/guides/auth/jwts
|
||||
-- - Índices PostgreSQL: https://www.postgresql.org/docs/current/sql-createindex.html
|
||||
Reference in New Issue
Block a user