Ir al contenido

Reglas de Seguridad (RLS)

Las reglas de seguridad le permiten definir políticas de Seguridad a Nivel de Fila (RLS) para sus tablas PostgreSQL directamente en las definiciones de sus colecciones. Cuando se genera el esquema Drizzle, Rebase crea las sentencias CREATE POLICY correspondientes.

const postsCollection: EntityCollection = {
slug: "posts",
table: "posts",
properties: { /* ... */ },
securityRules: [
{ operation: "select", access: "public" },
{ operations: ["insert", "update", "delete"], ownerField: "author_id" }
]
};
  1. Usted define securityRules en una colección
  2. rebase schema generate crea el esquema Drizzle con RLS habilitado
  3. rebase db push o rebase db migrate aplica las políticas a PostgreSQL
  4. Cada consulta es filtrada automáticamente por el contexto del usuario actual

La identidad del usuario autenticado está disponible en SQL a través de:

FunciónDevuelve
auth.uid()El ID del usuario actual
auth.roles()IDs de roles de aplicación separados por comas
auth.jwt()Reclamaciones JWT completas como JSONB

Estos se establecen automáticamente por transacción por el backend de Rebase.

El patrón más simple: los usuarios solo pueden acceder a las filas que poseen:

securityRules: [
{ operation: "all", ownerField: "user_id" }
]

Esto genera: USING (user_id = auth.uid())

Permitir que cualquiera (incluidos los usuarios no autenticados) lea:

securityRules: [
{ operation: "select", access: "public" }
]

Esto genera: USING (true)

Permitir a cualquier usuario autenticado:

securityRules: [
{ operation: "select", access: "authenticated" }
]

Restringir operaciones a roles específicos:

securityRules: [
{ operation: "all", roles: ["admin"] },
{ operation: "select", roles: ["editor", "viewer"] }
]

Para lógica compleja, utilice using y withCheck:

securityRules: [
{
operation: "select",
using: "EXISTS (SELECT 1 FROM org_members WHERE org_members.org_id = {org_id} AND org_members.user_id = auth.uid())"
}
]
  • using — Filtra qué filas existentes son visibles (se aplica a SELECT, UPDATE, DELETE)
  • withCheck — Valida nuevos valores de fila (se aplica a INSERT, UPDATE)

Las referencias a columnas utilizan la sintaxis {nombre_de_columna} que se resuelve a la columna completa cualificada por la tabla.

Combine atajos de conveniencia con SQL puro:

securityRules: [
// Los administradores pueden hacer cualquier cosa
{ operation: "all", roles: ["admin"], using: "true" },
// Los usuarios regulares solo pueden ver sus propias filas
{ operation: "select", ownerField: "user_id" },
// Los usuarios pueden insertar, pero solo para sí mismos
{ operation: "insert", withCheck: "{user_id} = auth.uid()" },
// Las filas bloqueadas no se pueden actualizar
{ operation: "update", mode: "restrictive", using: "{is_locked} = false" }
]

PostgreSQL tiene dos modos de política:

  • Permisivo (predeterminado) — Múltiples políticas permisivas se combinan con OR. Si alguna de ellas se cumple, se concede el acceso.
  • Restrictivo — Las políticas restrictivas se combinan con AND. Todas deben cumplirse.
securityRules: [
// Permisivo: los propietarios pueden acceder a sus filas
{ operation: "all", ownerField: "user_id" },
// Restrictivo: pero las filas bloqueadas no se pueden actualizar
{ operation: "update", mode: "restrictive", using: "{is_locked} = false", withCheck: "{is_locked} = false" }
]
OperaciónEquivalente SQLDescripción
"select"SELECTLeer filas
"insert"INSERTCrear nuevas filas
"update"UPDATEModificar filas existentes
"delete"DELETEEliminar filas
"all"Todas las anterioresAbreviatura para todas las operaciones

También puede usar operations (plural) para aplicar una regla a múltiples operaciones:

{ operations: ["insert", "update", "delete"], ownerField: "author_id" }
interface SecurityRule {
name?: string; // Nombre de política legible por humanos
operation?: SecurityOperation; // Operación única
operations?: SecurityOperation[]; // Múltiples operaciones
mode?: "permissive" | "restrictive"; // Predeterminado: "permissive"
access?: "public" | "authenticated";
ownerField?: string; // Columna que contiene el ID de usuario del propietario
roles?: string[]; // Roles de aplicación a los que se aplica esta política
using?: string; // Expresión SQL USING pura
withCheck?: string; // Expresión SQL WITH CHECK pura
}
securityRules: [
// Cualquiera puede leer publicaciones publicadas
{ operation: "select", access: "public", using: "{status} = 'published'" },
// Los autores pueden ver sus propios borradores
{ operation: "select", ownerField: "author_id" },
// Los autores pueden crear y editar sus propias publicaciones
{ operations: ["insert", "update"], ownerField: "author_id" },
// Solo los administradores pueden eliminar
{ operation: "delete", roles: ["admin"] }
]
securityRules: [
{
operation: "all",
using: "EXISTS (SELECT 1 FROM org_members WHERE org_members.org_id = {org_id} AND org_members.user_id = auth.uid())"
}
]

Una necesidad común es permitir que usuarios no autenticados envíen datos — formularios de contacto, suscripciones a boletines, aplicaciones públicas. Rebase proporciona un patrón limpio para esto.

const contactMessagesCollection: EntityCollection = {
slug: "contact_messages",
securityRules: [
// Cualquiera puede enviar un mensaje de contacto
{
operation: "insert",
access: "public",
withCheck: "true"
},
// Solo los administradores pueden leer, actualizar o eliminar mensajes
{ operations: ["select", "update", "delete"], roles: ["admin"] }
],
properties: { /* ... */ }
};

El atajo access: "public" genera una política que permite la operación sin requerir autenticación.

const leadSignupsCollection: EntityCollection = {
slug: "lead_magnet_signups",
securityRules: [
// Permitir inserciones anónimas
{ operation: "insert", access: "public", withCheck: "true" },
// Los administradores pueden ver todos los registros
{ operation: "select", roles: ["admin"] }
],
properties: { /* ... */ }
};

Cuando una solicitud llega sin un token JWT, el backend de Rebase establece las variables de sesión de PostgreSQL a:

VariableValor
app.user_id'anonymous'
app.user_roles'' (vacío)

Esto significa:

  • auth.uid() devuelve 'anonymous'
  • auth.roles() devuelve una cadena vacía
  • Las políticas access: "public" pasan porque generan USING (true) / WITH CHECK (true)
  • Las políticas access: "authenticated" fallan porque verifican un ID de usuario real
  • Las políticas ownerField fallan porque ninguna fila tendrá user_id = 'anonymous' (a menos que se establezca explícitamente)

Si necesita un control más granular, utilice SQL puro:

securityRules: [
{
operation: "insert",
withCheck: "auth.uid() = 'anonymous' OR auth.uid() IS NOT NULL"
}
]