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.md7.1CANCEL_ORDER(PRE-1..PRE-3, RG-1..RG-6, POST, ERR)docs/merise/mlt.mdsection 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.md3.14 (user.pin_hash, argon2id) et 3.20 (audit_log)docs/uml/state-commande.mdT5 (paid -> cancelled,stock_movementtypecancellation)
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_logimmuable (RG-T14) : chaque annulation ecrit une ligneaudit_log(action_code = order.cancel,actor_user_id,actor_role_id,entity_type,entity_id,summaryavec le statut anterieur et le montant re-credite) dans la meme transaction que l'UPDATEdu statut. La table n'accepte niUPDATEniDELETEau niveau applicatif (dictionary.md3.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_logenregistre des noms de champs et unsummarynon-personnel (detailsstocke les noms de champs modifies, pas de PII), conformement aRG-T14et a la classification dePROJECT_CONTEXT.mdsection 19. L'attributionstock_movement.user_iddu 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.