Ajout des commentaires dans le code

This commit is contained in:
PTA
2026-08-20 13:22:49 +02:00
parent 4201894e28
commit 5d8c0993a6
154 changed files with 5071 additions and 47 deletions

76
docs/README.md Normal file
View File

@@ -0,0 +1,76 @@
# PTA V6 — application PHP MVC multipage
PTA n’est plus une SPA. Chaque fonction principale correspond à une page PHP distincte :
- `planning.php` : saisie, brouillon, validation et semaines types ;
- `pending.php` : plannings en attente de validation ;
- `coverage.php` : contrôle des créneaux sans agent ;
- `structures.php` : planning hebdomadaire par lieu ;
- `agents.php` : planning mensuel et bilan par agent ;
- `vacations.php` : aperçu et enregistrement des vacances scolaires ;
- `administration.php` : lieux, agents et règles de couverture.
`index.php` redirige vers le choix de rôle lors de la première visite, puis vers la page d’accueil autorisée pour le profil sélectionné.
## Architecture
```text
page PHP
→ PageController
→ Models / Services
→ View dédiée
View
→ API JSON uniquement pour les actions asynchrones utiles
```
Les contrôleurs métier, modèles, services PDF et endpoints API de la V5.20 sont conservés. La différence principale porte sur l’interface : plus de routeur JavaScript, plus de vues cachées et plus de chargement de tous les modules sur une seule page.
## JavaScript chargé par page
- Saisie : `planning-editor.js` et `week-templates.js` ;
- À valider : `pending-validation.js` ;
- Couverture : `coverage.js` ;
- Par lieu : `structure-overview.js` ;
- Par agent : `agent-overview.js` ;
- Vacances : `vacation-calendar.js` ;
- Administration : `administration.js` et la partie configuration de `coverage.js`.
`assets/js/page-bootstrap.js` initialise uniquement les modules déclarés par la page et applique les filtres transmis dans l’URL.
## Liens directs
Une page de contrôle peut ouvrir directement le planning concerné :
```text
planning.php?structure_id=2&agent_id=7&week=2026-W31&focus=validation
```
## Installation
1. Configurer `config.php`.
2. Utiliser le schéma SQL correspondant à la V5.20.
3. Placer le dossier sur le serveur PHP.
4. Ouvrir `index.php` ou `planning.php`.
Aucune migration SQL supplémentaire n’est nécessaire pour passer de la V5.20 à la V6.
## Version 7
Voir `README_V7.md` pour le nouveau socle PTA annuel, les équipes des mercredis/vacances et les contrôles métier associés.
## V7.2 — Vue annuelle
La page `annual.php` reproduit l’esprit du tableau annuel historique : douze mois côte à côte, codes AP/PP/AE/PE, couleurs métier, vacances scolaires et accès direct à la semaine à corriger. Voir `README_V7_2.md`.
## V7.8 — Sécurité et droits en base
Les correctifs de sécurité et le déplacement de la matrice des droits dans des
vues SQL sont décrits dans `README_V7_8.md`. La migration
`database/migration_v7_8_securite_acces.sql` est obligatoire ; elle est appliquée
automatiquement au déploiement par le service `db-migrate` du docker-compose.
## V7.4 — Rôles et droits d’accès
La première arrivée ouvre `role.php`. Les droits Agent, Responsable de structure et Service Enfance sont vérifiés dans les pages et les contrôleurs API. Voir `README_V7_4.md`.

129
docs/README_DOCKER.md Normal file
View File

@@ -0,0 +1,129 @@
# PTA - Docker / Nginx / PHP / MariaDB / phpMyAdmin
## Installation
Copier les fichiers de ce kit à la racine du projet PTA :
- `Dockerfile`
- `docker-compose.yml`
- `.env.example`
- `docker/nginx/default.conf`
- `docker/php/php.ini`
- `docker/db/apply-migrations.sh`
- `database/pta_prod_schema_mariadb.sql`
- `database/migration_*.sql`
Puis :
```bash
cp .env.example .env
nano .env
```
Changer impérativement `DB_PASSWORD` et `DB_ROOT_PASSWORD`.
Démarrer :
```bash
docker compose up -d --build
```
Application :
```text
http://IP_DU_SERVEUR:8080
```
phpMyAdmin est volontairement publié sur `127.0.0.1:8081` par défaut et **exige
un identifiant MariaDB** à chaque connexion. Ne pas définir `PMA_USER` /
`PMA_PASSWORD` dans le compose : ces variables font basculer phpMyAdmin en
`auth_type=config`, c'est-à-dire une session déjà ouverte pour quiconque
atteint le port.
Sur le serveur lui-même :
```text
http://127.0.0.1:8081
```
Pour y accéder depuis un poste distant, le plus sûr est un tunnel SSH :
```bash
ssh -L 8081:127.0.0.1:8081 utilisateur@serveur
```
Puis ouvrir localement :
```text
http://127.0.0.1:8081
```
## Base de données
Le schéma `database/pta_prod_schema_mariadb.sql` est exécuté automatiquement uniquement
lorsque le volume MariaDB est créé pour la première fois.
Il ne sera PAS rejoué à chaque démarrage du conteneur.
### Migrations
Les fichiers `database/migration_*.sql` sont appliqués **automatiquement à chaque
`docker compose up`**, par le service jetable `db-migrate` :
- il attend que MariaDB soit sain, puis joue les migrations dans l'ordre alphabétique ;
- la table `schema_migration` mémorise les fichiers déjà appliqués — rien n'est rejoué
inutilement ;
- le service `php` ne démarre qu'après sa sortie en succès : une migration en échec arrête
le déploiement au lieu de faire tourner l'application sur une base incomplète ;
- il vérifie pour finir la présence des sept vues de contrôle d'accès de la V7.8.
Il n'est donc plus nécessaire de supprimer le volume ni d'exécuter les migrations à la main.
```bash
# Compte rendu de la dernière application
docker compose logs db-migrate
# Rejouer les migrations sans toucher au reste de la pile
docker compose up -d --force-recreate db-migrate
```
Toute nouvelle migration doit être écrite idempotente (`CREATE TABLE IF NOT EXISTS`,
`INSERT ... ON DUPLICATE KEY UPDATE`, `CREATE OR REPLACE VIEW`) : un fichier dont le contenu
change après coup est rejoué.
## Commandes utiles
```bash
# Etat
docker compose ps
# Logs
docker compose logs -f nginx php db
# Rebuild après modification du PHP / JS / CSS
docker compose up -d --build php nginx
# Journal de l'application des migrations
docker compose logs db-migrate
# Arrêt
docker compose down
# Arrêt SANS supprimer les données
docker compose down
# ATTENTION : supprime la base MariaDB persistée
docker compose down -v
```
## Connexion de l'application
Le `config.php` PTA utilise déjà les variables :
- `PTA_DB_HOST`
- `PTA_DB_PORT`
- `PTA_DB_NAME`
- `PTA_DB_USER`
- `PTA_DB_PASS`
Le compose les fournit automatiquement au conteneur PHP.

37
docs/README_V6.md Normal file
View File

@@ -0,0 +1,37 @@
# PTA V6 — architecture multipage simplifiée
La V6 abandonne le fonctionnement SPA. Chaque fonction principale possède sa propre URL et sa propre vue PHP :
- `planning.php` : saisie et validation d’un planning ;
- `pending.php` : plannings en attente de validation ;
- `coverage.php` : alertes de couverture ;
- `structures.php` : planning hebdomadaire d’un lieu ;
- `agents.php` : planning mensuel d’un agent ;
- `vacations.php` : périodes de vacances scolaires ;
- `administration.php` : agents, lieux et règles de couverture.
`index.php` redirige vers `planning.php`.
## JavaScript
Il n’existe plus de routeur JavaScript et plus de chargement simultané de toutes les vues. Chaque page charge uniquement :
1. `assets/js/core.js` ;
2. les modules nécessaires à cette page ;
3. `assets/js/page-bootstrap.js`, qui initialise les modules et les filtres transmis dans l’URL.
Les appels `fetch()` sont conservés uniquement pour les opérations utiles sans rechargement complet : chargement d’un planning, modification/suppression d’un créneau, validation, glisser-déposer, import des vacances, semaines types, etc.
## Liens directs
Les pages de contrôle redirigent vers la saisie avec les bons filtres :
```text
planning.php?structure_id=2&agent_id=7&week=2026-W31&focus=validation
```
Cela rend les pages partageables et utilisables avec les boutons Précédent/Suivant du navigateur.
## Base de données
Aucune migration SQL n’est nécessaire depuis la V5.20.

61
docs/README_V7.md Normal file
View File

@@ -0,0 +1,61 @@
# PTA V7 — socle métier annuel et équipes
Cette version transforme le projet multipage V6 en outil de construction et de contrôle du PTA annuel.
## Nouvelles pages
- `pta.php` : synthèse annuelle par agent, ventilation du temps, contrôles, contraintes médicales / TPT, préférences et interdictions de lieux.
- `teams.php` : constitution des équipes des mercredis périscolaires, des périodes extrascolaires et du périscolaire de semaine.
## Données métier ajoutées
- postes PTA : responsable, animation relais, animation, restauration/entretien ;
- contrat permanent ou temporaire ;
- date de fin éventuelle du contrat ;
- formation déjà prévue dans la répartition annuelle ;
- PTA annuel sur l’année civile (01/01 → 31/12) ;
- préconisations médicales et temps partiels thérapeutiques ;
- préférences/interdictions de lieux et distance domicile-lieu ;
- équipes avec effectif, qualification, responsable et statut ;
- motifs détaillés : ALP matin, restauration, ALP soir, mercredi périscolaire, extrascolaire, préparation, ménage de fond, JF et JNT.
## Contrôles intégrés
- éligibilité au PTA (contrat permanent et poste renseigné) ;
- quota proratisé à partir de 1 607 h ;
- périodes contractuelles ;
- durées et amplitudes journalières ;
- pause après une séquence continue ;
- repos entre deux journées ;
- durée hebdomadaire ;
- durée habituelle des mercredis et journées extrascolaires selon le poste ;
- semaines de vacances incomplètes pour les métiers d’animation ;
- préparation interdite aux agents de restauration/entretien ;
- enveloppe de formation de 14 h ;
- cible de congés et jours de fractionnement ;
- ménage de fond à deux agents sur deux journées de 7 h ;
- effectif et qualification des équipes ;
- contraintes médicales/TPT et préférences de lieu visibles avant validation.
## Installation
1. Sauvegarder la base.
2. Mettre à jour les fichiers PHP/JS.
3. Exécuter une seule fois :
```sql
SOURCE database/migration_v7_pta_annuel_equipes.sql;
```
4. Ouvrir `administration.php` et renseigner le poste et le type de contrat des agents existants.
## Points volontairement paramétrables
Les ratios d’encadrement, le taux minimal de qualification et l’effectif minimal sont saisis au niveau de chaque équipe. Ils ne sont pas figés dans le code, car ils dépendent du cadre de l’accueil, de l’âge des enfants et de l’organisation locale.
## Limites de ce socle
- Les temps de préparation ne sont pas encore créés automatiquement à partir des heures ALP/restauration : la table `structure_creneau_reference` est prête pour cette prochaine étape.
- Les temps partiels thérapeutiques sont signalés et contrôlés, mais ils ne recalculent pas automatiquement le quota contractuel sans décision explicite.
- La génération automatique fidèle au PTA de l’année passée nécessite le fichier Excel/PDF d’origine et ses formules.
- La cible « 25 congés annuels » est interprétée comme 25 jours ; ce point doit être confirmé métier.

10
docs/README_V7_1.md Normal file
View File

@@ -0,0 +1,10 @@
# PTA V7.1 — Correction du poste PTA
Cette version corrige l'emplacement du champ **Poste PTA** dans l'administration.
- Le lieu d'affectation ne possède aucun poste PTA.
- Le poste PTA est une caractéristique de l'agent.
- À la création d'un agent, le poste est choisi parmi : responsable d'accueil, agent d'animation relais, agent d'animation, agent de restauration collective et d'entretien.
- Le type de contrat est lui aussi renseigné sur l'agent.
Aucune migration SQL n'est nécessaire : le modèle de données V7 stockait déjà correctement `agent.id_poste`. La correction concerne le formulaire d'administration.

89
docs/README_V7_10.md Normal file
View File

@@ -0,0 +1,89 @@
# PTA V7.10 — Éditeur de semaine type et méthode de saisie explicite
## Le problème traité
L'écran de saisie ne proposait qu'un seul chemin : choisir un lieu, un agent, une semaine, puis
remplir les cinq jours. Répéter ce travail semaine après semaine pour un même agent était la norme.
Les semaines types existaient déjà, mais elles étaient **subordonnées à ce chemin** :
- on ne pouvait créer un modèle qu'en enregistrant une semaine *déjà planifiée* — impossible de
composer directement une semaine de référence ;
- les trois boutons (« Enregistrer comme semaine type », « Importer une semaine type », « Appliquer
sur une période ») étaient rangés dans l'en-tête de l'étape 3, à côté du bouton PDF, donc trouvés
par hasard ;
- rien n'indiquait que dupliquer un modèle sur une période était la voie la plus rapide.
## Ce qui change
### 1. La méthode de saisie devient un choix explicite
Après la sélection de l'agent, une étape « Choisir la méthode de saisie » propose deux voies :
- **Semaine type** — marquée « Le plus rapide », **ouverte par défaut** : composer une semaine de
référence, puis la dupliquer sur une période entière ;
- **Semaine précise** — saisir ou corriger une seule semaine, jour par jour.
Le second mode est l'écran d'avant, **inchangé** : mêmes champs, mêmes contrôles, mêmes boutons.
Un lien profond qui désigne une semaine (file de validation, tableau de bord, vue annuelle, bouton
« Corriger cette semaine ») ouvre directement le mode **Semaine précise** : la priorité donnée à la
semaine type ne doit jamais détourner quelqu'un qui vient corriger un planning identifié.
Les rôles sans droit aux semaines types — Responsable de structure — ne voient aucun sélecteur : la
page reste exactement celle qu'ils connaissent, et la numérotation des étapes se resserre de 1-2-3.
### 2. Un vrai éditeur de semaine type
Le mode « Semaine type » affiche les cinq jours ouvrés côte à côte. Pour chaque jour :
- ajouter autant de plages que nécessaire, chacune avec son motif, ses horaires et son lieu — une
semaine type peut donc couvrir plusieurs lieux d'affectation, comme le permet le modèle de données ;
- un total par jour, et le total de la semaine avec la part réellement décomptée du quota ;
- **« Copier vers… »** : recopier un jour sur un autre, ou sur tous les autres d'un coup — la façon
la plus courante de construire une semaine régulière.
La barre d'outils permet d'ouvrir une semaine type existante de l'agent, de la renommer, de
l'enregistrer ou de la supprimer. Rien n'est planifié à ce stade : une semaine type est une
description, pas un planning. Deux actions la transforment en plannings réels :
- **Dupliquer sur une période…** — la modale existante, avec ses filtres scolaire/vacances, son mode
compléter/remplacer et sa prévisualisation ;
- **Appliquer à la semaine sélectionnée…** — la modale d'import existante.
Ces deux boutons restent inactifs tant que le modèle n'a pas été enregistré : on ne duplique jamais
un brouillon d'écran, seulement ce qui est réellement en base.
### 3. Les contrôles ne bougent pas
Les plages composées à l'écran sont **intégralement revalidées par le serveur** :
- jours limités au lundi-vendredi ;
- horaires par tranches de 15 minutes, fin postérieure au début ;
- pas de chevauchement entre deux plages du même jour, même sur deux lieux différents — un agent
n'est jamais à deux endroits à la fois ;
- lieu actif, motif actif, et **« Formation » refusé pour un agent sans droit à l'enveloppe de
formation**, exactement comme à la saisie semaine par semaine.
L'écran effectue les mêmes contrôles avant d'envoyer, uniquement pour éviter un aller-retour inutile.
## Fichiers
| Ajouté | Rôle |
| --- | --- |
| `assets/js/week-template-editor.js` | module `PTA.modules.weekTemplateEditor` |
| `api/week_template.php` | contenu détaillé d'une semaine type (GET) |
| `api/save_week_template_entries.php` | enregistrement depuis l'éditeur (POST) |
| Modifié | Raison |
| --- | --- |
| `app/Models/WeekTemplateModel.php` | `saveFromEntries()`, validation des plages, persistance factorisée avec `saveFromWeek()` |
| `app/Controllers/WeekTemplateController.php` | `show()` et `storeEntries()` |
| `app/Views/partials/planning_editor.php` | sélecteur de méthode et panneau de l'éditeur |
| `app/Controllers/PageController.php` | chargement du module, sous-titre selon le droit |
| `assets/js/week-templates.js` | `openApplyModal()` / `openPeriodModal()` exposés, avec modèle présélectionné |
| `assets/js/page-bootstrap.js` | un lien profond vers une semaine ouvre le mode « Semaine précise » |
| `assets/style.css` | styles du sélecteur de méthode et de l'éditeur |
Aucune migration SQL : `modele_semaine_agent` et `modele_semaine_creneau` portaient déjà exactement
ce que l'éditeur produit (jour, lieu, motif, horaires).

31
docs/README_V7_2.md Normal file
View File

@@ -0,0 +1,31 @@
# PTA V7.2 — Vue annuelle de contrôle
Cette version ajoute une page `annual.php` destinée au responsable qui contrôle le prévisionnel annuel d’un agent.
## Fonctionnement
- sélection d’un agent et d’une année civile ;
- affichage de janvier à décembre dans un tableau horizontal compact ;
- en-tête de synthèse : poste, lieu principal, contrat, quotité, quota et reste à affecter ;
- affichage des périodes contractuelles qui traversent l’année civile ;
- colonnes proches du tableau d’annualisation historique :
- `AP` : activité périscolaire ;
- `PP` : préparation périscolaire ;
- `AE` : activité extrascolaire ;
- `PE` : préparation extrascolaire ;
- code couleur pour le travail, la préparation, la formation, les congés, les absences, les JNT et les journées de fractionnement ;
- vacances scolaires visibles sur le calendrier ;
- contour orange pour les affectations encore en brouillon ;
- clic sur une journée pour ouvrir directement la semaine correspondante dans la saisie du planning ;
- mode plein écran et impression A3 paysage.
## Architecture
- Page : `annual.php`
- Vue : `app/Views/partials/annual_overview.php`
- API : `api/annual_overview.php`
- Contrôleur : `app/Controllers/AnnualOverviewController.php`
- Modèle SQL/PHP : `app/Models/AnnualOverviewModel.php`
- JavaScript ciblé : `assets/js/annual-overview.js`
Aucune migration SQL n’est nécessaire depuis la V7.1.

15
docs/README_V7_3.md Normal file
View File

@@ -0,0 +1,15 @@
# PTA V7.3 — Correctif de la recherche des vacances scolaires
La recherche des vacances a été rendue plus robuste :
- l'API Explore v2.0 publiée par data.gouv.fr est interrogée en priorité ;
- v2.1 et l'ancienne API 1.0 servent de solutions de repli ;
- les enregistrements `Élèves`, `Tous` et `-` sont pris en compte ;
- une graphie sans accent de l'académie est essayée automatiquement ;
- pour les académies métropolitaines, une recherche de secours par zone A/B/C est effectuée ;
- l'indisponibilité d'une des deux années scolaires ne bloque plus toute l'année civile ;
- les rentrées et prérentrées sont exclues des propositions ;
- les avertissements de publication partielle sont visibles dans la page ;
- une liste d'académies aide à éviter les erreurs de saisie.
Aucune migration SQL n'est nécessaire.

48
docs/README_V7_4.md Normal file
View File

@@ -0,0 +1,48 @@
# PTA V7.4 — Rôles et droits d’accès
Cette version ajoute un choix de rôle lors de la première arrivée dans PTA. Le choix est conservé dans la session PHP.
## Rôles
### Agent
- consultation de son propre planning mensuel ;
- consultation du planning hebdomadaire de son lieu principal ;
- accès en lecture seule ;
- téléchargement de ses documents PDF.
L’agent ne peut pas modifier, supprimer, déplacer, dupliquer ou valider un créneau.
### Responsable de structure
- consultation du planning de son lieu ;
- consultation des agents rattachés ou planifiés sur ce lieu ;
- saisie et correction des horaires et des motifs ;
- déplacement et suppression des créneaux de son lieu ;
- consultation des alertes de couverture de son lieu.
Toute modification reste en **brouillon**. Le responsable ne peut pas valider un planning, gérer les référentiels, appliquer les semaines types globales ni intervenir sur un autre lieu.
### Service Enfance
- accès à toutes les pages ;
- contrôle de tous les agents et lieux ;
- validation et réouverture des plannings ;
- préparation du prévisionnel annuel N+1 ;
- gestion des vacances, équipes, PTA annuel, couvertures et référentiels ;
- administration des agents et des lieux.
## Sécurité
Les contrôles sont appliqués à deux niveaux :
1. l’interface masque ou verrouille les actions interdites ;
2. les contrôleurs PHP vérifient systématiquement le rôle et le périmètre avant toute lecture sensible ou modification.
Le sélecteur de rôle fourni ici sert à tester les profils. Il ne constitue pas une authentification. En production, l’identité, le rôle, l’agent associé et le lieu géré doivent être obtenus depuis un compte utilisateur authentifié (annuaire, SSO ou table de comptes dédiée), et non choisis librement.
## Installation
Aucune migration SQL n’est nécessaire depuis la V7.3.
Après déploiement, ouvrir `index.php`. Lors de la première visite de la session, l’application redirige vers `role.php`.

18
docs/README_V7_5.md Normal file
View File

@@ -0,0 +1,18 @@
# PTA V7.5 — année civile et agents mixtes péri/extra
## Corrections métier
- Le quota PTA est désormais suivi sur l'année civile, du **1er janvier au 31 décembre**.
- La date de début de contrat reste conservée comme information RH mais ne décale plus la période des 1 607 h.
- Un agent n'a pas de type « périscolaire » ou « extrascolaire » : il peut participer aux deux types d'équipes au cours de la même année.
- Le lieu principal reste un rattachement par défaut et n'empêche pas une affectation sur d'autres lieux.
- Les vacances scolaires servent à déterminer le contexte scolaire / extrascolaire ; elles ne définissent pas l'année de quota.
## Mise à jour
Depuis la V7.4 :
1. remplacer les fichiers applicatifs par ceux de la V7.5 ;
2. exécuter `database/migration_v7_5_quota_annee_civile.sql`.
La migration conserve les anciennes lignes PTA contractuelles comme historique et crée, si nécessaire, une ligne annuelle civile équivalente.

21
docs/README_V7_6.md Normal file
View File

@@ -0,0 +1,21 @@
# PTA V7.6 — Application d'une semaine type selon le calendrier
Cette version ajoute un filtre lors de l'application d'une semaine type sur une période.
Trois périmètres sont disponibles :
- **Toutes les semaines** : le modèle est répété sur toutes les dates du lundi au vendredi comprises dans la période.
- **Périodes scolaires uniquement** : les dates comprises dans une période de vacances validée dans PTA sont ignorées.
- **Périodes extrascolaires uniquement** : seules les dates comprises dans une période de vacances validée dans PTA sont retenues.
Le filtre est choisi explicitement par l'utilisateur. Il n'est plus déduit du type du lieu d'affectation.
L'aperçu avant application affiche :
- le périmètre retenu ;
- les vacances prises en compte ;
- les créneaux créés ;
- les créneaux ignorés ;
- les conflits éventuels.
Le mode **Compléter** et le mode **Remplacer les jours concernés** restent disponibles.
Aucune migration SQL n'est nécessaire depuis la V7.5.

19
docs/README_V7_7.md Normal file
View File

@@ -0,0 +1,19 @@
# PTA V7.7 — Validation annuelle depuis la Vue annuelle
## Fonctionnalité
Réservé au rôle **Service Enfance** : la page `annual.php` propose en bas du tableau annuel le bouton **Valider toutes les semaines de l’année**.
Le traitement :
1. recherche tous les plannings en brouillon de l’agent contenant des créneaux dans l’année civile affichée ;
2. contrôle toutes les semaines avant toute écriture ;
3. si une erreur est présente, **aucune semaine n’est validée** ;
4. les semaines en erreur sont listées avec un bouton **Corriger cette semaine** ;
5. ce bouton ouvre `planning.php` dans un **nouvel onglet**, préchargé sur le bon agent, le bon lieu et la bonne semaine ;
6. la Vue annuelle reste donc ouverte pour servir de tableau de contrôle ;
7. une fois les corrections effectuées, il suffit de relancer la validation annuelle ;
8. si seul le quota annuel est dépassé, une confirmation explicite est demandée ;
9. si tout est correct, tous les plannings en brouillon concernés passent à `VALIDE` en une seule opération SQL.
Aucune migration SQL n’est nécessaire.

139
docs/README_V7_8.md Normal file
View File

@@ -0,0 +1,139 @@
# PTA V7.8 — Correctifs de sécurité et descente des droits en base
Cette version ne change aucune règle métier. Elle corrige les failles relevées
lors de l'audit et déplace l'intégralité des règles d'accès dans la base.
## Failles corrigées
### 1. phpMyAdmin ouvert sans mot de passe (critique)
`docker-compose.yml` fournissait `PMA_USER` et `PMA_PASSWORD` au conteneur
phpMyAdmin. Ces deux variables font basculer phpMyAdmin en `auth_type=config` :
toute personne atteignant le port arrivait sur une session **déjà
authentifiée**, avec accès complet en lecture et en écriture à la base. Le
`.env` publiait de surcroît ce port sur `0.0.0.0`, donc sur tout le réseau.
Les deux variables ont été retirées et le port est revenu sur `127.0.0.1`.
phpMyAdmin réclame désormais un identifiant MariaDB à chaque connexion. Pour y
accéder depuis un poste distant : `ssh -L 8081:127.0.0.1:8081 utilisateur@serveur`.
### 2. XSS stocké via les attributs HTML
`PTA.utils.escapeHtml` s'appuyait sur `textContent` / `innerHTML`, qui
n'échappent ni `"` ni `'`. La fonction était pourtant utilisée dans une
vingtaine de contextes d'attribut (`value="…"`, `title="…"`, `aria-label="…"`).
Un libellé contenant un guillemet permettait de sortir de l'attribut et
d'injecter du script.
Les vecteurs réalistes étaient les libellés de vacances scolaires — importés
depuis une API externe — ainsi que les noms de lieux, les noms d'agents, les
libellés de diplôme et les commentaires PTA.
`escapeHtml` échappe désormais les cinq caractères et reste valable en contexte
texte comme en contexte attribut. L'implémentation dupliquée `escapeAttr` de
`annual-overview.js` a été supprimée au profit de la fonction commune.
### 3. Règles de refus nginx sans effet
Les blocs interdisant `config.php`, `db.php` et `page_bootstrap.php` étaient
placés **après** `location ~ \.php$`. nginx retenant la première expression
régulière correspondante, ces refus n'ont jamais été appliqués : les trois
fichiers d'amorçage étaient exécutables depuis le navigateur.
L'ordre des blocs a été corrigé et la couverture élargie (`/docker/`, fichiers
`.env`, `.sql`, `.ini`, `.log`, `.md`).
### 4. Divulgation d'informations techniques
`PTA_DEBUG` valait `1` par défaut : le message des exceptions — requêtes SQL,
noms de tables, chemins serveur — était renvoyé au navigateur. Les pages
d'erreur de `page_bootstrap.php` et de `role.php` affichaient de la même façon
`getMessage()`, et `db.php` y ajoutait l'hôte, l'utilisateur et le nom de la
base issus du message PDO.
Le mode debug est désormais désactivé par défaut. Tout détail technique est
journalisé côté serveur via `App\Core\Security::reportThrowable()` et n'atteint
le client que si `PTA_DEBUG` est explicitement activé.
### 5. Absence d'en-têtes de sécurité
Les réponses HTML portent maintenant une `Content-Security-Policy` **à nonce** :
`script-src 'self' 'nonce-…'`, sans `unsafe-inline`. PTA ne contient aucun
gestionnaire d'événement inline ni style inline, la politique peut donc rester
stricte. S'y ajoutent `X-Content-Type-Options`, `X-Frame-Options`,
`Referrer-Policy`, `Permissions-Policy`, `Cross-Origin-Opener-Policy` et, en
HTTPS, `Strict-Transport-Security`.
Tout nouveau `<script>` inline doit porter `nonce="<?= $cspNonce ?>"`, sinon le
navigateur refusera de l'exécuter.
### 6. Session et CSRF
`session_start()` était appelé à trois endroits sans paramètres de cookie.
`App\Core\Session::start()` centralise le démarrage et impose `HttpOnly`,
`SameSite=Lax`, `use_strict_mode` et `secure` en HTTPS — y compris hors du
conteneur Docker, où le `php.ini` fourni ne s'applique pas.
Le changement de rôle (`role.php?action=reset`), qui modifiait l'état de la
session via un simple GET, exige désormais le jeton CSRF. Le jeton est
régénéré à chaque changement de rôle.
### 7. Secrets versionnables
Le dépôt n'avait ni `.gitignore` ni `.dockerignore`. Le `.env` contenant les
mots de passe MariaDB était prêt à être commité, et `COPY . /var/www/html`
copiait `.env` et `.git` dans l'image publiée. Les deux fichiers ont été créés.
## Descente des droits en base
`app/Core/Access.php` ne contient plus aucune règle : la matrice des droits et
les périmètres de visibilité sont lus dans des vues SQL.
| Vue | Rôle |
| ------------------------------ | ----------------------------------------------------------- |
| `v_acces_role_page` | pages autorisées par rôle |
| `v_acces_role_permission` | permissions fonctionnelles par rôle |
| `v_acces_identite_agent` | identités d'agents actifs sélectionnables |
| `v_acces_identite_structure` | lieux d'affectation actifs sélectionnables |
| `v_acces_agent_par_structure` | quels agents un lieu peut consulter (rattachement ou planif.) |
| `v_acces_perimetre_creneau` | lieu et planning d'un créneau, avant modification |
| `v_acces_perimetre_planning` | lieu et agent d'un planning, avant validation |
Les deux premières s'appuient sur les tables `acces_role_page` et
`acces_role_permission` : **modifier un droit se fait désormais en SQL, sans
redéploiement**. Une permission absente de la base vaut « refusé ».
`SchemaModel::assertCurrent()` vérifie la présence des sept vues au démarrage.
Si la migration n'a pas été jouée, l'application refuse de démarrer plutôt que
de tourner sans contrôle de périmètre.
## Installation
La migration est **appliquée automatiquement au déploiement**. Le service jetable `db-migrate`
du compose joue `database/migration_v7_8_securite_acces.sql` dès que MariaDB est sain, et le
service `php` n'est démarré qu'après sa sortie en succès.
1. Sauvegarder la base.
2. Déployer :
```bash
docker compose up -d --build
docker compose logs db-migrate # doit afficher « Base à jour »
```
La migration est idempotente et suivie dans la table `schema_migration` : un second déploiement
l'ignore. Elle reste jouable à la main si besoin :
```sql
SOURCE database/migration_v7_8_securite_acces.sql;
```
## Reste à traiter
Le sélecteur de `role.php` n'est toujours **pas une authentification** :
n'importe quel visiteur peut choisir « Service Enfance » et obtenir le contrôle
complet de l'application. Tant que ce point n'est pas traité, aucune des
corrections ci-dessus ne protège une instance exposée sur Internet.
Le mot de passe MariaDB doit également être changé : le `.env` reprend encore la
valeur d'exemple.

118
docs/README_V7_9.md Normal file
View File

@@ -0,0 +1,118 @@
# PTA V7.9 — Parcours et lisibilité du Service Enfance
## Le problème traité
Le rôle **Service Enfance** ouvre onze écrans. Jusqu'ici :
- il atterrissait sur **Saisie**, alors que son travail commence par le contrôle et non par la saisie ;
- les onze onglets étaient alignés à plat, sans hiérarchie : rien ne distinguait une tâche
quotidienne (« À valider ») d'un référentiel modifié deux fois par an (« Administration ») ;
- chaque bandeau de page affichait le **même** sous-titre générique, il fallait donc ouvrir un écran
pour savoir à quoi il servait ;
- les chiffres qui déclenchent une action (brouillons en attente, manques de couverture, quotas)
vivaient sur trois écrans distincts : personne ne voyait l'état général sans faire le tour ;
- la pastille « À valider » de la barre de navigation n'était alimentée que **sur la page
« À valider » elle-même**, donc invisible là où elle aurait servi.
## Ce qui change
### 1. Un tableau de bord comme page d'accueil
Nouvelle page `dashboard.php`, **réservée au Service Enfance**, qui devient sa page d'atterrissage.
- **Quatre tuiles d'action**, vertes quand il n'y a rien à faire, rouges dès qu'un compteur remonte :
plannings à valider, manques de couverture de la semaine, quotas dépassés, lieux sans horaires de
couverture. Chaque tuile est un lien vers l'écran de traitement.
- **« À valider en priorité »** : les cinq brouillons dont la semaine est la plus ancienne, avec un
bouton **Contrôler** qui ouvre directement l'écran de saisie sur le bon agent, le bon lieu, la
bonne semaine, curseur sur la validation.
- **« Couverture à renforcer »** : les cinq plages les plus déficitaires, triées par nombre d'agents
manquants, avec un lien vers l'écran de couverture déjà filtré.
- **« Suivi des quotas annuels »** : consommation globale, reste à affecter, agents sans aucune heure,
et le détail par agent avec une jauge et un lien vers sa vue annuelle.
- **« Actions fréquentes »** : six raccourcis vers les écrans les plus utilisés.
Un sélecteur de semaine en haut de page rejoue l'ensemble des indicateurs de couverture, et indique
si la semaine est scolaire, de vacances, ou à cheval sur les deux.
### 2. Navigation regroupée par intention
Les onglets sont désormais rangés en quatre groupes : **Piloter** (tableau de bord, à valider,
couverture), **Planifier** (saisie, équipes), **Consulter** (par agent, par lieu, vue annuelle, PTA
détaillé), **Paramétrer** (vacances, administration).
Les rôles qui n'ont que quelques pages — Agent (2), Responsable de structure (4) — gardent la barre
simple d'avant, sans titres de groupe. Une page autorisée en base mais absente des groupes reste
affichée dans un groupe « Autres » plutôt que de disparaître.
### 3. Chaque page dit ce qu'on y fait
Le sous-titre du bandeau est propre à chaque écran et décrit l'action, pas le produit.
### 4. La pastille « À valider » suit partout
Elle est alimentée depuis n'importe quel écran par un compteur léger
(`api/dashboard_summary.php?scope=badges`). L'échec de cet appel est silencieux : une commodité ne
doit jamais empêcher une page de fonctionner.
## Ce qui ne change pas
Aucune règle métier, aucun calcul, aucun droit n'est modifié.
Le tableau de bord ne fait que **relire** ce que les autres écrans calculent déjà : la file de
validation vient de `PlanningModel::pendingValidation()`, la couverture de
`CoverageModel::reportForStructure()`, et le quota reprend exactement la logique de `QuotaModel`
(année civile, motifs décomptés uniquement, quota de l'année la plus proche à défaut de l'année
exacte) — un agent ne peut donc pas afficher deux valeurs différentes selon l'écran consulté.
L'endpoint `api/dashboard_summary.php` est gardé par `requireService()` : 403 pour les autres rôles,
401 sans session.
## Migration
Aucun geste manuel : `database/migration_v7_9_tableau_de_bord.sql` est appliqué au démarrage par le
service `db-migrate`, qui joue tous les `database/migration_*.sql` dans l'ordre. Un simple
```bash
docker compose up -d --build
```
suffit, sur base neuve comme sur base existante. La migration insère une seule ligne dans
`acces_role_page` et est idempotente (`ON DUPLICATE KEY UPDATE`).
Si la migration n'était pas jouée, l'onglet n'apparaîtrait pas, le Service Enfance atterrirait comme
avant sur l'écran de saisie et `dashboard.php` renverrait vers l'accueil : l'application resterait
entièrement fonctionnelle, simplement sans tableau de bord.
Retour arrière :
```sql
DELETE FROM acces_role_page WHERE role = 'SERVICE_ENFANCE' AND page = 'dashboard';
DELETE FROM schema_migration WHERE fichier = 'migration_v7_9_tableau_de_bord.sql';
```
## Fichiers
| Ajouté | Rôle |
| --- | --- |
| `dashboard.php` | point d'entrée de la page |
| `api/dashboard_summary.php` | endpoint JSON |
| `app/Controllers/DashboardController.php` | agrégation et gardes d'accès |
| `app/Models/DashboardModel.php` | suivi des quotas et compteurs |
| `app/Views/pages/dashboard.php`, `app/Views/partials/dashboard.php` | vue |
| `assets/js/dashboard.js` | module `PTA.modules.dashboard` |
| `database/migration_v7_9_tableau_de_bord.sql` | droit d'accès à la page |
| Modifié | Raison |
| --- | --- |
| `docker-compose.yml` | service `db-migrate`, port interne de la base rétabli à 3306, défaut de mot de passe aligné entre `db` et `php` |
| `docker/db/apply-migrations.sh` | application automatique et idempotente des migrations |
| `app/Core/Access.php` | `homeUrl()` vise le tableau de bord, avec repli si la migration manque |
| `app/Controllers/PageController.php` | méthode `dashboard()` et sous-titre propre à chaque page |
| `app/Views/layouts/header.php` | navigation regroupée, sous-titre contextuel |
| `app/Views/layouts/footer.php`, `app/Views/pages/role.php` | versions d'assets `?v=7.9` |
| `assets/js/page-bootstrap.js` | pastille « À valider » sur toutes les pages |
| `assets/style.css` | styles de la navigation groupée et du tableau de bord |
Les jauges de quota utilisent l'élément natif `<progress>` : la CSP interdit l'attribut `style`, une
largeur ne peut donc pas être calculée en JavaScript sur une simple `div`.