SEC-P0-001A — Autorisation de création Publication par Acteur
Statut : rapport d’implémentation
Date : 2026-08-06
Périmètre traité : POST /api/v1/publications uniquement
Vulnérabilité initiale
L’audit SEC-P0-001 a confirmé un IDOR sur POST /api/v1/publications.
Avant correction, la route était protégée par :
identify.appauth:sanctumthrottle:publication-write- validation
exists:acteurs,idsuracteur_id - validation de scope applicatif dans
PublicationWriteService
La création ne vérifiait pas que l’utilisateur authentifié avait un droit réel sur l’acteur ciblé par acteur_id.
Scénario d’exploitation
Un utilisateur authentifié rattaché à l’acteur A pouvait envoyer :
{
"titre": "Publication IDOR",
"contenu": "Tentative avec acteur_id falsifié.",
"scope": "public",
"type_id": "...",
"acteur_id": "acteur-B"
}
Si acteur-B existait et que le scope était compatible avec X-App-Source, le service pouvait créer la publication pour l’acteur B.
Règle cible
Pour créer une publication avec un acteur_id, l’utilisateur doit être autorisé sur cet acteur via le RBAC Acteur canonique.
Règle appliquée :
- admin global : autorisé par
ActorAccessService; - owner : autorisé car le rôle possède
manage_publications; - collaborateur actif : autorisé uniquement si son rôle possède
manage_publications; - collaborateur sans permission : refusé;
- collaborateur inactif : refusé;
- projection legacy
user_acteursseule : refusée.
Aucun nouveau rôle et aucune nouvelle permission n’ont été créés.
Mécanisme d’autorisation utilisé
Le correctif réutilise ActorAccessService::hasPermission($profile, $acteurId, 'manage_publications').
Ce mécanisme s’appuie sur :
acteur_collaborateurs;acteur_roles;acteur_permissions;- statut collaborateur
active; - rôle admin global via
Profile::isAdmin().
user_acteurs n’est pas utilisé comme source d’autorisation.
Code modifié
api/app/Modules/Publication/Controllers/PublicationWriteController.php- injection de
ActorAccessService; - ajout du contrôle avant tout appel à
PublicationWriteService::createPublication(); - refus HTTP 403 générique si l’utilisateur n’a pas
manage_publicationssur l’acteur ciblé.
- injection de
Le contrôle est placé dans le contrôleur de la route générique POST /api/v1/publications, conformément au périmètre SEC-P0-001A. Les autres consommateurs existants de PublicationWriteService, notamment AssoSuite et Mairie, conservent leurs mécanismes d’autorisation propres et restent hors périmètre de cette mission.
Tests ajoutés
Fichier modifié :
api/tests/Feature/Publication/PublicationWriteTest.php
Scénarios ajoutés :
- collaborateur actif avec
manage_publicationspeut créer une publication; - collaborateur sans
manage_publicationsest refusé; - collaborateur inactif est refusé;
- owner de l’acteur A ne peut pas publier pour l’acteur B;
- utilisateur authentifié sans acteur est refusé;
- projection legacy
user_acteursseule est refusée; - admin global conserve le comportement historique.
Le test create_publication_regression_idor_acteur_id_tiers_est_refuse_et_ne_cree_rien caractérise le scénario IDOR initial : il aurait créé une publication avant correction, et prouve désormais le refus.
Comportement refusé
Après correction, les requêtes suivantes retournent 403 et ne créent aucune ligne dans publications :
- utilisateur sans droit sur l’acteur ciblé;
- utilisateur owner d’un autre acteur;
- collaborateur actif sans permission;
- collaborateur inactif;
- utilisateur présent seulement dans
user_acteurs.
Compatibilité préservée
Les cas légitimes restent compatibles :
- owner existant;
- collaborateur actif doté de
manage_publications; - admin global;
- validation de scope existante;
- règles de modération initiale existantes;
- structure de réponse
201existante.
Dette restante
Cette mission ne traite volontairement pas les autres sous-chantiers SEC-P0-001 :
- routes IA avec
actor_id; - abonnements et boosts;
- Analytics;
- services mairie avec
{acteurId}et{communeId}; - pivots
commune_ids; - guard Acteur central;
- Workspace;
dmv_public.
Ces sujets restent à traiter dans les PR suivantes de SEC-P0-001.