update
This commit is contained in:
121
docs/README_V7_27.md
Normal file
121
docs/README_V7_27.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user