update
This commit is contained in:
1287
docs/README.md
1287
docs/README.md
File diff suppressed because it is too large
Load Diff
@@ -1,129 +0,0 @@
|
||||
# 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.
|
||||
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