Aller au contenu principal

SEC-P0-001B2 — Blocage IDOR IA sur les Actor Services

Statut : rapport d'implémentation Date : 2026-08-08 Périmètre traité : routes IA Actor Services uniquement

1. Objectif

SEC-P0-001B2 corrige les routes IA de génération et d'amélioration des prestations rattachées à un acteur.

La correction applique le pattern validé en SEC-P0-001B1 : validation HTTP, résolution de l'acteur ciblé, contrôle RBAC via ActorAccessService, puis seulement après autorisation accès au pipeline IA.

Aucune refonte IA globale n'est introduite.

2. Routes corrigées

Toutes les routes suivantes sont déclarées sous identify.app, auth:sanctum, throttle:ai, préfixe /api/v1/ai.

RouteMéthodeContrôleurRequestactor_idPermission ajoutée
/api/v1/ai/service/generatePOSTAIController::generateServiceDescriptionGenerateServiceDescriptionRequestbody nullablemanage_actor
/api/v1/ai/service/improvePOSTAIController::improveServiceDescriptionImproveServiceDescriptionRequestbody nullablemanage_actor

Routes volontairement exclues :

  • IA publication, déjà traitée par SEC-P0-001B1 ;
  • IA onboarding, réservé à un sous-chantier ultérieur ;
  • lecture quota IA, réservée à un sous-chantier ultérieur ;
  • durcissement backoffice et noCreditDebit, réservé à un sous-chantier ultérieur ;
  • cache/quota structurels, réservés à un sous-chantier ultérieur.

3. Nature exacte des services concernés

Le terme service dans ces routes IA désigne les Actor Services, c'est-à-dire les prestations ou services rattachés à un acteur dans acteur_services.

Ces routes ne concernent pas les Municipal Services, c'est-à-dire les services municipaux gérés dans mairie_services.

Distinction validée :

FamilleTableSurface métierPermission
Actor Services / prestations acteuracteur_servicespage Workspace acteur services/prestationsmanage_actor
Municipal Services / services municipauxmairie_servicesmodule Mairie / Municipal Managementmanage_services

4. Divergence initiale et arbitrage

L'audit B2 a détecté une ambiguïté terminologique : la spécification B0 proposait manage_services, mais les consommateurs réels des routes /ai/service/* sont les prestations acteur.

Preuves code :

  • la page Workspace des prestations acteur appelle /ai/service/generate et /ai/service/improve avec l'actor_id du contexte acteur ;
  • cette même page autorise la gestion avec manage_actor ;
  • les mutations backend acteur_services passent par ActeurPolicy::update, qui vérifie manage_actor via ActorAccessService ;
  • les routes mairie_services sont séparées et protégées par mairie.access, qui utilise manage_services.

Arbitrage validé :

  • utiliser ActorAccessService::hasPermission(..., 'manage_actor') pour les routes IA Actor Services ;
  • ne pas utiliser manage_services sur ces routes ;
  • conserver manage_services pour les services municipaux.

5. Vulnérabilité initiale

Avant B2, les deux routes acceptaient un actor_id nullable dans le payload validé, puis le transmettaient directement à AIGatewayService.

Un utilisateur authentifié pouvait donc falsifier actor_id et déclencher, pour un acteur tiers :

  • lecture ou consommation de quota IA ;
  • lecture ou consommation de boost assistant ;
  • lecture ou écriture cache IA ;
  • appel provider ;
  • écriture ai_usage_logs.

La validation existante portait sur la forme UUID, pas sur le droit métier de l'utilisateur sur l'acteur ciblé.

6. Mécanisme RBAC

Pour tout actor_id non nul sur /ai/service/generate ou /ai/service/improve, le contrôleur appelle :

ActorAccessService::hasPermission(auth()->user(), $actorId, 'manage_actor')

Cas autorisés :

  • owner de l'acteur ;
  • collaborateur actif dont le rôle porte manage_actor ;
  • admin global, conformément au comportement historique de ActorAccessService.

Cas refusés :

  • utilisateur autorisé sur un acteur A qui fournit l'actor_id d'un acteur B ;
  • collaborateur sans manage_actor, même s'il possède manage_services ;
  • collaborateur inactif ;
  • utilisateur authentifié sans accès acteur ;
  • relation legacy user_acteurs seule.

7. Réutilisation du pattern B1

SEC-P0-001B2 réutilise le helper local de AIController introduit par B1 en le généralisant à une permission explicite.

Le point d'application reste volontairement dans AIController, comme validé par B0 :

  • le contrôleur connaît le cas d'usage HTTP et la permission cible ;
  • AIGatewayService reste l'orchestrateur technique IA ;
  • aucune façade ou abstraction IA nouvelle n'est créée.

8. Pipeline avant / après

Avant :

HTTP
-> Form Request
-> actor_id client
-> AIGatewayService
-> quota / boost / cache / provider / logs / consommation

Après :

HTTP
-> Form Request
-> actor_id client
-> ActorAccessService::hasPermission(..., manage_actor)
-> 403 immédiat si refus
-> AIGatewayService seulement si autorisé
-> quota / boost / cache / provider / logs / consommation

9. Effets de bord bloqués

Les tests de refus B2 vérifient que le 403 intervient avant :

  • appel à AIGatewayService;
  • appel provider OpenAI ;
  • consommation quota ;
  • consommation boost ;
  • écriture ai_usage_logs ;
  • écriture ai_cache.

Les snapshots de tests couvrent aussi l'absence de modification de ai_user_quotas, boosts et boost_usages.

10. Tests

Tests ajoutés dans api/tests/Feature/AI/AITest.php :

  • owner autorisé sur /ai/service/generate ;
  • collaborateur actif avec manage_actor et sans manage_services autorisé sur /ai/service/improve ;
  • admin global autorisé ;
  • régression IDOR : owner de l'acteur A avec actor_id de l'acteur B reçoit 403 ;
  • collaborateur avec manage_services seulement, sans manage_actor, reçoit 403 ;
  • collaborateur inactif reçoit 403 ;
  • utilisateur sans acteur reçoit 403 ;
  • projection legacy user_acteurs seule reçoit 403.

11. Compatibilité

Compatibilité conservée :

  • URI inchangées ;
  • méthodes HTTP inchangées ;
  • payloads inchangés ;
  • validation Form Request inchangée ;
  • réponses des appels légitimes inchangées ;
  • prompts, provider, gateway, quota, cache et logs inchangés après autorisation.

Aucune modification frontend n'a été réalisée.

12. Hors périmètre respecté

Non modifiés :

  • mairie_services ;
  • permission manage_services ;
  • ActorPolicy ;
  • RBAC global ;
  • frontend Workspace ;
  • onboarding IA ;
  • quota IA ;
  • backoffice IA ;
  • cache/quota structurels.

13. Rollback

Rollback applicatif minimal :

  • retirer les appels authorizeActorForAi(..., 'manage_actor') dans generateServiceDescription et improveServiceDescription ;
  • conserver ou rétablir le helper B1 pour les routes publication ;
  • retirer les tests B2 associés.

Le rollback réouvrirait l'IDOR sur les routes IA Actor Services.

14. Definition of Done

B2 est terminé lorsque :

  • les deux routes IA Actor Services contrôlent actor_id via ActorAccessService et manage_actor ;
  • le contrôle précède tout effet de bord IA ;
  • les cas owner, collaborateur manage_actor et admin restent autorisés ;
  • les cas acteur tiers, absence de permission, inactivité, absence d'accès et legacy seul retournent 403 ;
  • les tests prouvent l'absence d'effet de bord IA sur refus ;
  • mairie_services et manage_services restent inchangés ;
  • aucun frontend, commit ou PR n'est créé pendant la mission.