ajoute les pages légales, la purge RGPD et sert polices et scripts depuis le portail
Pages /legal et /confidentialite réécrites d'après le système réel, purge des classes archivées depuis 90 jours (prouvée par Pest), archivage côté prof. Polices, xterm.js et CodeMirror ne sont plus chargés depuis Google ni jsDelivr. Pages d'aperçu signalées, sans vrais établissements ni faux chiffres. Journaux Laravel gardés 14 jours. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0a5df89201
commit
e4a6aaebb1
62 changed files with 822 additions and 238 deletions
|
|
@ -11,13 +11,17 @@ retirer la ligne (et le noter dans le journal) quand c'est fait.
|
|||
| Clé age des sauvegardes : copie hors du serveur non confirmée | Sans elle, aucune sauvegarde n'est déchiffrable si le serveur est perdu (voir `docs/runbooks/sauvegardes.md`). Adam doit confirmer l'avoir mise en lieu sûr. | Dès que possible | Étape 3 |
|
||||
| Larastan (PHPStan niveau 6) absent | Exigé par le CLAUDE.md depuis l'étape 8, jamais installé. | Bloc B | Étape 8 |
|
||||
| Vitrine : Tailwind par CDN d'exécution (`cdn.tailwindcss.com`, 19 pages) | Non destiné à la production selon Tailwind, lent, et oblige la vitrine à tolérer les scripts en ligne dans sa CSP. Compiler ces styles avec Vite. | Avant ouverture publique | Étape 12 (design) |
|
||||
| Vitrine : 52 scripts en ligne, Google Fonts, jsDelivr (démos xterm, CodeMirror) | CSP vitrine avec `'unsafe-inline'` ; CDN parfois filtrés par les réseaux scolaires. Auto-héberger polices et bibliothèques, passer les scripts en fichiers. | Avant ouverture publique | Étape 12 |
|
||||
| Vitrine : 52 scripts en ligne ; Pyodide et sql.js depuis jsDelivr (`/ide`) ; vidéo YouTube possible (`/cours/…/cm`) | CSP vitrine avec `'unsafe-inline'` ; tiers déclarés dans `/confidentialite`. Polices, xterm.js et CodeMirror sont auto-hébergés depuis l'étape 14. Héberger Pyodide/sql.js, passer les scripts en fichiers. | Avant ouverture publique | Étapes 12 et 14 |
|
||||
| xterm.js 5.5.0 (6.0.0 disponible) | Version majeure à évaluer séparément. | Bloc B | Étape 12 |
|
||||
| NodeSource installé par `curl … \| bash` | Script distant exécuté en root au premier déploiement. Déclarer le dépôt (deb822) avec une clé vérifiée. | Bloc B | Étape 8 |
|
||||
| Aucune vérification dans un vrai navigateur | Extension Chrome non connectée (étapes 8 et 12). La CSP n'est contrôlée que par ses rapports. | Dès que possible | Étapes 8 et 12 |
|
||||
| Aucune vérification dans un vrai navigateur | Extension Chrome non connectée (étapes 8, 12 et 14). La CSP n'est contrôlée que par ses rapports. À vérifier en priorité : `/ide` (Pyodide, WebAssembly) et les pages vitrine qui utilisent xterm.js/CodeMirror désormais servis par le portail. | Dès que possible | Étapes 8, 12, 14 |
|
||||
| Pas de sonde hors du serveur | Si le serveur tombe, personne n'est prévenu. | À décider avec Adam | Étape 7 |
|
||||
| Kolla installé depuis une branche, pas un tag | Reproductibilité. | Montée de version | ADR 0001 |
|
||||
| Correction séquentielle (~5 s pour le lab 08) | Acceptable ; à paralléliser si la charge l'exige. | Bloc B | Étape 11 |
|
||||
| Jeton de console : ≈ 1,1 s par appel Nova `remote-consoles` | Plus gros poste de l'ouverture d'un lab (2,8 s au total, critère < 5 s tenu). Préparer les jetons à l'attribution si besoin. | Bloc B | Étape 13 |
|
||||
| Tâches Ansible toujours « modifiées » (composer, npm, build, cache) | Brouille la lecture de l'idempotence. | Bloc B | Étape 8 |
|
||||
| `Permissions-Policy` ferme caméra et micro | À ouvrir pour la visio. | Étape 15 | Étape 12 |
|
||||
| Mentions légales à compléter | Forme juridique, SIREN et adresse du siège d'Esquisse Formation ; délégué à la protection des données s'il en est désigné ; article « Disponibilité et responsabilité » des CGU. | Avant le pilote | Étape 14 |
|
||||
| Slogans « du lycée au master » | Le catalogue va aujourd'hui du lycée au BUT. Positionnement laissé tel quel : décision éditoriale d'Adam. | À décider | Étape 14 |
|
||||
| Ancien `storage/logs/laravel.log` | Fichier unique d'avant la rotation quotidienne (étape 14) : jamais purgé, à supprimer ou archiver (accord d'Adam). | Dès que possible | Étape 14 |
|
||||
| Pages d'aperçu publiques (32) | Signalées comme aperçus avec données fictives ; à retirer ou remplacer au fur et à mesure que les fonctions réelles arrivent. | Au fil du bloc B | Étape 14 |
|
||||
|
|
|
|||
|
|
@ -566,3 +566,37 @@ Un serveur qui « marche » n'est pas un serveur qui *revient* : trois valeurs p
|
|||
**Suite**
|
||||
|
||||
Étape 14 : pages légales et RGPD.
|
||||
|
||||
## 2026-10-09 — Étape 14 : pages légales et RGPD
|
||||
|
||||
**Fait**
|
||||
|
||||
- **Purge RGPD** : bouton « Archiver la classe » côté prof (plus de séance possible, date de suppression affichée, désarchivage possible d'ici là) et commande `classes:purger-archivees`, chaque nuit : une classe archivée depuis 90 jours est supprimée avec, en cascade, ses élèves, séances, sessions, machines et corrections. Le compte du prof reste ; le journal ne garde ni nom ni prénom.
|
||||
- **Pages légales réécrites d'après le système réel** : `/legal` (mentions légales : Esquisse Formation, SIREN et adresse à compléter par Adam ; hébergeur vérifié à la source, mentions légales de Scaleway ; CGU) et `/confidentialite` (données réellement traitées, finalités et bases légales, durées vérifiées sur le serveur, hébergement et tiers, sécurité, cookies, droits, sous-traitance). `/statut` redirige vers la page de statut.
|
||||
- **Plus aucun envoi d'adresse IP chez Google** : Archivo et IBM Plex Mono sont téléchargées à la compilation et servies par le portail (`Vite::fonts()`), au lieu de Google Fonts, chargé jusque-là sur **toutes** les pages, espace élève compris. xterm.js et CodeMirror servis depuis `public/vendor/` (copiés des paquets npm à la compilation) au lieu de jsDelivr.
|
||||
- **Journaux** : Laravel passe d'un fichier unique jamais purgé à un fichier par jour gardé 14 jours (comme Nginx).
|
||||
- **Interdits du prompt** : aucun logo Marianne, ministère, drapeau de l'UE ni « logo RGPD » sur le site. Bandeau « Aperçu d'une fonction en préparation — données fictives » sur les 32 pages d'aperçu (choix d'Adam) ; vrais établissements des tableaux de bord remplacés par des noms fictifs ; « 20 cursus » corrigé en « 6 cursus » ; « 99 cours » présenté comme un programme en cours de publication ; « Conforme au RGPD » relié à `/confidentialite` ; « Hébergé en France · Paris » ramené à « Hébergé en France » (commune du datacenter non vérifiable sur le site de Scaleway).
|
||||
- 8 nouveaux tests Pest (33 au total) ; runbook `docs/runbooks/rgpd.md`.
|
||||
|
||||
**Problèmes rencontrés et solution**
|
||||
|
||||
1. **La page `/legal` du design décrivait un système qui n'existe pas** : « aucun traceur tiers » et « les données ne quittent pas l'UE » alors que Google Fonts était chargé partout, un « mode examen » qui enregistre les changements d'onglet (non construit), « temps passé, réponses aux QCM » (non collectés). Réécrite affirmation par affirmation.
|
||||
2. **Faux clients et chiffres inventés** dans les pages d'aperçu (« Lycée Vauban Brest », « 608 élèves »…). Mon premier test cherchait nom et ville collés et a laissé passer les tableaux où ils sont dans deux cellules (« Lycée Vauban | Brest ») : test réécrit pour chercher chaque nom seul, sur les 32 aperçus — il a ensuite trouvé `/paiement` et les dernières lignes de `/academie`.
|
||||
3. **La CSP de l'étape 12 cassait probablement l'exécution de code sur `/ide`** (Pyodide doit télécharger ses fichiers et compiler du WebAssembly) : `connect-src` et `'wasm-unsafe-eval'` ajoutés pour la vitrine. Non vérifié dans un navigateur (extension non connectée).
|
||||
4. Deux erreurs dans mes tests : `toContain($texte, $message)` de Pest traite le second argument comme un autre texte à trouver ; le test « toutes les pages en 200 » ne prévoyait pas la redirection voulue de `/statut`.
|
||||
5. Tinker lancé avec un fichier est resté bloqué en attente de saisie : remplacé par des tests Pest permanents (aucune police externe sur aucune page ; aucune ressource externe sur les pages de l'application).
|
||||
|
||||
**Critère vérifié**
|
||||
|
||||
- Pages en ligne : `/legal` 200, `/confidentialite` 200, `/statut` → 302 vers `https://status.syllabuslab.fr/` ; page de statut publique opérationnelle (étape 12).
|
||||
- **La purge fonctionne, prouvée par Pest** (`PurgeRgpdTest`, 5 tests) : classe archivée depuis 91 jours supprimée avec élèves, séances, sessions, machines et corrections, compte du prof conservé ; classes archivées depuis 89 jours et classes actives épargnées ; seules les classes concernées touchées ; archivage par le prof, refus pour un autre prof. En production : `classes:purger-archivees` → « 0 classe(s) purgée(s) » (aucune classe archivée), planifiée à 02:30.
|
||||
- `/connexion` ne charge plus que des ressources de `syllabuslab.fr` ; aucune mention de Google Fonts sur l'accueil ; 0 violation CSP depuis le déploiement.
|
||||
|
||||
**Comprendre**
|
||||
|
||||
Une politique de confidentialité n'est pas un texte juridique à recopier : c'est une description exacte de ce que fait le système, et elle engage. Celle du design promettait « aucun traceur tiers » pendant que chaque page, y compris celles des élèves mineurs, envoyait l'adresse IP du visiteur à Google pour charger une police. Le bon réflexe est de partir de la réalité (que collecte-t-on ? où ? combien de temps ? qui d'autre la voit ?), de la vérifier sur le serveur, puis de corriger le système quand la réalité n'est pas défendable — ici, servir les polices nous-mêmes — plutôt que d'adapter le texte. La purge suit la même logique : une durée de conservation écrite n'a de valeur que si un programme l'applique, et un test prouve qu'il l'applique.
|
||||
|
||||
**Suite**
|
||||
|
||||
- Adam : compléter forme juridique, SIREN, adresse du siège (et DPO s'il y en a un) dans `/legal` et `/confidentialite` ; vérifier `/ide` dans un navigateur.
|
||||
- Étape 15 : visio de classe — seulement après validation des étapes 1 à 11 (le prompt l'exige).
|
||||
|
|
|
|||
41
docs/runbooks/rgpd.md
Normal file
41
docs/runbooks/rgpd.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Runbook — Données personnelles (RGPD)
|
||||
|
||||
Politique publique : `https://syllabuslab.fr/confidentialite`
|
||||
(`portail/resources/views/confidentialite.blade.php`). **Toute nouvelle donnée
|
||||
collectée, tout nouveau service tiers ou toute nouvelle durée de conservation
|
||||
doit d'abord y être décrit.** La page affirme seulement ce qui a été vérifié.
|
||||
|
||||
## Où sont les données
|
||||
|
||||
| Donnée | Où | Durée |
|
||||
|---|---|---|
|
||||
| Comptes profs, classes, élèves, séances, sessions, corrections | MariaDB du portail (`portail_mariadb`) | jusqu'à 90 jours après l'archivage de la classe |
|
||||
| VM des élèves (disques) | OpenStack, une stack par session | fin de session (45 min par défaut) |
|
||||
| Journaux Nginx (IP, pages) | `/var/log/nginx/` | 14 jours (logrotate) |
|
||||
| Journaux Laravel | `storage/logs/laravel-AAAA-MM-JJ.log` | 14 jours (`LOG_STACK=daily`, `LOG_DAILY_DAYS=14`) |
|
||||
| Sauvegardes chiffrées | Dedibackup (Scaleway, France) | 7 quotidiennes + 4 hebdomadaires (≤ 5 semaines) |
|
||||
|
||||
## Purge automatique
|
||||
|
||||
- Le prof archive sa classe (page de la classe, « Archiver la classe ») : plus de séance possible ; la date de suppression est affichée.
|
||||
- `classes:purger-archivees` (chaque nuit à 02:30 UTC) supprime les classes archivées depuis 90 jours, et en cascade leurs élèves, séances, sessions, machines et corrections. Le compte du prof reste. Le journal ne garde que l'identifiant de la classe et le nombre d'élèves.
|
||||
- Une classe avec un lab encore en cours est reportée (anomalie : un lab expire en 45 min) et signalée dans le journal.
|
||||
- Preuve : `portail/tests/Feature/PurgeRgpdTest.php`.
|
||||
|
||||
```
|
||||
ssh paillasse "cd /var/www/portail && sudo -u www-data HOME=/tmp php8.3 artisan classes:purger-archivees"
|
||||
```
|
||||
|
||||
## Répondre à une demande d'exercice des droits
|
||||
|
||||
Contact public : adam@belkacemi.me. Pour un élève, la demande passe normalement par son établissement (responsable du traitement).
|
||||
|
||||
- **Accès / portabilité (élève)** : prénom, nom, identifiant, classe, sessions et corrections ; extraire avec `artisan tinker` (`App\Models\Eleve::with('labSessions.corrections')->where('identifiant', '…')->first()->toArray()`), sans le code secret (haché, inutile).
|
||||
- **Effacement d'un élève** : `App\Models\Eleve::where('identifiant', '…')->first()->delete()` — cascade sur ses sessions et corrections.
|
||||
- **Effacement d'un compte prof** : supprimer d'abord ses classes (ou les archiver), puis `App\Models\User::where('email', '…')->first()->delete()`.
|
||||
- Noter la demande et sa date de traitement (hors du dépôt : le registre contient des données personnelles).
|
||||
- Rappeler que les sauvegardes chiffrées contenant la donnée expirent d'elles-mêmes sous 5 semaines.
|
||||
|
||||
## Interdits sur le site (prompt de déploiement, étape 14)
|
||||
|
||||
Pas de logo Marianne ou ministère, de drapeau de l'UE, de faux « logo RGPD », de chiffres inventés ni de faux clients. Formulations autorisées : « Hébergé en France », « Conforme au RGPD » avec un lien vers `/confidentialite`. Les pages d'aperçu (fonctions pas encore construites) portent le bandeau `partials/apercu` et n'affichent que des noms fictifs (`PagesLegalesTest`).
|
||||
Loading…
Add table
Add a link
Reference in a new issue