Aller au contenu

Règles de Sécurité (RLS)

Les règles de sécurité vous permettent de définir des politiques de sécurité au niveau des lignes (RLS) pour vos tables PostgreSQL directement dans vos définitions de collection. Lorsque le schéma Drizzle est généré, Rebase crée les instructions CREATE POLICY correspondantes.

const postsCollection: EntityCollection = {
slug: "posts",
table: "posts",
properties: { /* ... */ },
securityRules: [
{ operation: "select", access: "public" },
{ operations: ["insert", "update", "delete"], ownerField: "author_id" }
]
};
  1. Vous définissez des securityRules sur une collection
  2. rebase schema generate crée un schéma Drizzle avec RLS activé
  3. rebase db push ou rebase db migrate applique les politiques à PostgreSQL
  4. Chaque requête est automatiquement filtrée par le contexte de l’utilisateur actuel

L’identité de l’utilisateur authentifié est disponible en SQL via :

FonctionRenvoie
auth.uid()L’ID de l’utilisateur actuel
auth.roles()ID de rôles d’application séparés par des virgules
auth.jwt()Revendications JWT complètes en JSONB

Ceux-ci sont définis automatiquement par transaction par le backend Rebase.

Le modèle le plus simple — les utilisateurs ne peuvent accéder qu’aux lignes qu’ils possèdent :

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

Ceci génère : USING (user_id = auth.uid())

Permettre à quiconque (y compris les utilisateurs non authentifiés) de lire :

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

Ceci génère : USING (true)

Permettre à tout utilisateur authentifié :

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

Restreindre les opérations à des rôles spécifiques :

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

Pour une logique complexe, utilisez using et 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 — Filtre les lignes existantes visibles (s’applique à SELECT, UPDATE, DELETE)
  • withCheck — Valide les nouvelles valeurs de ligne (s’applique à INSERT, UPDATE)

Les références de colonnes utilisent la syntaxe {column_name} qui est résolue en colonne entièrement qualifiée par la table.

Mélangez les raccourcis pratiques avec du SQL brut :

securityRules: [
// Les administrateurs peuvent tout faire
{ operation: "all", roles: ["admin"], using: "true" },
// Les utilisateurs réguliers ne peuvent voir que leurs propres lignes
{ operation: "select", ownerField: "user_id" },
// Les utilisateurs peuvent insérer, mais uniquement pour eux-mêmes
{ operation: "insert", withCheck: "{user_id} = auth.uid()" },
// Les lignes verrouillées ne peuvent pas être mises à jour
{ operation: "update", mode: "restrictive", using: "{is_locked} = false" }
]

PostgreSQL a deux modes de politique :

  • Permissif (par défaut) — Plusieurs politiques permissives sont combinées avec un OU. Si l’une d’elles passe, l’accès est accordé.
  • Restrictif — Les politiques restrictives sont combinées avec un ET. Toutes doivent passer.
securityRules: [
// Permissif : les propriétaires peuvent accéder à leurs lignes
{ operation: "all", ownerField: "user_id" },
// Restrictif : mais les lignes verrouillées ne peuvent pas être mises à jour
{ operation: "update", mode: "restrictive", using: "{is_locked} = false", withCheck: "{is_locked} = false" }
]
OpérationÉquivalent SQLDescription
"select"SELECTLire les lignes
"insert"INSERTCréer de nouvelles lignes
"update"UPDATEModifier les lignes existantes
"delete"DELETESupprimer les lignes
"all"Toutes les opérations ci-dessusRaccourci pour toutes les opérations

Vous pouvez également utiliser operations (au pluriel) pour appliquer une règle à plusieurs opérations :

{ operations: ["insert", "update", "delete"], ownerField: "author_id" }
interface SecurityRule {
name?: string; // Nom de politique lisible par l'homme
operation?: SecurityOperation; // Opération unique
operations?: SecurityOperation[]; // Opérations multiples
mode?: "permissive" | "restrictive"; // Par défaut : "permissive"
access?: "public" | "authenticated";
ownerField?: string; // Colonne contenant l'ID de l'utilisateur propriétaire
roles?: string[]; // Rôles d'application auxquels cette politique s'applique
using?: string; // Expression SQL brute USING
withCheck?: string; // Expression SQL brute WITH CHECK
}
securityRules: [
// Tout le monde peut lire les publications publiées
{ operation: "select", access: "public", using: "{status} = 'published'" },
// Les auteurs peuvent voir leurs propres brouillons
{ operation: "select", ownerField: "author_id" },
// Les auteurs peuvent créer et modifier leurs propres publications
{ operations: ["insert", "update"], ownerField: "author_id" },
// Seuls les administrateurs peuvent supprimer
{ 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())"
}
]

Un besoin courant est de permettre aux utilisateurs non authentifiés de soumettre des données — formulaires de contact, inscriptions à la newsletter, applications publiques. Rebase offre un modèle clair pour cela.

const contactMessagesCollection: EntityCollection = {
slug: "contact_messages",
securityRules: [
// Tout le monde peut soumettre un message de contact
{
operation: "insert",
access: "public",
withCheck: "true"
},
// Seuls les administrateurs peuvent lire, modifier ou supprimer des messages
{ operations: ["select", "update", "delete"], roles: ["admin"] }
],
properties: { /* ... */ }
};

Le raccourci access: "public" génère une politique qui autorise l’opération sans nécessiter d’authentification.

const leadSignupsCollection: EntityCollection = {
slug: "lead_magnet_signups",
securityRules: [
// Autoriser les insertions anonymes
{ operation: "insert", access: "public", withCheck: "true" },
// Les administrateurs peuvent consulter toutes les inscriptions
{ operation: "select", roles: ["admin"] }
],
properties: { /* ... */ }
};

Lorsqu’une requête arrive sans jeton JWT, le backend Rebase définit les variables de session PostgreSQL à :

VariableValeur
app.user_id'anonymous'
app.user_roles'' (vide)

Cela signifie que :

  • auth.uid() renvoie 'anonymous'
  • auth.roles() renvoie une chaîne vide
  • Les politiques access: "public" passent car elles génèrent USING (true) / WITH CHECK (true)
  • Les politiques access: "authenticated" échouent car elles vérifient un véritable ID d’utilisateur
  • Les politiques ownerField échouent car aucune ligne n’aura user_id = 'anonymous' (sauf si explicitement défini)

Si vous avez besoin d’un contrôle plus granulaire, utilisez du SQL brut :

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