DOC
Accueil/ Projets/ Gestion RH/ ADR-0003 - RLS permissive, contrôle d'accès côté client

ADR-0003 - RLS permissive, contrôle d'accès côté client

  • Statut : 🔴 Accepté (état de fait) - À REMPLACER en priorité (P0)
  • Date : 2026-07-10 (constat)
  • Tags : sécurité, RGPD, base de données

Contexte

Supabase applique la Row Level Security (RLS) de PostgreSQL pour décider qui peut lire/écrire quoi. Dans ce projet, toutes les tables ont la même policy :

CREATE POLICY "Authenticated full access" ON <table>
  FOR ALL USING (auth.role() = 'authenticated')
  WITH CHECK (auth.role() = 'authenticated');

(Voir supabase-schema.sql:90-94, supabase-payroll.sql:34, supabase-planning.sql:32-33.)

Le commentaire du schéma l'assume : « le contrôle admin/manager se fait côté app ». Les rôles (admin/manager/employee) et les périmètres (scope_type/scope_id) ne sont donc vérifiés que dans le JavaScript (isAdmin, visibleEmployees, NAV…).

Décision (constatée)

Autoriser tout utilisateur authentifié à faire SELECT/INSERT/UPDATE/DELETE sur toutes les tables, et gérer la visibilité/les droits uniquement dans l'UI.

Conséquences

Positives

  • Développement simple : aucune policy fine à écrire.

Négatives / RISQUE MAJEUR

  • La clé anon est publique (présente dans le bundle JS). N'importe quel utilisateur connecté - y compris un simple salarié ou un manager - peut, en dehors de l'interface (console navigateur, curl, client Supabase), exécuter par ex. supabase.from('employees').select('*') et récupérer les salaires, RIB, adresses, dates de naissance de tout le monde, ou modifier/supprimer des lignes.
  • Non-conformité RGPD : pas de minimisation ni de cloisonnement d'accès aux données personnelles.
  • Le badge « rôle » et le filtrage visibleEmployees donnent une fausse impression de cloisonnement.

Alternatives / Remédiation cible (proposée - à acter en ADR séparé)

Option Description Reco
Policies RLS par rôle Lire app_users.role/scope_* dans les policies (fonctions auth.uid() → rôle) Recommandé
Vue « employé » restreinte Un salarié ne voit que sa ligne ; un manager que son périmètre
Colonnes sensibles séparées Isoler salaire/RIB dans une table à accès admin ✅ (complément)
Edge Function de confiance Exposer des RPC contrôlées plutôt que l'accès table brut ⏳ option

Esquisse de policy cible (employés) :

-- Un admin voit tout ; un salarié voit sa fiche ; un manager voit son périmètre.
CREATE POLICY employees_select ON employees FOR SELECT USING (
  is_admin(auth.uid())
  OR employee_self(auth.uid(), employees.id)
  OR employee_in_manager_scope(auth.uid(), employees.id)
);

Détails et prompt d'implémentation : ../prompts/05-durcir-securite-rls.md.

Références

  • gestionrh-github/supabase-schema.sql:78-94
  • gestionrh-github/src/App.jsx : isAdmin, visibleEmployees, MANAGER_PAGES