ba6482d4ffc86a224080bc517f4da0482aae8ebf
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
92a0ebf853 |
Configurer les rôles et leurs capacités
Critère d'acceptation de WP-01 resté sans écran : « un rôle personnalisé créé par un client modifie effectivement l'accès, sans changement de code ». Le catalogue de capacités existait, l'autorisation s'y adossait déjà, mais seul le semis pouvait attribuer quoi que ce soit. Deux dangers opposés, et il faut se protéger des deux. L'escalade d'abord : on ne peut accorder qu'une capacité qu'on détient soi-même, sans quoi la première personne autorisée à éditer un rôle s'accorde l'accès aux rémunérations en trois clics et le catalogue ne sert plus à rien. Le retrait, lui, reste permis même sur une capacité qu'on n'a pas — réduire un droit n'a jamais élargi le sien, et l'interdire empêcherait de corriger un rôle trop large. Le verrouillage ensuite : au moins un rôle doit conserver la gestion des droits. La condition se juge sur l'ensemble des rôles après coup, non sur celui qu'on édite — ce qui compte n'est pas que ce rôle-ci garde la capacité, mais qu'un rôle la garde. Sans cela une organisation se fermerait dehors, et le seul recours serait une intervention en base. Un rôle naît sans aucune capacité : en hériter de celles de son créateur distribuerait des droits que personne n'a demandés. Le niveau propriétaire se délègue à part. Un rôle système ne se supprime pas, un rôle attribué non plus — ses membres se retrouveraient dehors sans que personne l'ait décidé pour eux. Le test va jusqu'au bout : créer le rôle, l'attribuer, se connecter, constater qu'un écran s'ouvre et qu'un autre reste fermé. S'arrêter à « la case est cochée » n'aurait rien dit du critère. Trois défauts d'isolation des tests corrigés au passage, tous du même genre — un état qui s'accumule d'une exécution à l'autre : - la file des congés désignait « la première ligne de ce salarié » pour son nettoyage, et tombait sur une ligne laissée par un passage antérieur ; - l'écran de conservation tronquait à quarante pièces triées par ancienneté, faisant disparaître les pièces échues derrière celles dont il n'y a rien à dire — corrigé dans le produit, pas seulement dans le test ; - le test de verrouillage de période consommait un mois par exécution sans jamais le rendre. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv |
||
|
|
6f3b630ced |
Déclarer et appliquer les durées de conservation
La table RetentionPolicy existait et cinq durées y étaient semées depuis la matrice ; rien ne les appliquait. Une durée déclarée que personne n'exécute est une conformité de papier. Aucune durée par défaut n'est appliquée, et c'est le point central : la matrice interdit explicitement d'aligner tout sur cinq ans. Un objet sans politique déclarée se conserve, et l'écran le signale plutôt que de le taire. Symétrie inverse, tout aussi importante : effacer faute de règle serait aussi fautif que garder indéfiniment. La justification est obligatoire au niveau du serveur. Une durée sans motif est une durée qu'on ne saura pas défendre le jour d'un contrôle. Les politiques sont effectif-datées comme le reste de l'application : une pièce déposée en mars relève de la règle en vigueur en mars. Sans cela, un durcissement rétroactif purgerait ce que la règle du moment autorisait à garder. La résolution va du précis au général — Document:SICK_NOTE avant Document — car un arrêt de travail et un contrat n'ont aucune raison de se conserver aussi longtemps. Trois refus distincts plutôt qu'un seul : absence de politique, conservation suspendue à titre probatoire, échéance non atteinte. Les confondre sous « rien à purger » empêcherait de vérifier que la conservation est réellement tenue. Un quatrième existe : employee_departure est déclaré mais non calculable, PlanFlow ne modélisant pas de date de départ — purger sur une date inventée serait pire que ne pas purger, et l'écran l'affiche comme tel. La purge s'exécute en ligne de commande pour une tâche planifiée, la matrice demandant des purges automatiques ; un bouton qu'il faut penser à presser n'en est pas une. Elle passe par le client scopé et la RLS, compte par compte. Les tables append-only en sont exclues par construction : le journal d'audit doit survivre aux données qu'il décrit, sans quoi on ne pourrait plus démontrer que la purge a eu lieu. L'échéance affichée est dérivée de la politique, jamais stockée — même discipline que la péremption d'un export. Deux pièges rencontrés : un objet de composants exporté depuis un module client ne survit pas au passage par un composant serveur, React n'en recevant qu'un undefined ; et le minLength du navigateur masquait le contrôle serveur de la justification, que des espaces suffisent à contourner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv |
||
|
|
2047d7f5cc |
Exiger un second facteur des rôles administrateur et RH
La matrice n° 15 l'impose ; les colonnes existaient en base depuis WP-01 mais rien ne les remplissait. TOTP (RFC 6238) plutôt qu'un code envoyé par message : le second facteur ne doit pas dépendre du canal qui sert déjà à réinitialiser le mot de passe, faute de quoi un accès à la boîte donnerait les deux d'un coup. L'algorithme est écrit ici plutôt qu'emprunté — trente lignes, et une dépendance de moins sur le chemin d'authentification. Il est éprouvé contre les vecteurs publiés de la RFC, ce qui distingue « le code change toutes les trente secondes » de « le code est celui qu'attend l'application du téléphone ». L'obligation est adossée aux capacités, pas à des rôles nommés qu'un client renomme librement : distribuer les droits, ou lire les rémunérations. Elle est tenue au point de passage de toutes les routes applicatives, où l'écran d'enrôlement remplace le contenu. Il remplace plutôt qu'il ne redirige : une redirection depuis un layout se joue aussi pendant la navigation qui suit la connexion, et Next y répond par une page vide. Le secret n'est enregistré qu'après qu'un code en a été tiré — l'enregistrer d'avance laisserait des comptes porteurs d'un facteur que leur détenteur ne sait pas produire, c'est-à-dire des comptes fermés. Le rejeu d'un code est refusé dans sa propre fenêtre : sans cela le facteur protège d'un mot de passe volé, pas d'un code lu par-dessus l'épaule. Dix codes de secours accompagnent chaque activation, conservés hachés. Et parce que PlanFlow est auto-hébergé et qu'il n'y a pas d'éditeur à appeler, un retrait depuis le serveur existe — l'accès « break glass » que demande la même ligne de la matrice. Sans lui, le second facteur deviendrait le risque principal plutôt que la protection. Un défaut trouvé par les tests, et il aurait été grave : la réactualisation de la route après activation remplaçait l'écran par celui d'un compte déjà enrôlé, emportant les codes de secours avant que leur destinataire ait pu les noter. La suite de tests suit le produit : la mise en place enrôle réellement le compte de direction et calcule les codes comme le ferait un téléphone. Les écrans qui n'éprouvent pas l'authentification ont migré vers la session commune — deux connexions parallèles sur un même compte se heurtent au refus de rejeu, ce qui est le comportement voulu et non un défaut à contourner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv |