corentin_wakdo/docs/uml/security-sequence.md

12 KiB

Diagramme de sequence securite - Annulation de commande avec PIN (CANCEL_ORDER)

Phase UML : P1 - Conception, complement UML (passe security-by-design) Statut : v0.2 - flux sensible PIN-gate + audit_log (controle anti-fraude interne) Date : 2026-06-12 Branche : feat/p1-conception Auteur methodologie : BYAN


1. Objet du document

Ce document decrit le flux temporel securise de l'annulation d'une commande en back-office (CANCEL_ORDER). L'annulation est une action de manipulation d'argent : annuler une commande deja paid peut servir a masquer un detournement d'especes (l'equipier encaisse, annule, garde le cash). Le flux ci-dessous materialise le controle qui adresse ce risque : une re-authentification par PIN par equipier (RG-T13) avant l'execution, et l'ecriture d'une ligne audit_log immuable dans la meme transaction que l'effet (RG-T14), de sorte que chaque annulation est rattachee a une personne meme sur un poste partage.

Le diagramme reste au niveau conceptuel / logique. Il nomme les echanges entre participants sans detailler l'implementation PHP ni le SQL exact. Il complete l'operation CANCEL_ORDER du docs/merise/mlt.md (7.1), la transition T5 de docs/uml/state-commande.md (paid -> cancelled, re-credit du stock) et le cas d'utilisation "Annuler une commande" de docs/uml/use-cases.md (UC13).

Sources :

  • docs/merise/mlt.md 7.1 CANCEL_ORDER (PRE-1..PRE-3, RG-1..RG-6, POST, ERR)
  • docs/merise/mlt.md section 2 : RG-T13 (PIN sensible), RG-T14 (audit_log), RG-T11 (re-credit dans la meme transaction), RG-T07 (garde de concurrence), RG-T08 (transaction atomique)
  • docs/merise/dictionary.md 3.14 (user.pin_hash, argon2id) et 3.20 (audit_log)
  • docs/uml/state-commande.md T5 (paid -> cancelled, stock_movement type cancellation)

2. Participants

Participant Role Couche
Equipier Counter / Drive / Admin titulaire de order.cancel, sur poste partage Acteur
Admin UI Interface back-office (Bloc 3, formulaire d'annulation + saisie PIN) Presentation
Controleur Back-end REST sous /api/* (Bloc 2), orchestre la transaction Application
Verif PIN Service de verification du PIN (password_verify argon2id) Application
BDD Base de donnees MariaDB Persistance

La session est partagee par poste de travail pour le routine 95% ; le PIN re-introduit une attribution individuelle sur le sous-ensemble sensible (mlt.md RG-T13). Le PIN n'est pas une session : il est verifie a chaque action sensible et sert a capturer le user_id qui sera ecrit dans audit_log.


3. Diagramme de sequence

sequenceDiagram
    actor Equipier
    participant AdminUI as Admin UI
    participant Ctrl as Controleur
    participant PIN as Verif PIN
    participant BDD

    Note over Equipier,BDD: Phase 1 - Demande et controle de permission

    Equipier->>AdminUI: demander l'annulation d'une commande
    AdminUI->>Ctrl: POST /api/orders/{id}/cancel
    Ctrl->>Ctrl: verifier session active + is_active = 1 (RG-T02)
    Ctrl->>BDD: lire role_permission (permission order.cancel ?) (RG-T03, PRE-1)
    BDD-->>Ctrl: permission presente
    Ctrl->>BDD: lire customer_order (existe ? statut ?) (PRE-2, PRE-3)
    BDD-->>Ctrl: status courant

    alt status hors ['pending_payment','paid'] (delivered ou cancelled)
        Ctrl-->>AdminUI: 422 CANNOT_CANCEL_IN_STATE {current_status}
        AdminUI-->>Equipier: refus, aucun changement d'etat (ERR-1)
    else status annulable
        Note over Equipier,BDD: Phase 2 - Re-authentification PIN (RG-T13)

        Ctrl-->>AdminUI: demander la saisie du PIN
        Equipier->>AdminUI: saisir le PIN
        AdminUI->>Ctrl: soumettre le PIN (re-auth action sensible)
        Ctrl->>PIN: verifier le PIN soumis
        PIN->>BDD: lire user.pin_hash de l'equipier
        BDD-->>PIN: pin_hash (argon2id)
        PIN->>PIN: password_verify(pin, pin_hash)

        alt PIN incorrect
            PIN-->>Ctrl: echec
            Ctrl-->>AdminUI: refus du PIN, action rejetee
            AdminUI-->>Equipier: PIN incorrect, aucun changement d'etat
        else PIN correct
            PIN-->>Ctrl: succes, acting user_id capture (RG-T13)

            Note over Equipier,BDD: Phase 3 - Transaction atomique (RG-T08, RG-T11)

            Ctrl->>BDD: BEGIN transaction
            Ctrl->>BDD: UPDATE customer_order SET status = 'cancelled', cancelled_at = NOW() WHERE id = :id AND status IN ('pending_payment','paid') (RG-1, RG-T07)
            BDD-->>Ctrl: lignes affectees

            alt 0 ligne affectee (annulation concurrente)
                Ctrl->>BDD: ROLLBACK
                Ctrl-->>AdminUI: 409 INVALID_TRANSITION (ERR-2)
                AdminUI-->>Equipier: deja annulee par un autre poste
            else 1 ligne affectee
                opt statut anterieur = paid (re-credit du stock)
                    Ctrl->>BDD: UPDATE ingredient SET stock_quantity = stock_quantity + :units (par ingredient consomme) (RG-3)
                    Ctrl->>BDD: INSERT stock_movement (type cancellation, delta +units, order_id, user_id de l'equipier) (RG-3)
                end
                Ctrl->>BDD: INSERT audit_log (action_code order.cancel, actor_user_id, actor_role_id, entity_type customer_order, entity_id, summary [statut anterieur + montant re-credite]) (RG-6, RG-T14)
                Ctrl->>BDD: COMMIT
                BDD-->>Ctrl: transaction validee
                Ctrl-->>AdminUI: 200 annulation confirmee (OUT-1)
                AdminUI-->>Equipier: commande annulee, trace enregistree
            end
        end
    end

4. Notes de modelisation : chaque pas et sa regle

Le tableau ci-dessous mappe chaque interaction du diagramme a la regle mlt.md 7.1 ou a la regle transverse correspondante, et a l'entite ecrite.

# Interaction Regle (mlt.md) Entite ecrite / lue
1 Verifier session active + is_active = 1 RG-T02 user (lecture)
2 Verifier order.cancel via role_permission RG-T03, 7.1 PRE-1 role_permission (lecture)
3 Charger la commande et lire son status 7.1 PRE-2, PRE-3 customer_order (lecture)
4 Bloquer si status est delivered ou cancelled 7.1 ERR-1 aucune ecriture (HTTP 422)
5 Demander + verifier le PIN (password_verify argon2id) RG-T13, 7.1 RG-6 user.pin_hash (lecture)
6 Rejeter si PIN incorrect, sans changement d'etat RG-T13 aucune ecriture
7 Capturer l'acting user_id pour l'audit RG-T13 (en memoire, sert aux pas 11-12)
8 BEGIN transaction RG-T08 transaction
9 UPDATE customer_order SET status='cancelled' avec garde AND status IN (...) 7.1 RG-1, RG-T07 customer_order (ecriture)
10 ROLLBACK + 409 si 0 ligne affectee (concurrence) 7.1 ERR-2, RG-T07 aucune ecriture nette
11 Re-credit conditionnel du stock si statut anterieur paid 7.1 RG-3, RG-T11 ingredient, stock_movement (type cancellation)
12 INSERT audit_log dans la meme transaction 7.1 RG-6, RG-T14 audit_log (ecriture)
13 COMMIT (tout ou rien) RG-T08, RG-T11 transaction
14 Reponse 200 de confirmation 7.1 OUT-1 aucune ecriture

4.1 Re-credit conditionnel du stock (RG-T11)

Le re-credit ne s'applique que si la commande etait au statut paid avant l'annulation (7.1 RG-3). Une commande pending_payment n'avait pas encore decremente le stock (le decrement a lieu a la transition paid), donc il n'y a rien a re-crediter. Pour chaque order_item d'une commande paid, les unites consommees sont recalculees (format normal/maxi, ajustees par les order_item_modifier), ingredient.stock_quantity est re-incremente et un stock_movement de type cancellation est insere. RG-T11 garantit que ce re-credit et l'UPDATE du statut sont dans la meme transaction : il n'y a pas de decrement orphelin si une etape echoue.

4.2 Garde de concurrence (RG-T07)

L'UPDATE porte la clause AND status IN ('pending_payment','paid'). Si deux postes tentent d'annuler la meme commande au meme instant, seul le premier obtient une ligne affectee ; le second recoit 0 ligne et le controleur repond 409 INVALID_TRANSITION apres ROLLBACK (7.1 ERR-2). Cette garde optimiste reduit le risque d'une double annulation et d'un double re-credit du stock.

4.3 PIN distinct de la session (RG-T13)

La session reste partagee par poste pour le flux routine. Le PIN est verifie a chaque action du sous-ensemble sensible (annulation, prix/VAT, RBAC, gestion utilisateur, correction d'inventaire), et c'est lui qui fournit l'actor_user_id ecrit dans audit_log. Le pin_hash est un hash argon2id (dictionary.md 3.14), compare via password_verify ; il fait partie des champs RESTRICTED tenus hors des logs et des reponses API.


5. Menace adressee : repudiation et detournement d'especes

L'annulation d'une commande paid est le geste qui permet le schema de fraude "encaisser puis annuler pour garder le cash" (insider cash-skim). Sans controle, sur un poste a session partagee, une annulation ne serait rattachee a personne : l'auteur pourrait nier l'avoir faite (repudiation). Le flux ci-dessus reduit le risque de ce schema en combinant deux mecanismes concrets :

  • PIN par equipier (RG-T13) : l'annulation exige une re-authentification individuelle. Sur un poste partage, cela rattache l'acte a une personne et non au seul poste. Le PIN tend a dissuader l'usage opportuniste d'une session laissee ouverte par un collegue.
  • audit_log immuable (RG-T14) : chaque annulation ecrit une ligne audit_log (action_code = order.cancel, actor_user_id, actor_role_id, entity_type, entity_id, summary avec le statut anterieur et le montant re-credite) dans la meme transaction que l'UPDATE du statut. La table n'accepte ni UPDATE ni DELETE au niveau applicatif (dictionary.md 3.20). Une annulation ne peut donc pas exister sans sa trace, et la trace ne peut pas etre effacee par l'auteur.

L'effet combine : un pic d'annulations rattachees a un meme actor_user_id devient visible et opposable lors d'une revue. Ceci ne supprime pas le risque, mais le reduit en transformant un acte anonyme et niable en un acte attribue et trace. La residualite (collusion, partage de PIN) releve de controles organisationnels hors du modele de donnees.

Note : audit_log enregistre des noms de champs et un summary non-personnel (details stocke les noms de champs modifies, pas de PII), conformement a RG-T14 et a la classification de PROJECT_CONTEXT.md section 19. L'attribution stock_movement.user_id du re-credit complete la trace cote stock sans double journalisation.


6. Coherence avec les autres livrables

Verification Resultat
Statuts annulables coherents avec state-commande.md Oui : pending_payment et paid (T3, T5) ; delivered non annulable (7.1 ERR-1)
Transition paid -> cancelled avec re-credit Couverte par T5 et 7.1 RG-3 (stock_movement type cancellation)
Entites ecrites presentes au dictionnaire customer_order (3.10), ingredient, stock_movement, audit_log (3.20)
Regle PIN appliquee RG-T13 (sous-ensemble sensible inclut 7.1) ; user.pin_hash (3.14)
Regle audit appliquee RG-T14 ; colonnes conformes a audit_log (3.20)
Atomicite re-credit + statut + audit RG-T08 + RG-T11 (une transaction, COMMIT / ROLLBACK)
Codes d'erreur 422 CANNOT_CANCEL_IN_STATE (ERR-1), 409 INVALID_TRANSITION (ERR-2)

7. Arbitrage tranche

Le flux retient la re-authentification par PIN plutot qu'une re-saisie du mot de passe complet : le PIN couvre le sous-ensemble sensible sans casser le routine 95% a session partagee (RG-T13), tout en fournissant l'attribution individuelle. L'audit_log est ecrit dans la meme transaction que l'effet (RG-T14 + RG-T08) : une annulation sans trace ne peut pas etre committee. Le re-credit du stock est conditionnel au statut anterieur paid (RG-T11), ce qui ecarte un re-credit indu sur une commande pending_payment qui n'a pas ete decrementee. Les codes d'erreur (CANNOT_CANCEL_IN_STATE, INVALID_TRANSITION) reprennent ceux de mlt.md 7.1, sans en inventer de nouveaux.