+7
−1
+55
−1
+2
−0
Loading
Étape 4 du socle multi-organisations. L'inscription publique existe, fermée par
défaut, ouvrable en modérée ou en libre-service. Les deux formes partagent le même
socle — passer de l'une à l'autre ne fait que retirer l'étape d'approbation.
Le point dur était annoncé : le produit n'envoyait AUCUN courriel, et c'est une
promesse écrite dans SECURITY.md. La sortie n'est pas d'y renoncer mais de scinder
la promesse en deux. `platform-mail` envoie les messages qui font vivre les
comptes ; il n'est jamais appelé depuis le moteur d'exercice — vérifiable par grep,
et c'est désormais documenté comme un invariant — et il reste inerte sans SMTP
configuré, journalisant ce qu'il aurait envoyé sans aucun appel réseau. Le moteur
d'exercice reste hermétique ; la plateforme qui l'héberge ne peut plus l'être.
Un seul mécanisme pour trois usages (activation, mot de passe oublié, invitation) :
un jeton de 32 octets dont seule l'empreinte SHA-256 est stockée, comme les liens
d'accès joueur. La primitive est maintenant partagée — `common/one-time-token` —
plutôt que dupliquée.
Ce que je me suis interdit :
- Aucun oracle d'énumération. « Mot de passe oublié » répond pareil pour une
adresse connue et inconnue ; un lien invalide, expiré ou déjà consommé donne le
même message. Distinguer ces cas permettrait de tester l'existence des comptes.
- Consommer un lien invalide tous les autres liens d'activation et de
réinitialisation en cours pour cette adresse. Un lien encore valide après un
changement de mot de passe serait une porte laissée ouverte.
- `passwordHash` d'un compte en attente d'activation est le hash bcrypt d'un
secret jeté, et non une valeur bidon : `compare` doit pouvoir le lire pour
répondre « faux » proprement plutôt que lever.
Le mot de passe ne transite plus en clair quand le courriel est configuré — c'était
le trou que j'avais signalé. Sans SMTP, le comportement historique subsiste, et la
réponse dit lequel des deux a été appliqué.
Limitation de débit sans nouvelle dépendance : fenêtre glissante, module pur testé
(une fenêtre fixe autorise deux fois le plafond à cheval sur sa frontière). Le
compteur vit en mémoire du processus — garde-fou contre l'abus ordinaire, pas
contre une attaque distribuée ; c'est écrit dans le code et dans SECURITY.md.
Les invitations ferment la porte laissée ouverte à l'étape 1 : un admin
d'organisation peut enfin ajouter quelqu'un qui a un compte ailleurs, mais par
consentement — l'intéressé accepte lui-même.
nodemailer 6.9.16 (MIT) est la seule dépendance ajoutée, épinglée et inscrite dans
DEPENDENCIES.md avec sa licence, sa maintenance, et le fait qu'elle est la seule
capable d'émettre du trafic sortant.
À FAIRE côté exploitant : `.env.example` doit recevoir SMTP_HOST, MAIL_FROM,
SMTP_PORT, SMTP_SECURE, SMTP_USER, SMTP_PASSWORD. Je n'ai pas pu l'éditer, ce
fichier étant hors de mes permissions ; les variables sont documentées dans
SECURITY.md.
Non vérifié : aucune chaîne Node dans cet environnement, donc rien n'a été compilé
ni testé, et `pnpm install` reste à lancer pour nodemailer. Contrôles manuels :
équilibre des délimiteurs, absence de cycle entre modules Nest, 644 clés i18n sans
doublon, classes CSS présentes, tables hors cloisonnement bien absentes de
TENANT_MODELS, aucun appel du mailer depuis le moteur.
Co-Authored-By:
Claude (RCA) <noreply@anthropic.com>