mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-17 04:06:44 +02:00
- Serveur Rust (server-rs/, axum + sqlx) : les 148 adresses de l'API, l'interface, les migrations de démarrage et les tâches de fond ; même comportement que Node (banc : 2027 cas, 0 écart hors écarts déclarés USERS-02, RAPPRO-21, R4, R5). - Node retiré : server/, dépendances serveur, image Docker sans Node (51 Mo), docker-compose à un seul service applicatif. - R4 : écritures d'un autre site refusées (Sec-Fetch-Site / Origin). - R5 : « aujourd'hui » et sauvegarde automatique à l'heure de Paris. - R6 : mainteneur dans .github/CODEOWNERS. - Menu latéral : Accueil puis Calendrier en tête. - Plan et état : docs/PLAN-MIGRATION-RUST.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ke2qyS8rgacP3ap5iezDbM
300 lines
14 KiB
TypeScript
300 lines
14 KiB
TypeScript
// Lot 5 : connexion et session, utilisateurs, magasins, sauvegardes, paramètres
|
|
// généraux, diagnostic du schéma (docs/PLAN-MIGRATION-RUST.md). Les lectures
|
|
// de /api/user, /api/default-credentials-check (socle.ts, « auth »),
|
|
// /api/users et /api/groups (referentiel.ts) sont déjà au banc.
|
|
import type { ReadCase, WriteScenario } from "../types";
|
|
import { LOGGED_ACCOUNTS } from "../types";
|
|
import { STUB_SMTP_PORT } from "../config";
|
|
|
|
// Le hash renvoyé par Node après POST / PUT /api/users (sel aléatoire) n'est pas
|
|
// renvoyé par Rust : écart voulu, retiré de la comparaison (réponses et table users)
|
|
const PASSWORD_HASH = {
|
|
fields: ["password"],
|
|
reason: "USERS-02 : l'empreinte du mot de passe n'est plus renvoyée après création ou modification d'un compte",
|
|
};
|
|
|
|
// Session d'une connexion : identifiant et échéance propres à chaque passage
|
|
// originalMaxAge : express-session le recalcule à la milliseconde près quand il
|
|
// réenregistre une session (86399999 ou 86400000 selon l'instant)
|
|
const SESSION_IGNORE = ["sid", "expire", "expires", "originalMaxAge"];
|
|
|
|
const BROWSER_ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
|
|
|
|
// Magasin dont le serveur SMTP est le service simulé du banc
|
|
const SMTP = {
|
|
smtpEnabled: true,
|
|
smtpHost: "127.0.0.1",
|
|
smtpPort: STUB_SMTP_PORT,
|
|
smtpSecure: false,
|
|
smtpUser: "toul@banc.test",
|
|
smtpSenderEmail: "toul@banc.test",
|
|
smtpSenderName: "Magasin Toul",
|
|
};
|
|
|
|
export const reads: ReadCase[] = [
|
|
{ module: "backups", path: "/api/backups" },
|
|
{ module: "backups", path: "/api/backups/backup_banc_1.sql/download" },
|
|
{ module: "backups", path: "/api/backups/backup_banc_500.sql/download", accounts: ["admin", "manager"] },
|
|
{ module: "backups", path: "/api/backups/absente.sql/download", accounts: ["admin", "manager"] },
|
|
{ module: "utilities", path: "/api/utilities" },
|
|
{ module: "admin", path: "/api/debug/log-schema" },
|
|
{ module: "admin", path: "/api/debug/download-schema" },
|
|
];
|
|
|
|
export const writes: WriteScenario[] = [
|
|
// ---------- Session ----------
|
|
{
|
|
module: "session",
|
|
name: "connexion réussie : réponse, cookie et session enregistrée",
|
|
accounts: ["anonyme"],
|
|
steps: [
|
|
{ method: "POST", path: "/api/login", body: { username: "directeur", password: "banc-directeur" } },
|
|
{ method: "GET", path: "/api/user" },
|
|
],
|
|
tables: ["sessions"],
|
|
ignore: SESSION_IGNORE,
|
|
},
|
|
{
|
|
module: "session",
|
|
name: "connexion par formulaire ou par l'adresse",
|
|
accounts: ["anonyme"],
|
|
steps: [
|
|
{ method: "POST", path: "/api/login", body: { username: "employe", password: "banc-employe" }, bodyType: "form" },
|
|
{ method: "POST", path: "/api/login?username=manager&password=banc-manager" },
|
|
{ method: "POST", path: "/api/login?username=sansmagasin", body: { password: "banc-sans-magasin" } },
|
|
],
|
|
tables: [],
|
|
},
|
|
{
|
|
module: "session",
|
|
name: "connexion refusée : identifiants mal formés",
|
|
accounts: ["anonyme"],
|
|
steps: [
|
|
{ method: "POST", path: "/api/login", body: { username: "manager", password: 123 } },
|
|
{ method: "POST", path: "/api/login", body: { username: "manager", password: true } },
|
|
{ method: "POST", path: "/api/login", body: { username: "manager", password: ["banc-manager"] } },
|
|
{ method: "POST", path: "/api/login", body: { username: ["manager"], password: "banc-manager" } },
|
|
{ method: "POST", path: "/api/login", body: { username: "manager", password: "" } },
|
|
{ method: "POST", path: "/api/login", body: { username: "MANAGER", password: "banc-manager" } },
|
|
{ method: "POST", path: "/api/login", body: { username: 0, password: "x" } },
|
|
{ method: "POST", path: "/api/login", body: { username: 12, password: "x" } },
|
|
{ method: "POST", path: "/api/login", body: ["manager", "banc-manager"] },
|
|
{ method: "POST", path: "/api/login?username=manager&username=directeur&password=banc-manager" },
|
|
{ method: "POST", path: "/api/login", body: { username: "admin", password: "admin" } },
|
|
],
|
|
tables: ["sessions"],
|
|
},
|
|
{
|
|
module: "session",
|
|
name: "connexion par-dessus une session ouverte",
|
|
steps: [
|
|
{ method: "POST", path: "/api/login", body: { username: "sansmagasin", password: "banc-sans-magasin" } },
|
|
// L'ancienne session est détruite : le cookie du compte ne vaut plus rien
|
|
{ method: "GET", path: "/api/user" },
|
|
],
|
|
tables: ["sessions"],
|
|
ignore: SESSION_IGNORE,
|
|
},
|
|
{
|
|
module: "session",
|
|
name: "déconnexion (GET et POST, selon Accept) puis lecture de l'utilisateur",
|
|
accounts: [...LOGGED_ACCOUNTS, "anonyme"],
|
|
steps: [
|
|
{ method: "GET", path: "/api/logout", headers: { accept: BROWSER_ACCEPT } },
|
|
{ method: "GET", path: "/api/user" },
|
|
{ method: "POST", path: "/api/logout", headers: { accept: "application/json" } },
|
|
{ method: "POST", path: "/api/logout" },
|
|
],
|
|
tables: ["sessions"],
|
|
},
|
|
{
|
|
// Session ouverte par la connexion, lue par l'adresse de test SMTP
|
|
module: "session",
|
|
name: "session acceptée par le test SMTP d'un magasin",
|
|
steps: [{ method: "POST", path: "/api/groups/1/test-smtp" }],
|
|
tables: [],
|
|
},
|
|
|
|
// ---------- Utilisateurs ----------
|
|
{
|
|
module: "users",
|
|
name: "créer, modifier, affecter, retirer et supprimer un utilisateur",
|
|
steps: [
|
|
{
|
|
method: "POST",
|
|
path: "/api/users",
|
|
body: { id: "bench_nouveau", username: "nouveau", email: "nouveau@banc.test", firstName: "Noé", lastName: "Nouveau", password: "banc-nouveau", role: "employee" },
|
|
},
|
|
{ method: "PUT", path: "/api/users/bench_nouveau", body: { firstName: "Noémie", lastName: "", email: "", role: "manager", password: "banc-nouveau-2" } },
|
|
{ method: "POST", path: "/api/users/bench_nouveau/groups", body: { groupId: 2 } },
|
|
{ method: "POST", path: "/api/users/bench_nouveau/groups", body: { groupId: 3 } },
|
|
{ method: "DELETE", path: "/api/users/bench_nouveau/groups/2" },
|
|
{ method: "GET", path: "/api/users" },
|
|
{ method: "PUT", path: "/api/users/bench_employee", body: { password: "banc-employe-2" } },
|
|
{ method: "DELETE", path: "/api/users/bench_nouveau" },
|
|
{ method: "GET", path: "/api/users" },
|
|
// Le mot de passe modifié est vérifiable (en dernier : la connexion
|
|
// remplace la session du compte qui rejoue le scénario)
|
|
{ method: "POST", path: "/api/login", body: { username: "employe", password: "banc-employe-2" } },
|
|
],
|
|
tables: ["users", "user_groups"],
|
|
deviation: PASSWORD_HASH,
|
|
},
|
|
{
|
|
module: "users",
|
|
name: "créer un utilisateur sans identifiant ni adresse",
|
|
accounts: ["admin", "manager"],
|
|
steps: [
|
|
{ method: "POST", path: "/api/users", body: { username: "sansid", password: "banc-sans-id", email: "" } },
|
|
{ method: "POST", path: "/api/users", body: { username: "blanc", password: "x", email: " " } },
|
|
{ method: "POST", path: "/api/users", body: { id: "", username: "sansid2", password: "x", email: null, firstName: "", role: "directeur" } },
|
|
// Le mot de passe du compte créé est vérifiable
|
|
{ method: "POST", path: "/api/login", body: { username: "sansid", password: "banc-sans-id" } },
|
|
],
|
|
tables: ["users"],
|
|
// Identifiant généré : manual_<millisecondes>_<aléatoire>
|
|
ignore: ["id"],
|
|
deviation: PASSWORD_HASH,
|
|
},
|
|
{
|
|
module: "users",
|
|
name: "utilisateurs invalides, en double ou introuvables",
|
|
steps: [
|
|
{ method: "POST", path: "/api/users", body: {} },
|
|
{ method: "POST", path: "/api/users", body: { username: "", password: "", email: 5, role: "chef" } },
|
|
{ method: "POST", path: "/api/users", body: { username: "a", password: "b", email: "pas-un-courriel", firstName: null } },
|
|
{ method: "POST", path: "/api/users", body: { username: "manager", password: "x" } },
|
|
{ method: "POST", path: "/api/users", body: { username: "autre", password: "x", email: "manager@banc.test" } },
|
|
{ method: "POST", path: "/api/users", body: { id: "bench_manager", username: "autre2", password: "x" } },
|
|
{ method: "POST", path: "/api/users", body: ["x"] },
|
|
{ method: "PUT", path: "/api/users/bench_employee", body: { role: "chef", firstName: 5 } },
|
|
{ method: "PUT", path: "/api/users/bench_employee", body: { username: "manager" } },
|
|
{ method: "PUT", path: "/api/users/bench_employee", body: { email: "directeur@banc.test" } },
|
|
{ method: "PUT", path: "/api/users/inconnu", body: { firstName: "X" } },
|
|
// Noms effacés ; adresse vide ignorée (non effacée, comme Node)
|
|
{ method: "PUT", path: "/api/users/bench_employee", body: { firstName: null, lastName: "", email: "" } },
|
|
{ method: "DELETE", path: "/api/users/admin_local" },
|
|
{ method: "DELETE", path: "/api/users/bench_employee" },
|
|
{ method: "POST", path: "/api/users/bench_manager/groups", body: { groupId: "2" } },
|
|
{ method: "POST", path: "/api/users/bench_manager/groups", body: {} },
|
|
{ method: "DELETE", path: "/api/users/bench_manager/groups/abc" },
|
|
{ method: "DELETE", path: "/api/users/bench_manager/groups/1" },
|
|
// Mot de passe vide enregistré tel quel (défaut de Node) : connexion
|
|
// impossible (en dernier : une connexion réussie remplacerait la session)
|
|
{ method: "PUT", path: "/api/users/bench_sans_magasin", body: { password: "" } },
|
|
{ method: "POST", path: "/api/login", body: { username: "sansmagasin", password: "banc-sans-magasin" } },
|
|
],
|
|
tables: ["users", "user_groups"],
|
|
deviation: PASSWORD_HASH,
|
|
},
|
|
|
|
// ---------- Magasins ----------
|
|
{
|
|
module: "groups",
|
|
name: "créer, modifier, tester et supprimer un magasin",
|
|
steps: [
|
|
{
|
|
method: "POST",
|
|
path: "/api/groups",
|
|
body: { name: "Magasin Toul", color: "#7B1FA2", address: "", phone: "", logo: "", nocodbConfigId: null, ...SMTP, smtpPassword: "secret-toul" },
|
|
},
|
|
// Node relit le mot de passe chiffré par l'autre serveur
|
|
{ method: "POST", path: "/api/groups/{{0.id}}/test-smtp" },
|
|
{ method: "PUT", path: "/api/groups/{{0.id}}", body: { name: "Magasin de Toul", phone: "03 83 00 00 05", smtpPassword: "" } },
|
|
{ method: "PUT", path: "/api/groups/{{0.id}}", body: { smtpPassword: "nouveau-secret", smtpPort: String(STUB_SMTP_PORT) } },
|
|
{ method: "POST", path: "/api/groups/{{0.id}}/test-smtp" },
|
|
{ method: "PUT", path: "/api/groups/{{0.id}}", body: { smtpSenderEmail: "", smtpPort: null } },
|
|
{ method: "POST", path: "/api/groups/{{0.id}}/test-smtp" },
|
|
{ method: "GET", path: "/api/groups" },
|
|
{ method: "DELETE", path: "/api/groups/{{0.id}}" },
|
|
],
|
|
tables: ["groups"],
|
|
// Chiffré avec un vecteur aléatoire
|
|
ignore: ["smtp_password"],
|
|
},
|
|
{
|
|
module: "groups",
|
|
name: "tester le SMTP : configuration incomplète, serveur injoignable, magasin introuvable",
|
|
steps: [
|
|
{ method: "POST", path: "/api/groups/2/test-smtp" },
|
|
{ method: "POST", path: "/api/groups/999/test-smtp" },
|
|
{ method: "POST", path: "/api/groups/abc/test-smtp" },
|
|
// Port fermé : connexion refusée (modification réservée à l'admin et au manager)
|
|
{ method: "PUT", path: "/api/groups/2", body: { smtpHost: "127.0.0.1", smtpPort: 1, smtpSenderEmail: "metz@banc.test" } },
|
|
{ method: "POST", path: "/api/groups/2/test-smtp" },
|
|
],
|
|
tables: [],
|
|
},
|
|
{
|
|
module: "groups",
|
|
name: "magasins invalides, introuvables ou encore utilisés",
|
|
steps: [
|
|
{ method: "POST", path: "/api/groups", body: {} },
|
|
{ method: "POST", path: "/api/groups", body: { name: "X", smtpSenderEmail: 5 } },
|
|
{ method: "POST", path: "/api/groups", body: { name: "X", smtpSenderEmail: "abc", smtpPort: "0" } },
|
|
{ method: "POST", path: "/api/groups", body: { name: "X", smtpPort: "abc", phone: 12 } },
|
|
{ method: "PUT", path: "/api/groups/1", body: { name: 5 } },
|
|
{ method: "PUT", path: "/api/groups/999", body: { name: "Fantôme" } },
|
|
{ method: "PUT", path: "/api/groups/abc", body: { name: "X" } },
|
|
// Un manager modifie un magasin qui n'est pas le sien (comme Node)
|
|
{ method: "PUT", path: "/api/groups/2", body: { color: "#000000", smtpPassword: null } },
|
|
{ method: "DELETE", path: "/api/groups/1" },
|
|
{ method: "DELETE", path: "/api/groups/999" },
|
|
{ method: "DELETE", path: "/api/groups/abc" },
|
|
],
|
|
tables: ["groups"],
|
|
ignore: ["smtp_password"],
|
|
},
|
|
|
|
// ---------- Sauvegardes ----------
|
|
{
|
|
module: "backups",
|
|
name: "créer une sauvegarde (pg_dump), ménage des anciennes",
|
|
steps: [
|
|
{ method: "POST", path: "/api/backups" },
|
|
{ method: "GET", path: "/api/backups" },
|
|
{ method: "DELETE", path: "/api/backups/{{0.filename}}" },
|
|
{ method: "GET", path: "/api/backups" },
|
|
],
|
|
// Les deux plus anciennes (sans fichier) ont été supprimées par le ménage
|
|
tables: ["database_backups"],
|
|
// Identifiant aléatoire, nom horodaté, taille du fichier pg_dump
|
|
ignore: ["id", "filename", "size"],
|
|
},
|
|
{
|
|
module: "backups",
|
|
name: "supprimer une sauvegarde et son fichier",
|
|
steps: [
|
|
{ method: "DELETE", path: "/api/backups/backup_banc_500.sql" },
|
|
{ method: "GET", path: "/api/backups/backup_banc_500.sql/download" },
|
|
{ method: "DELETE", path: "/api/backups/backup_banc_500.sql" },
|
|
{ method: "DELETE", path: "/api/backups/absente.sql" },
|
|
{ method: "GET", path: "/api/backups" },
|
|
],
|
|
tables: ["database_backups"],
|
|
},
|
|
|
|
// ---------- Paramètres généraux ----------
|
|
{
|
|
module: "utilities",
|
|
name: "modifier les paramètres généraux",
|
|
steps: [
|
|
{ method: "POST", path: "/api/utilities", body: { salesAnalysisUrl: "https://analyse.banc.test/nouveau" } },
|
|
{ method: "POST", path: "/api/utilities", body: { automaticBackupsEnabled: true, autre: 1 } },
|
|
{ method: "POST", path: "/api/utilities", body: { salesAnalysisUrl: null } },
|
|
{ method: "GET", path: "/api/utilities" },
|
|
],
|
|
tables: ["utilities"],
|
|
},
|
|
{
|
|
module: "utilities",
|
|
name: "paramètres généraux invalides",
|
|
steps: [
|
|
{ method: "POST", path: "/api/utilities", body: { salesAnalysisUrl: 5 } },
|
|
{ method: "POST", path: "/api/utilities", body: { salesAnalysisUrl: "x".repeat(501), automaticBackupsEnabled: "oui" } },
|
|
{ method: "POST", path: "/api/utilities", body: [] },
|
|
],
|
|
tables: ["utilities"],
|
|
},
|
|
];
|