Files
pta/docs/README_V7_27.md
2026-08-25 15:12:11 +02:00

122 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PTA V7.27 — Le Service Enfance distribue les jetons des agents et des responsables
## Le problème traité
Depuis la V7.11, tout compte d’accès passait par la DSI : création, jeton perdu, départ d’un
remplaçant. Or c’est le **Service Enfance** qui sait qu’un agent est arrivé, qu’un responsable a
laissé son jeton chez lui ou qu’un contrat s’arrête. Le détour par la DSI ajoutait un délai — parfois
plusieurs jours — sans ajouter le moindre contrôle : la DSI ne pouvait rien vérifier de ce qu’on lui
demandait, elle exécutait.
## Ce qui change
L’onglet **Administration** du Service Enfance gagne une section « Jetons d’accès des agents et des
responsables », sous les horaires de couverture. On y fait, pour ces deux rôles, ce que la page DSI
permet :
- **créer** un compte pour un agent ou un lieu qui n’en a pas encore ;
- **régénérer** un jeton perdu ou compromis — l’ancien cesse aussitôt de fonctionner ;
- **désactiver** puis réactiver un compte (départ temporaire) ;
- **supprimer** un compte définitivement ;
- **générer en lot** les jetons de toutes les identités actives qui n’en ont pas.
Comme sur la page DSI, un jeton produit n’est lisible **qu’une fois**, dans le panneau « Jetons à
transmettre » : la base n’en garde qu’une empreinte SHA-256. Perdu, il ne se retrouve pas — il se
régénère.
## Ce qui ne change pas : la frontière de privilège
La délégation s’arrête à **deux rôles : AGENT et RESPONSABLE_STRUCTURE**. Les comptes
**SERVICE_ENFANCE** et **DSI** n’apparaissent pas dans cette section, ne sont pas créables, et toute
action qui en désigne un est refusée en **403**. Ce n’est pas une commodité d’affichage :
- un compte Service Enfance créé par le Service Enfance serait une **auto-promotion** — le rôle se
distribuerait lui-même, sans contrôle extérieur ;
- un compte DSI créé par le Service Enfance lui ouvrirait le **journal d’activité**, c’est-à-dire la
trace de ses propres actions.
Le **second facteur** de la V7.12 reste hors de portée : ces deux rôles n’ont pas de mot de passe, et
le Service Enfance n’a aucun moyen d’en effacer un. La réinitialisation de mot de passe reste une
action DSI, sur la page DSI.
La borne est posée **des deux côtés** :
| Où | Quoi |
| --- | --- |
| `AccessAccountController::ROLES_DELEGUES` | La liste blanche PHP, contrôlée avant toute écriture et à la relecture de tout compte désigné |
| `CompteModel::lister(['roles' => …])` | La liste SQL est bornée ; le filtre d’interface ne peut que **restreindre** à l’intérieur de cette borne |
| `migration_v7_9h_jetons_service_enfance.sql` | La permission `can_manage_field_access`, accordée au seul Service Enfance |
Une frontière de privilège ne doit pas dépendre d’un seul des deux côtés : une ligne mal saisie en
base ne doit pas pouvoir ouvrir la création de comptes privilégiés, et inversement.
## Traçabilité
Toutes les actions sont journalisées en catégorie `ADMINISTRATION`, avec le rôle de l’acteur et un
contexte `canal = DELEGATION_SERVICE_ENFANCE` qui les distingue des mêmes actions faites par la DSI.
Les **refus** le sont aussi, en `ACCES_REFUSE` : `ROLE_HORS_PERIMETRE` (création tentée sur un rôle
privilégié) et `COMPTE_HORS_PERIMETRE` (action tentée sur un compte privilégié). La DSI garde donc la
lecture complète de ce que le Service Enfance fait des accès.
## Fichiers
| Fichier | Rôle |
| --- | --- |
| `database/migration_v7_9h_jetons_service_enfance.sql` | Permission `can_manage_field_access`. Aucune page nouvelle : « Administration » est déjà ouverte au Service Enfance |
| `app/Controllers/AccessAccountController.php` | Les cinq endpoints, la liste blanche et les refus |
| `api/acces_comptes.php`, `acces_compte_creer.php`, `acces_compte_jeton.php`, `acces_compte_statut.php`, `acces_comptes_manquants.php` | Points d’entrée minces |
| `app/Models/CompteModel.php` | Filtre `roles` sur `lister()` |
| `app/Views/partials/administration.php` | La section et le panneau des jetons |
| `assets/js/access-tokens.js` | Module `accessTokens` |
## V7.28 — Couper l’accès depuis la fiche des agents
La page **Agents** gagne, à côté de « Modifier », un bouton qui **coupe ou rétablit l’accès** de
l’agent : « Couper l’accès » / « Rétablir l’accès ». C’est le geste courant — un départ, un arrêt
long, un retour — là où la section « Jetons d’accès » de l’onglet Administration porte la
distribution des jetons elle-même.
Ce bouton **ne touche pas au jeton** : désactiver puis réactiver rend le *même* jeton valable, et
l’agent n’a rien à recevoir. Il ne touche pas non plus à la fiche de l’agent, qui reste planifiable —
c’est l’accès à l’application qui est coupé, pas la personne au planning. Un agent qui n’a pas encore
de compte affiche un bouton inerte « Sans compte d’accès », qui renvoie vers Administration : seul
cet écran sait afficher un jeton fraîchement produit.
Aucun droit nouveau : le module `agentAccess` réutilise `api/acces_comptes.php` et
`api/acces_compte_statut.php`, donc les mêmes gardes, la même liste blanche de rôles et la même
journalisation. L’état des comptes n’est **pas** rendu par PHP mais chargé par le module — le HTML
servi ne porte aucune information sur les comptes, comme sur l’écran DSI.
## V7.29 — Un accès coupé retire l’agent des listes d’affectation
Couper l’accès d’un agent le retire désormais des listes **où l’on affecte** :
| Écran | Effet |
| --- | --- |
| Saisie (`api/agents.php` → `AgentModel::listForLocation()`) | L’agent n’est plus proposé dans le sélecteur |
| Équipes (`PageController::teams()` → `allSelectable()`) | L’agent n’est plus proposé à la composition |
Trois bornes, toutes portées par le même `NOT EXISTS` de `AgentModel::SANS_COMPTE_COUPE` :
- **un agent SANS compte reste proposé.** Il n’a pas encore reçu son jeton, ce qui est l’état normal
avant distribution : le filtrer viderait les listes d’une installation neuve ;
- le filtre porte sur `compte_acces.actif`, **pas** sur `agent.actif` : fermer une fiche d’agent et
couper un accès restent deux décisions distinctes ;
- les écrans de **consultation** (planning par agent, vue annuelle, PTA détaillé) et le
**référentiel Agents** continuent de montrer l’agent. Sans quoi on ne pourrait plus ni relire son
année passée, ni lui rétablir son accès — le bouton vit précisément sur cette page.
**Ce qui n’est pas défait :** les créneaux déjà saisis et les équipes déjà composées restent en
place. Couper un accès n’efface rien du passé. En revanche, un lien profond qui désigne un agent
coupé (file de validation, tableau de bord) ne peut plus le sélectionner : l’écran de saisie le dit
alors explicitement, plutôt que de rester vide — rétablir l’accès rend la saisie de nouveau possible.
## Correctif embarqué
`CompteModel::lister()` répétait le paramètre nommé `:q` quatre fois dans sa clause de recherche. PDO
tourne ici **sans émulation de requêtes préparées** (`ATTR_EMULATE_PREPARES = 0`) : MariaDB refuse
alors un même paramètre employé plusieurs fois. **Toute recherche par nom, matricule ou début de
jeton échouait en 500 sur la page DSI**, depuis la V7.11. Les marqueurs sont désormais numérotés
(`:q_0` … `:q_3`). Le même champ de recherche existant dans la nouvelle section, le défaut aurait été
reconduit tel quel.