Mise en place des RLS
Sécuriser l’accès aux données selon le rôle de l’utilisateur
Notions théoriques
Qu’est-ce que le Row Level Security (RLS) ?
Le Row Level Security (RLS) est une fonctionnalité de PostgreSQL (et donc de Supabase) qui permet de restreindre l’accès aux lignes d’une table en fonction de l’utilisateur connecté.
Il ne s’agit pas seulement de masquer des colonnes ou de bloquer des tables entières, mais bien de filtrer ligne par ligne ce qu’un utilisateur peut voir, insérer, modifier ou supprimer.
RLS est un outil essentiel pour éviter les fuites de données dans les applications multi-utilisateurs.
RLS doit être activé explicitement sur chaque table où l’on souhaite l’utiliser.
Pourquoi activer RLS ?
Par défaut, toutes les lignes d’une table sont visibles si un utilisateur a la permission de faire un SELECT
.
Cela pose problème dans une application avec différents rôles (guest, student, teacher, admin, super-admin).
Avec RLS, on peut dire :
- un
guest
ne voit que son propre profil, - un
student
ne peut écrire des messages que dans ses classes, - un
teacher
peut voir les membres de ses classes, - un
admin
peut modifier toutes les classes, - un
super-admin
peut tout faire.
Activer RLS sur une table
Pour activer RLS sur une table dans Supabase :
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
Supabase n’applique aucune restriction par défaut après activation.
Il faut ajouter des politiques (POLICY
) pour définir ce qui est autorisé.
Ajouter une politique RLS
Une politique est une règle SQL qui s’applique à un type d’opération : SELECT
, INSERT
, UPDATE
, ou DELETE
.
Exemple : autoriser un utilisateur à voir son propre profil :
create policy "Un utilisateur peut voir son propre profil"
on profiles
for select
using (auth.uid() = id);
Explication :
auth.uid()
est l’ID de l’utilisateur connecté.id
est la clé primaire deprofiles
, qui référenceauth.users.id
.
Exemple de politique INSERT
: écrire un message uniquement si on est membre d’une classe
create policy "Un utilisateur peut écrire un message dans une classe à laquelle il appartient"
on messages
for insert
with check (
exists (
select 1 from class_members
where class_id = messages.class_id
and user_id = auth.uid()
)
);
Tester les politiques
Il est possible de simuler un utilisateur dans l’éditeur SQL Supabase :
set role authenticated;
set local jwt.claims.sub = 'uuid-de-l-utilisateur';
Cela permet de tester les effets d’une politique comme si on était connecté avec un rôle donné.
Bonnes pratiques avec RLS
Toujours activer RLS avant de déployer une application multi-utilisateurs.
Créer des politiques pour chaque opération (SELECT, INSERT, UPDATE, DELETE).
Tester les politiques avec différents rôles pour éviter les fuites accidentelles.
Ne jamais supposer que les utilisateurs vont se limiter à l’interface prévue. Ils peuvent envoyer des requêtes SQL directement (via API ou outils comme DBeaver).
Exemple pratique
Il est possible de sécuriser la table profiles
pour que :
- un utilisateur ne voie que son propre profil,
- un
admin
ousuper-admin
voie tous les profils.
Étape 1 : activer RLS sur la table profiles
alter table profiles enable row level security;
Étape 2 : autoriser un utilisateur à voir son propre profil
create policy "lecture de son propre profil"
on profiles
for select
using (auth.uid() = id);
Étape 3 : autoriser les admins à voir tous les profils
create policy "lecture pour les admins"
on profiles
for select
using (
exists (
select 1 from user_roles
where user_id = auth.uid()
and role in ('admin', 'super-admin')
)
);
Étape 4 : tester avec un utilisateur simulé
set role authenticated;
set local jwt.claims.sub = 'uuid-du-student';
select * from profiles;
Résultat : l’étudiant ne voit que sa propre ligne.
Changer l’UUID pour celui d’un admin
: il verra tous les profils.
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Ce TP a pour objectif de mettre en place des politiques Row Level Security (RLS) sur les tables existantes de votre BD Supabase,
en appliquant des restrictions d'accès selon les rôles définis dans la table user_roles
.
Étape 1 : Activer RLS sur la table profiles
Dans l’éditeur SQL de Supabase, activer RLS sur la table profiles
.
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
Une solution
Vous devez être connecté pour voir le contenu.
Étape 2 : Créer une politique RLS pour permettre à un utilisateur de voir uniquement son propre profil
Créer une politique SELECT
qui autorise un utilisateur à voir uniquement sa propre ligne dans la table profiles
.
CREATE POLICY "lecture de son propre profil"
ON profiles
FOR SELECT
USING (auth.uid() = id);
Une solution
Vous devez être connecté pour voir le contenu.
Étape 3 : Cr éer une politique RLS permettant aux administrateurs de voir tous les profils
Créer une politique SELECT
qui autorise les utilisateurs ayant le rôle admin
ou super-admin
à voir tous les profils.
CREATE POLICY "lecture pour les admins"
ON profiles
FOR SELECT
USING (
EXISTS (
SELECT 1 FROM user_roles
WHERE user_id = auth.uid()
AND role IN ('admin', 'super-admin')
)
);
Une solution
Vous devez être connecté pour voir le contenu.
Étape 4 : Tester les politiques avec un utilisateur simulé
Simuler une session avec un utilisateur ayant un rôle student
(ou autre) pour vérifier que les politiques fonctionnent.
SET ROLE authenticated;
SET LOCAL jwt.claims.sub = 'uuid-de-l-utilisateur';
SELECT * FROM profiles;
Changer l’UUID pour tester différents cas :
- UUID d’un
guest
→ ne doit voir que sa ligne - UUID d’un
admin
→ doit voir tous les profils
Une solution
Vous devez être connecté pour voir le contenu.
Étape 5 : Créer une politique RLS pour permettre aux utilisateurs de modifier uniquement leur propre profil
Créer une politique UPDATE
qui autorise un utilisateur à modifier uniquement sa propre ligne.
CREATE POLICY "modification de son propre profil"
ON profiles
FOR UPDATE
USING (auth.uid() = id);
Une solution
Vous devez être connecté pour voir le contenu.
Étape 6 : Créer une politique RLS permettant aux administrateurs de modifier tous les profils
Créer une politique UPDATE
qui autorise les admin
et super-admin
à modifier n’importe quel profil.
CREATE POLICY "modification pour les admins"
ON profiles
FOR UPDATE
USING (
EXISTS (
SELECT 1 FROM user_roles
WHERE user_id = auth.uid()
AND role IN ('admin', 'super-admin')
)
);
Une solution
Vous devez être connecté pour voir le contenu.
Étape 7 : créer une politique RLS pour interdire les suppressions sauf pour les super-admins
Créer une politique DELETE
qui autorise uniquement les super-admin
à supprimer un profil.
CREATE POLICY "suppression réservée aux super-admins"
ON profiles
FOR DELETE
USING (
EXISTS (
SELECT 1 FROM user_roles
WHERE user_id = auth.uid()
AND role = 'super-admin'
)
);
Une solution
Vous devez être connecté pour voir le contenu.
Étape 8 : Tester les politiques UPDATE
et DELETE
avec différents utilisateurs
Simuler une session avec un student
, un teacher
, un admin
et un super-admin
. Tester les opérations suivantes :
UPDATE
sur sa propre ligneUPDATE
sur une autre ligneDELETE
sur sa propre ligneDELETE
sur une autre ligne
Utiliser les commandes :
SET ROLE authenticated;
SET LOCAL jwt.claims.sub = 'uuid-de-l-utilisateur';
Puis effectuer une requête comme :
UPDATE profiles
SET username = 'nouveau_nom'
WHERE id = 'uuid-d-un-autre-user';
Et :
DELETE FROM profiles
WHERE id = 'uuid-d-un-autre-user';
Une solution
Vous devez être connecté pour voir le contenu.
Étape 9 : sécuriser la table messages
pour que seuls les membres d’une classe puissent y insérer des messages
Créer une politique INSERT
sur la table messages
qui autorise l’insertion uniquement si l’utilisateur est membre de la classe indiquée.
CREATE POLICY "insertion de messages par les membres de la classe"
ON messages
FOR INSERT
WITH CHECK (
EXISTS (
SELECT 1 FROM class_members
WHERE class_id = messages.class_id
AND user_id = auth.uid()
)
);
Une solution
Vous devez être connecté pour voir le contenu.
Étape 10 : Tester l’insertion de messages avec un utilisateur non membre de la classe
Simuler un utilisateur avec un UUID qui n’est pas membre de la classe ciblée, puis tenter une insertion :
INSERT INTO messages (content, class_id, sender_id)
VALUES ('test', 'uuid-classe', 'uuid-user');
Une solution
Vous devez être connecté pour voir le contenu.
Étape 11 : Vérifier que les politiques RLS n’introduisent pas de fuites de données
Tester les requêtes suivantes avec un utilisateur guest
:
SELECT * FROM profiles;
SELECT * FROM messages;
Puis vérifier que :
- seules les lignes autorisées sont visibles,
- aucune erreur n’est levée,
- aucune fuite d’information n’est possible.
Une solution
Vous devez être connecté pour voir le contenu.