Aller au contenu principal

SEC-P0-001B3 — IA Actor Onboarding Authorization

Statut

  • Branche API : feature/sec-p0-001b3-ai-onboarding-authorization.
  • Périmètre : route IA onboarding acteur uniquement.
  • Aucun commit créé pendant l'implémentation.
  • Aucune PR documentaire créée tant que la chaîne documentaire B2 reste ouverte.

Route corrigée

RouteContrôleurRequestPrompt IADomaine
POST /api/v1/ai/onboarding/suggestAIController::onboardingSuggestOnboardingSuggestRequestonboarding.suggestActor onboarding

Cette route accepte un actor_id optionnel côté payload et peut déclencher la logique d'onboarding gratuit acteur lorsque actor_id est présent.

Arbitrage permission

Permission cible retenue :

ActorAccessService::hasPermission($user, $actorId, 'manage_actor')

Justification :

  • l'action manuelle équivalente est la modification du profil acteur ;
  • ActeurPolicy::update délègue déjà à ActorAccessService::hasPermission(..., 'manage_actor') ;
  • l'onboarding enrichit les données d'un acteur, il ne relève pas de mairie_services ;
  • manage_services reste réservé au domaine des services municipaux.

Divergence levée

Dans cette route, le terme onboarding désigne l'aide IA à la constitution d'un profil acteur.

  • Actor onboarding → acteur/profil acteur → manage_actor.
  • Municipal Services → mairie_servicesmanage_services.

La route POST /api/v1/ai/onboarding/suggest ne manipule pas mairie_services.

Ordre de contrôle

Le contrôle RBAC est exécuté immédiatement après validation du payload et avant :

  • calcul X-App-Source / noCreditDebit ;
  • AIGatewayService ;
  • AIQuotaService::isFirstOnboarding ;
  • création ou mutation de quota acteur ;
  • lecture ou écriture cache ;
  • appel provider IA ;
  • écriture ai_usage_logs ;
  • consommation de boost ;
  • marquage onboarding_at.

Sémantique onboarding gratuit conservée

Le comportement existant reste inchangé pour les appels autorisés :

  • AIGatewayService détecte l'onboarding gratuit uniquement pour promptKey = onboarding.suggest, userId != null et actorId != null ;
  • AIQuotaService::isFirstOnboarding($userId, $actorId) lit ou crée le quota acteur ;
  • si onboarding_at est null, l'appel autorisé consomme 0 crédit ;
  • après succès provider, AIQuotaService::markOnboardingUsed($userId, $actorId) renseigne onboarding_at ;
  • les appels sans actor_id conservent le comportement historique de quota personnel.

Le correctif ne modifie ni le calcul de quota, ni le débit gratuit, ni la structure cache/quota.

Scénarios testés

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

  • owner autorisé ;
  • collaborateur actif avec manage_actor et sans manage_services autorisé ;
  • admin global autorisé selon comportement historique ;
  • régression IDOR avec actor_id tiers refusée en 403 ;
  • collaborateur avec manage_services mais sans manage_actor refusé en 403 ;
  • collaborateur inactif refusé en 403 ;
  • utilisateur sans acteur refusé en 403 ;
  • projection legacy user_acteurs seule refusée en 403 ;
  • refus IDOR sans création de quota acteur ;
  • contrat onboarding gratuit autorisé toujours routé au gateway avec credits_used = 0.

Pour les refus, les tests prouvent :

  • aucun AIGatewayService::execute appelé ;
  • aucun appel provider HTTP envoyé ;
  • aucun quota acteur consommé ;
  • aucun quota acteur créé quand il n'existait pas ;
  • aucun onboarding_at renseigné après refus ;
  • aucun ai_usage_logs créé ;
  • aucun ai_cache écrit ;
  • aucun boost_usages créé.

Hors périmètre confirmé

Non modifiés :

  • mairie_services ;
  • manage_services hors usage test de refus ;
  • AIQuotaService ;
  • AIGatewayService ;
  • AICacheService ;
  • AIUsageLogger ;
  • ActorPolicy ;
  • RBAC global ;
  • frontend ;
  • SEC-P0-001B4, SEC-P0-001B5, SEC-P0-001B6 ;
  • DB-001.

Rollback

Rollback isolé : retirer l'appel à authorizeActorForAi(..., 'manage_actor') dans AIController::onboardingSuggest.

Ce rollback restaurerait le comportement antérieur sans migration DB, mais réouvrirait le risque IDOR onboarding acteur.