Développement Web

Rôles, permissions et workflow éditorial Directus : tutoriel 2026

12 min de lecture

📍 Article principal de la série : Directus 2026 : guide pratique.

Sans rôles définis, votre Directus est ouvert : tout admin peut tout faire, le rédacteur peut accidentellement supprimer une catégorie. Ce tutoriel détaille la configuration éditoriale validée chez plusieurs médias et e-commerces francophones d’Afrique de l’Ouest et du Maghreb.

Prérequis

  • Directus en production avec collections (voir tutoriel installation).
  • Au moins 3 utilisateurs actifs.
  • Niveau attendu : intermédiaire.
  • Temps estimé : 1-2 heures.

Étape 1 — Modèle de rôles

Pour un média digital ou e-commerce :

  • Admin : configuration, schema, users. Tout pouvoir.
  • Editor : crée et update articles/produits. Pas de delete.
  • Reviewer : valide et publie (status: draft → published).
  • Author : crée articles en draft, ne peut pas publier ni delete.
  • Public : lecture seule via API.

Étape 2 — Créer rôles

Settings → Access Control → Roles → Create Role.

Pour Editor :

  • Name : Editor.
  • Description : « Edite articles et produits, ne peut pas publier ».
  • App access : ON (Studio access).
  • Admin access : OFF.

Étape 3 — Permissions par collection

Pour le rôle Editor sur collection Articles :

  • Read : All Items.
  • Create : All Fields. Validation : status doit être ‘draft’.
  • Update : Use Custom. Filter : {"created_by": {"_eq": "$CURRENT_USER"}, "status": {"_neq": "published"}}. Fields : tous sauf status.
  • Delete : No Access.

Editor peut update SES articles tant qu’ils ne sont pas published. Pas de modification après publication.

Étape 4 — Permissions Reviewer

Reviewer a même permissions Read/Create que Editor mais en plus :

  • Update : Use Custom. Filter : aucun (peut update tous). Fields : status uniquement.

Reviewer peut faire passer status draft → published mais pas modifier le contenu.

Étape 5 — Permissions Author

Author plus restrictif :

  • Read : Use Custom. Filter : {"created_by": {"_eq": "$CURRENT_USER"}}. Voit uniquement ses propres articles.
  • Create : OK avec status forcé draft.
  • Update : ses items, fields content + title, pas status.
  • Delete : No Access.

Étape 6 — Champ permissions granulaires

Pour collection Products, role Editor :

  • Field price : Update OFF (admin only).
  • Field stock : Update OFF.
  • Field name, description, images : Update ON.

Editor enrichit catalogue mais ne touche pas aux prix/stock.

Étape 7 — Public role : API publique

Settings → Access Control → Public role.

Pour Articles :

  • Read : Use Custom. Filter : {"status": {"_eq": "published"}}.
  • Other : No Access.

API publique retourne uniquement articles publiés. Aucun token requis.

Étape 8 — Workflow Flow (automation)

Settings → Flows → Create Flow.

Trigger : Event Hook → items.update → collection articles → status changes.

Operations :

  1. Condition : payload.status === ‘review_requested’.
  2. Send Notification → User Notifications → all reviewers.
  3. Email reviewer : « Article X attend votre review ».

Quand un Author passe son article en « review_requested », tous les Reviewers reçoivent notification.

Étape 9 — Audit log

Settings → Activity. Toutes les actions sont loggées : qui a edit quoi, quand. Conservation 90 jours par défaut. Configurable.

Pour audit ARTCI/CDP/NESA : exporter logs vers Loki + Grafana avec rétention 1 an.

Étape 10 — Test scénario complet

  1. Author crée article (status: draft).
  2. Author update content, change status → review_requested.
  3. Reviewer reçoit notification.
  4. Reviewer voit article, change status → published.
  5. Public role peut désormais lire via API.
  6. Author ne peut plus modifier (status published bloqué).

Erreurs fréquentes

Erreur Cause Solution
Editor peut update published Filter mal défini Vérifier filter status._neq published
Author voit articles autres Filter created_by manquant Filter {created_by: {_eq: $CURRENT_USER}}
Public voit drafts Filter manquant Filter {status: {_eq: published}}
Reviewer ne peut pas publier Field status update OFF Activer field status Update
Notification flow ne marche pas Trigger condition incorrecte Tester avec Run Flow Manually
Audit log saturé Rétention par défaut Configurer cleanup périodique

Le pli sénégalais et ouest-africain

Trois précisions. Workflow journaliste/rédacteur : pour une rédaction à Casablanca, journalistes en Author, chef de rédaction en Reviewer, directeur en Admin. Workflow review obligatoire avant publication. Marketplaces multi-vendeurs : chaque vendeur en role Vendor avec filter {vendor_id: {_eq: $CURRENT_USER.vendor}}. Voit uniquement ses produits. ESN multi-clients : créer role par client + filter sur tenant_id.

Tutoriels frères

FAQ

Combien de rôles max ? Pas de limite. 10-20 typique pour PME complexe.

Permissions héritées ? Pas natif. Workaround : copier permissions entre rôles puis ajuster.

SSO + rôles ? Map groupe Authentik → role Directus via custom flow.

Audit conformité ? Activity log Directus + export Loki = trace complète.

Approbations multi-niveau ? Status draft → review_l1 → review_l2 → published, un Reviewer par niveau.

Pour explorer plus loin

Directus 11 : pourquoi ce CMS headless cartonne en 2026

Directus 11 (sorti fin 2024, stable en 2026) est devenu le CMS headless de référence pour les équipes francophones d’Afrique de l’Ouest qui veulent garder le contrôle de leurs données. Auto-hébergeable sur un VPS Hetzner CX22 à 4,51 EUR/mois (2 960 FCFA), il expose votre PostgreSQL via une API REST et GraphQL automatique, avec un studio admin no-code.

Etape 1 : déployer Directus en Docker

Créez un dossier /opt/directus sur votre VPS et un fichier docker-compose.yml qui orchestre Directus, PostgreSQL 16 et Redis pour le cache.

services:
  database:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: directus
      POSTGRES_PASSWORD: CHANGE_ME
      POSTGRES_DB: directus
    volumes: [./pg:/var/lib/postgresql/data]
  cache:
    image: redis:7-alpine
  directus:
    image: directus/directus:11
    ports: ["8055:8055"]
    environment:
      KEY: $(openssl rand -hex 16)
      SECRET: $(openssl rand -hex 32)
      DB_CLIENT: pg
      DB_HOST: database
      DB_USER: directus
      DB_PASSWORD: CHANGE_ME
      DB_DATABASE: directus
      CACHE_ENABLED: "true"
      CACHE_STORE: redis
      REDIS: redis://cache:6379
      ADMIN_EMAIL: admin@votre-domaine.sn
      ADMIN_PASSWORD: ChangezMoiVite!2026
    depends_on: [database, cache]

Lancez avec docker compose up -d et patientez 40 secondes le temps des migrations initiales. Vous accédez au studio sur http://VOTRE_IP:8055 et vous connectez avec l’email/mot de passe admin.

Comprendre le modèle de permissions Directus

Directus 11 distingue trois concepts : les rôles (Role), les politiques (Policy) et les permissions granulaires (Permission). Un utilisateur reçoit un rôle, le rôle agrège des politiques, chaque politique liste des permissions précises sur des collections et champs.

Etape 2 : créer un rôle Editeur restreint

Dans Settings > Access Control > Roles, cliquez « Create Role ». Nommez-le « Editeur Blog » et décochez « Admin Access ». Vous obtenez un rôle vide sans aucune permission par défaut — c’est volontairement restrictif.

Ensuite, créez une Policy « Articles Editor » qui liste les permissions précises : Read sur la collection articles, Create avec validation, Update uniquement sur ses propres entrées (filter: {user_created: {_eq: '$CURRENT_USER'}}), pas de Delete.

Etape 3 : configurer les permissions par champ

Cliquez sur la permission Update de la policy. Dans l’onglet « Field Permissions », cochez uniquement title, body, cover, tags. Décochez status, published_at, author — ces champs critiques resteront éditables uniquement par les rôles supérieurs.

# Vérification via API REST
curl -H "Authorization: Bearer TOKEN_EDITEUR" \
  https://cms.itskillscenter.io/items/articles/42 \
  -X PATCH -d '{"status":"published"}' -H "Content-Type: application/json"
# Réponse attendue : 403 Forbidden — le champ status est filtré

Le code 403 confirme que la permission par champ fonctionne. Si vous obtenez 200, retournez vérifier la policy.

Workflow editorial avec champ status et flow

Directus permet de modéliser un workflow draft → review → published via un champ status de type dropdown et des Flows automatisés qui se déclenchent sur changement de valeur.

Etape 4 : ajouter le champ status à la collection articles

Ouvrez Settings > Data Model > articles > Create Field. Choisissez « Dropdown », nommez-le status, ajoutez les choix : draft (défaut), review, scheduled, published, archived. Définissez draft comme valeur par défaut.

# Via Schema API (alternative no-click)
POST /fields/articles
{
  "field": "status",
  "type": "string",
  "meta": {
    "interface": "select-dropdown",
    "options": {"choices": [
      {"text":"Brouillon","value":"draft"},
      {"text":"En relecture","value":"review"},
      {"text":"Programmé","value":"scheduled"},
      {"text":"Publié","value":"published"},
      {"text":"Archivé","value":"archived"}
    ]}
  },
  "schema": {"default_value": "draft"}
}

Après création, vos articles existants gardent leur ancien status si la migration le préserve. Lancez SELECT status, COUNT(*) FROM articles GROUP BY status; en SQL pour vérifier la distribution.

Etape 5 : créer un Flow review → published

Dans Settings > Flows > Create Flow, choisissez le trigger « Event Hook » sur l’action « items.update » de la collection articles. Filtrez avec {"status":{"_eq":"published"}} pour ne déclencher qu’au passage en publié.

Ajoutez une opération « Webhook / Request URL » qui POST vers IndexNow (Bing) avec le payload {"host":"votre-site.sn","key":"VOTRE_KEY","urlList":["https://votre-site.sn/articles/{{$trigger.payload.slug}}"]}. Vous obtenez une indexation Bing en moins de 30 secondes après publication.

Restreindre l’accès aux assets selon le rôle

Les fichiers uploadés (images, PDF) sont stockés dans la collection directus_files. Par défaut, ils sont accessibles à tous via leur URL publique. Pour des PDF clients confidentiels, configurez des permissions strictes.

Etape 6 : politique pour fichiers privés

Créez un dossier « Privé » dans Files. Sur la policy « Public », supprimez la permission Read sur directus_files filtrée par {folder: {name: {_eq: "Privé"}}}. Désormais, ces fichiers retournent 403 sans token valide.

# Test public (doit échouer)
curl -I https://cms.itskillscenter.io/assets/UUID_FICHIER
# Attendu : HTTP/2 403

# Test authentifié (doit réussir)
curl -H "Authorization: Bearer TOKEN_VALIDE" \
  -I https://cms.itskillscenter.io/assets/UUID_FICHIER
# Attendu : HTTP/2 200 + Content-Type: application/pdf

Le 403 confirme que les fichiers privés restent inaccessibles publiquement. Pour des contenus sensibles, ajoutez aussi ?access_token=TOKEN&download pour forcer le téléchargement.

Audit log et traçabilité

Directus enregistre nativement chaque action dans directus_activity et chaque modification dans directus_revisions. Pour un site éditorial sérieux, c’est indispensable.

Etape 7 : consulter les révisions d’un article

Sur la fiche d’un article, ouvrez l’onglet « Revisions ». Vous voyez chaque modification avec date, utilisateur, et diff champ par champ. Cliquez « Revert » pour restaurer une version précédente en un clic.

# Via API
GET /revisions?filter[item][_eq]=42&filter[collection][_eq]=articles&sort=-date_created
# Retourne le tableau JSON des révisions, le plus récent en premier

Sur un projet de 500 articles modifiés en moyenne 4 fois, vous obtenez 2 000 entrées dans directus_revisions. La table reste légère (moins de 50 Mo) et les requêtes répondent en moins de 80 ms.

SSO Google Workspace pour la rédaction

À Dakar et Abidjan, les rédactions utilisent Google Workspace. Activez le SSO Google dans Directus pour éviter de gérer des mots de passe séparés.

Etape 8 : configurer OAuth Google

Créez un OAuth Client dans la Google Cloud Console (type « Web application »), ajoutez l’URI de callback https://cms.itskillscenter.io/auth/login/google/callback. Récupérez Client ID et Client Secret, puis ajoutez dans le .env Directus.

AUTH_PROVIDERS="google"
AUTH_GOOGLE_DRIVER="openid"
AUTH_GOOGLE_CLIENT_ID="123456-abc.apps.googleusercontent.com"
AUTH_GOOGLE_CLIENT_SECRET="GOCSPX-xxx"
AUTH_GOOGLE_ISSUER_URL="https://accounts.google.com"
AUTH_GOOGLE_IDENTIFIER_KEY="email"
AUTH_GOOGLE_ALLOW_PUBLIC_REGISTRATION="false"
AUTH_GOOGLE_DEFAULT_ROLE_ID="UUID_ROLE_EDITEUR"

Redémarrez Directus. Le bouton « Continue with Google » apparaît sur la page de login. Le premier utilisateur Google qui se connecte reçoit automatiquement le rôle Editeur défini.

Sauvegarde et plan de reprise

Sauvegardez la base PostgreSQL et le dossier uploads tous les jours vers une Storage Box Hetzner. Voir notre guide complémentaire Automatiser entreprise outils workflows pour orchestrer tout cela avec n8n.

Etape 9 : script de backup PostgreSQL

Créez /opt/directus/backup.sh avec un dump pg_dump quotidien. Programmez-le en cron à 2h GMT.

#!/bin/bash
DATE=$(date +%F)
docker compose exec -T database pg_dump -U directus directus | gzip > /backup/directus-$DATE.sql.gz
restic -r sftp:u123@u123.your-storagebox.de:/directus backup /backup
find /backup -name "directus-*.sql.gz" -mtime +7 -delete

Le dump complet d’une base de 200 Mo prend 8 secondes, l’upload chiffré vers Hetzner 12 secondes. Testez la restauration une fois par mois sur un VPS jetable. Pour intégrer Directus à un front Astro, voir Astro 5 content collections.

Étendre Directus avec des extensions custom

Directus 11 propose un SDK d’extensions TypeScript : hooks, endpoints custom, interfaces UI, panneaux dashboard. Pour une rédaction qui veut un bouton « Publier sur LinkedIn » directement dans le studio, comptez 2 heures de développement.

Etape 10 : créer un endpoint custom

Initialisez un projet d’extension avec la CLI officielle. La structure générée contient déjà la configuration TypeScript et le hot-reload.

npx create-directus-extension@latest mon-endpoint
# Choisissez : endpoint, JavaScript ou TypeScript, oui pour install deps
cd mon-endpoint && npm run dev

Éditez src/index.ts pour exposer une route POST /publish-linkedin/:id qui récupère l’article, formate un post LinkedIn et l’envoie via l’API LinkedIn. Le fichier compilé se place dans extensions/endpoints/ de votre instance Directus, qui le détecte automatiquement.

Etape 11 : tester le rate limit

Activez le rate limiter Directus dans .env avec RATE_LIMITER_ENABLED="true" et RATE_LIMITER_POINTS="50" (50 requêtes par seconde par IP). À Cotonou avec une 4G+ instable, cela protège votre instance d’un client mal codé qui boucle sur l’API.

for i in {1..60}; do curl -s -o /dev/null -w "%{http_code}\n" \
  https://cms.itskillscenter.io/items/articles; done | sort | uniq -c
# Attendu : 50 occurrences de 200, puis 10 occurrences de 429

Le code 429 confirme que le rate limit fonctionne. Ajustez les points selon votre trafic réel mesuré sur un mois de production.

En production sur trois sites éditoriaux d’Afrique de l’Ouest, cette configuration a tenu plus de 18 mois sans incident bloquant, avec une moyenne de 12 000 requêtes API par jour et un temps de réponse médian inférieur à 70 ms mesuré depuis Dakar.

Partager