Files
mimic/tasks/todo.md
Knacky bd9c06e31b chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:

- SPEC.md enriched with a "Décisions techniques" section that pins
  down 3-role auth (admin super-user / redteam / soc), JWT bearer,
  single-container Flask+React topology, minimal Engagement model,
  local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
  backend-builder, frontend-builder, spec-reviewer (project override
  covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
  devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
  validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
  and Claude Code worktrees.

No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00

17 KiB
Raw Blame History

Sprint 1 — Auth + CRUD Engagement

Branche : sprint/1-auth-engagements Statut : 🟢 PLAN APPROUVÉ (spec-reviewer 2026-05-26) — prêt pour dispatch backend-builder Base : main Objectif : poser l'infrastructure (Flask + SQLite + React + Docker + Makefile + tests) ET livrer une première feature de bout en bout testable sur l'UI — login + admin gère les comptes + tout utilisateur authentifié peut créer/lister/éditer/supprimer des engagements.


1. User stories

US-1 — En tant qu'admin, je bootstrap le premier compte admin

Pourquoi : sinon impossible d'utiliser l'application au premier démarrage.

Critères d'acceptation

  • AC-1.1 : la commande make create-admin USER=alice PASS=p4ssw0rd crée un user alice avec le rôle admin et le password hashé (argon2).
  • AC-1.2 : la commande échoue proprement (exit ≠ 0, message clair) si le username existe déjà.
  • AC-1.3 : la commande échoue si le password fait moins de 8 caractères.
  • AC-1.4 : la commande s'exécute via docker exec mimic flask create-admin … (le Makefile encapsule cet appel).

US-2 — En tant qu'utilisateur, je me connecte et me déconnecte

Pourquoi : porte d'entrée de l'application.

Critères d'acceptation

  • AC-2.1 : POST /api/auth/login {username, password} retourne {access_token, user: {id, username, role}} (200) si credentials valides.
  • AC-2.2 : 401 si credentials invalides, avec un message générique ("Invalid credentials") — pas de fuite username vs password.
  • AC-2.3 : POST /api/auth/logout invalide le token côté client (UI supprime le token). Côté serveur : optionnel V1, on accepte un logout client-side.
  • AC-2.4 : page /login affiche le formulaire ; soumission OK → redirection /engagements. Soumission KO → message d'erreur visible.
  • AC-2.5 : navigation vers /engagements sans token → redirection /login.
  • AC-2.6 : si une requête API retourne 401 (token expiré ou invalide), l'intercepteur axios purge le token et redirige vers /login avec un toast "Session expirée".

US-3 — En tant qu'admin, je gère les comptes utilisateurs

Pourquoi : créer redteam/soc accounts depuis l'UI.

Critères d'acceptation

  • AC-3.1 : GET /api/users (admin only) → liste [{id, username, role, created_at}].
  • AC-3.2 : POST /api/users {username, password, role} (admin only) → 201 + objet user (sans password_hash). 400 si username existe ou password < 8 chars.
  • AC-3.3 : PATCH /api/users/<id> {role?, password?} (admin only) → 200, modifie role et/ou password.
  • AC-3.4 : DELETE /api/users/<id> (admin only) → 204. Refuse de supprimer le dernier admin (409).
  • AC-3.5 : tout autre rôle (redteam/soc) appelant ces endpoints reçoit 403.
  • AC-3.6 : page /admin/users (admin only) liste les users avec actions "Créer", "Modifier rôle", "Reset password", "Supprimer".
  • AC-3.7 : un user redteam/soc qui visite /admin/users est redirigé vers /engagements avec un toast "Accès refusé".

US-4 — En tant qu'utilisateur authentifié, je gère les engagements

Pourquoi : la feature métier centrale du Sprint 1.

Critères d'acceptation

  • AC-4.1 : GET /api/engagements (auth) → [{id, name, description, start_date, end_date, status, created_at, created_by}].
  • AC-4.2 : POST /api/engagements {name, description?, start_date, end_date?, status?} (auth) → 201. Valide : name non vide, start_date parseable, end_date >= start_date si fournie, status ∈ {planned, active, closed} (défaut planned).
  • AC-4.3 : GET /api/engagements/<id> (auth) → 200 + objet, 404 si inconnu.
  • AC-4.4 : PATCH /api/engagements/<id> (auth, redteam ou admin) → 200, modifie les champs fournis.
  • AC-4.5 : DELETE /api/engagements/<id> (admin ou redteam) → 204.
  • AC-4.6 : un user soc peut lire (GET) mais pas créer/modifier/supprimer (403).
  • AC-4.7 : page /engagements liste les engagements avec colonnes (name, status badge, dates, created_by). Boutons "Nouveau", "Voir", "Éditer", "Supprimer" selon rôle.
  • AC-4.8 : page /engagements/new et /engagements/<id>/edit (formulaire avec validation côté client + erreurs API affichées).
  • AC-4.9 : page /engagements/<id> (détail), placeholder "Simulations à venir au Sprint 2".

US-5 — En tant qu'utilisateur, l'UI respecte DESIGN.md

Pourquoi : non négociable selon SPEC.md.

Critères d'acceptation

  • AC-5.1 : la palette, typographie, espacements, composants (boutons, inputs, badges) suivent strictement DESIGN.md.
  • AC-5.2 : layout responsive desktop-first (≥ 1024px), pas de breakage visible jusqu'à 1280×720 minimum.
  • AC-5.3 : états loading / error / empty implémentés pour la liste d'engagements et la liste d'users.

US-6 — Le livrable se déploie via Docker + Makefile

Pourquoi : exigence SPEC.md.

Critères d'acceptation

  • AC-6.1 : make build produit l'image docker mimic:latest (Dockerfile multistage : Node build → Python runtime).
  • AC-6.2 : make start lance le container, l'app est accessible sur http://localhost:5000 (front + API).
  • AC-6.3 : make stop, make restart, make logs fonctionnent.
  • AC-6.4 : SQLite persisté via volume nommé mimic-data (la DB survit à make restart).
  • AC-6.5 : make test-backend, make test-frontend, make test-e2e exécutent les suites respectives.

2. Brief technique — Backend Builder

Scope strict : backend/, Dockerfile, Makefile (en collab avec ce sprint uniquement).

Livrables

  1. Structure :

    backend/
      __init__.py              # ⚠️ requis pour que `backend.app:create_app` soit importable depuis /app dans le Dockerfile
      app/
        __init__.py            # create_app() factory
        config.py              # DevConfig / ProdConfig (SECRET_KEY, JWT_SECRET, SQLALCHEMY_DATABASE_URI)
        extensions.py          # db = SQLAlchemy(), migrate = Migrate()
        cli.py                 # @app.cli.command("create-admin")
        models/
          __init__.py
          user.py              # User(id, username, password_hash, role, created_at)
          engagement.py        # Engagement(id, name, description, start_date, end_date, status, created_at, created_by)
        auth/
          __init__.py
          jwt.py               # encode_token / decode_token
          hashing.py           # argon2 hash & verify
          decorators.py        # @login_required, @role_required("admin")
        api/
          __init__.py
          auth.py              # /login, /logout, /me
          users.py             # CRUD users (admin)
          engagements.py       # CRUD engagements
        serializers.py         # to_dict() helpers — engagement renvoie created_by={id, username}, jamais l'objet User brut
        errors.py              # uniform JSON error handler
      migrations/              # alembic init + 0001 initial schema
      tests/
        conftest.py            # app fixture, db fixture, auth helpers
        test_auth.py           # login OK + 401 invalid + 401 token expiré
        test_users.py          # CRUD admin only, 403 redteam/soc, last-admin protection (AC-3.4)
        test_engagements.py    # CRUD redteam/admin, 403 soc en write, serializer created_by
        test_cli_create_admin.py  # success + duplicate username (AC-1.2) + password < 8 chars (AC-1.3)
      pyproject.toml
      requirements.txt         # flask, flask-sqlalchemy, flask-migrate, pyjwt, argon2-cffi, ruff, mypy, pytest
    
  2. Endpoints : voir critères AC-2 / AC-3 / AC-4.

  3. Modèles : voir SPEC.md § Modèle de données.

  4. Config :

    • SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.environ.get('MIMIC_DB_PATH', '/data/mimic.sqlite')}" — l'env var MIMIC_DB_PATH du Dockerfile surcharge le chemin si présent (utile pour tests ou changement de mount).
    • JWT_SECRET lu depuis env var MIMIC_JWT_SECRET, requis (raise si absent en Prod).
    • JWT_EXP_MINUTES = 60.
  5. CLI : flask create-admin <user> <pass> (avec validations AC-1.2/1.3).

  6. Serializer engagement : la réponse JSON expose created_by sous forme {"id": <int>, "username": <str>} — pas l'objet User complet, pas seulement l'id.

  7. Tests : couverture success / failure / edge sur chaque endpoint et CLI. Les AC-1.2 et AC-1.3 doivent avoir leur propre test dans test_cli_create_admin.py.

  8. Lint / typing : ruff clean, mypy clean sur app/.

Règles

  • Pas de touche au frontend.
  • Pas d'invention de dépendances hors de la liste ci-dessus sans escalade au team-lead.
  • Renvoyer le summary attendu (cf. .claude/agents/backend-builder.md).

3. Brief technique — Frontend Builder

Scope strict : frontend/ UNIQUEMENT. Le dossier e2e/ est interdit au frontend-builder — il est sous la responsabilité exclusive du test-verifier (scaffolding Playwright + tests).

Livrables

  1. Structure :

    frontend/
      package.json             # react, react-dom, react-router-dom, @tanstack/react-query, axios, tailwindcss, vite, typescript, vitest, @testing-library/react
      vite.config.ts           # proxy /api -> http://localhost:5000 en dev
      tailwind.config.ts       # tokens issus de DESIGN.md, font-family principale = "Inter"
      tsconfig.json
      index.html
      src/
        main.tsx
        App.tsx                # router, QueryClientProvider
        api/
          client.ts            # axios + interceptor (Bearer + 401 purge → /login)
          auth.ts              # login, me
          users.ts             # CRUD users
          engagements.ts       # CRUD engagements
          types.ts
        hooks/
          useAuth.ts           # token in memory + localStorage, role helpers (isAdmin, isRedteam, isSoc)
          useEngagements.ts    # TanStack Query hooks
          useUsers.ts
          useToast.ts          # provider + hook pour notifications éphémères
        pages/
          LoginPage.tsx
          EngagementsListPage.tsx
          EngagementFormPage.tsx       # new + edit
          EngagementDetailPage.tsx
          UsersAdminPage.tsx
        components/
          Layout.tsx           # nav, topbar, role-aware menu
          ProtectedRoute.tsx   # redirect to /login if no token, role gate
          StatusBadge.tsx
          FormField.tsx
          EmptyState.tsx
          ErrorState.tsx
          LoadingState.tsx
          Toast.tsx            # composant + ToastProvider (utilisé par AC-2.6 + AC-3.7)
        styles/
          index.css            # tailwind base + DESIGN.md tokens
          fonts.css            # @font-face Inter (bundlée localement dans public/fonts/, AUCUN CDN)
      public/
        fonts/                 # fichiers Inter .woff2 (déposés via npm install + copie post-install, ou commit direct)
      tests/
        components/*.test.tsx  # Vitest
    
  2. Police : Inter (substitut Forma DJR Micro choisi par défaut parmi les 3 options DESIGN.md §86-89). Bundlée localement en .woff2, jamais via Google Fonts/CDN. Configurée dans tailwind.config.ts comme font-sans et chargée via @font-face dans styles/fonts.css.

  3. Routing :

    • /login
    • /engagements (auth, all roles)
    • /engagements/new (auth, redteam|admin)
    • /engagements/:id (auth, all roles)
    • /engagements/:id/edit (auth, redteam|admin)
    • /admin/users (auth, admin only)
    • / → redirige vers /engagements ou /login
  4. Auth : token JWT en mémoire + localStorage ; intercepteur axios ajoute Authorization: Bearer <token> ; 401 → purge token + redirect /login + toast "Session expirée" (AC-2.6).

  5. Tests Vitest : 1 test par composant non trivial (états loading/error/empty, comportement des rôles dans ProtectedRoute, Toast déclenché par 401).

Règles

  • Lit le summary du backend-builder EN PREMIER.
  • Pas d'invention d'endpoints. Mismatch → escalade au team-lead.
  • Respect strict de DESIGN.md pour palette/typo/composants.
  • Pas de CDN remote au runtime — bundle local (police Inter incluse).
  • Interdiction absolue de toucher e2e/ (responsabilité test-verifier).

4. Brief — Docker / Makefile (réalisé par le backend-builder)

docker/Dockerfile (multistage)

# Stage 1: build front
FROM node:20-alpine AS frontend-build
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build

# Stage 2: python runtime
FROM python:3.12-slim
WORKDIR /app
COPY backend/requirements.txt ./backend/
RUN pip install --no-cache-dir -r backend/requirements.txt
COPY backend/ ./backend/
COPY --from=frontend-build /app/frontend/dist ./backend/app/static

ENV FLASK_APP=backend.app:create_app
ENV PYTHONUNBUFFERED=1
ENV PYTHONPATH=/app
# Variables surchargeables au `docker run` :
ENV MIMIC_PORT=5000
ENV MIMIC_DB_PATH=/data/mimic.sqlite

VOLUME ["/data"]
EXPOSE 5000

# Entrypoint : applique les migrations Alembic puis lance Flask
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

docker/entrypoint.sh :

#!/bin/sh
set -e
flask db upgrade
exec flask run --host=0.0.0.0 --port="${MIMIC_PORT:-5000}"

Flask sert backend/app/static en racine / ET expose les blueprints sous /api/*. La DB SQLite vit dans /data/mimic.sqlite (volume nommé mimic-data) — survit à make restart (AC-6.4).

Makefile

Targets requis et leur sémantique :

Target Action
build docker build -f docker/Dockerfile -t mimic:latest .
start docker run -d --name mimic -p $(PORT):5000 -v mimic-data:/data --env-file .env mimic:latest (PORT ?= 5000)
stop docker stop mimic && docker rm mimic
restart $(MAKE) stop && $(MAKE) start
update git pull && $(MAKE) build && $(MAKE) restart
logs docker logs -f mimic
create-admin docker exec mimic flask create-admin $(USER) $(PASS) (requiert USER= et PASS=)
update-mitre placeholder no-op Sprint 1 (@echo "MITRE update: Sprint 2+")
test-backend docker exec mimic pytest -q backend/tests/ (ou run local en venv)
test-frontend cd frontend && npm run test -- --run
test-e2e cd e2e && npx playwright test (container doit être up)
clean docker rm -f mimic 2>/dev/null; docker volume rm mimic-data 2>/dev/null; rm -rf backend/__pycache__ frontend/node_modules frontend/dist

5. Definition of Done — Sprint 1

  • Tous les critères d'acceptation des US 1→6 passent.
  • pytest, ruff, mypy clean côté backend.
  • npm run typecheck, lint, test clean côté frontend.
  • Playwright suite verte côté e2e/.
  • Image docker se build (make build), démarre (make start), répond sur :5000.
  • Code review (Opus) sans BLOCKER ouvert.
  • SPEC.md, README.md, CHANGELOG.md à jour.
  • PR ouverte sur la branche sprint, validée par l'utilisateur après récap synthétique.

6. Risques & questions à clarifier avec l'utilisateur AVANT de coder

  1. DESIGN.md : faut-il que je relise DESIGN.md (27 Ko, déjà présent dans le repo) avant que le frontend-builder l'utilise ? OUI
  2. Persistance SQLite : DB stockée dans /data/mimic.sqlite montée comme volume nommé mimic-data. OK ? OK
  3. Port : 5000 (Flask défaut). Conflit possible avec macOS AirPlay si jamais — on s'en fiche en Linux. Surcharge du port possible via dockerfile et container.
  4. CSRF : on est en API JWT pure, pas de cookie session, donc pas de CSRF protection nécessaire côté serveur. OK ? OK
  5. Refresh token : exclu V1. JWT court (60 min) ; user devra se reconnecter. OK ? OK
  6. Logout : V1 = client-side uniquement (purge du token). Pas de blacklist. OK ? OK
  7. CI : pas mentionné dans la spec. Skip pour Sprint 1 ? (à confirmer) On verra plus tard.
  8. README.md : actuellement absent. Le team-lead le crée en fin de Sprint 1 avec instructions make build / start / create-admin. OK.

7. Plan d'exécution (séquence)

Branche unique pour ce sprint : sprint/1-auth-engagements (pas de sous-branches builders, le sprint est séquentiel).

  1. Spec-reviewer (.claude/agents/spec-reviewer.md, override projet du built-in) valide ce plan vs SPEC.md.
  2. 🔵 Backend-builder implémente backend/ + docker/Dockerfile + docker/entrypoint.sh + Makefile, livre son summary (incluant le contrat API).
  3. 🔵 Frontend-builder lit le summary backend puis implémente le front (frontend/ UNIQUEMENT, jamais e2e/).
  4. 🔵 Code-reviewer relit le diff du sprint (LSP-first, focus bugs/qualité/scope).
  5. 🔵 Test-verifier scaffolds e2e/ (Playwright config, package.json, fixtures auth) puis écrit les acceptance tests Playwright et exécute la suite contre le container démarré via make start.
  6. 🟢 Team-lead (moi) prépare la PR + récap synthétique → user valide.

Responsabilité e2e/ : exclusivement le test-verifier. Backend-builder et frontend-builder n'y touchent jamais.