Aller au contenu
English

Remise du travail

  • Leçon 19
  • 45 min
  • Module 6 : Valider et documenter la conception

Ce que vous saurez faire

Remettez la conception à une autre équipe sous une forme qu’elle peut vérifier, et pas seulement lire. Générez le rapport de tests de sécurité à partir des résultats des tests. Rassemblez la matrice d’accès et la configuration d’identité, puis notez ce qui reste ouvert. Chaque affirmation dans les documents de remise a une commande qui la prouve.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 45 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

  • Le projet après Auditabilité : python -m pytest affiche 452 passed, 1 deselected (452 tests réussis, 1 désélectionné).
  • PostgreSQL et Azurite en cours d’exécution, et les données chargées. Cette leçon ne nécessite ni le fournisseur ni l’API.

À la fin de cette leçon, votre projet correspond à l’instantané end-of-m06, la fin du cours. Testé sur macOS (puce Apple); les commandes Windows sont écrites, mais pas testées.

Vous vendez une maison. Le nouveau propriétaire reçoit les clés, mais elles ne suffisent pas. Un bon vendeur ajoute une liste. Elle indique quelle clé ouvre quelle porte et comment changer le code de l’alarme. Elle contient le dernier contrôle de sécurité de la chaudière, avec sa date. Elle nomme aussi les choses qui restent à faire : « la fenêtre de toit fuit un peu; nous n’avons pas choisi de réparation ». Chaque ligne peut être vérifiée. L’acheteur essaie chaque clé, lit le certificat et regarde la fenêtre.

Une mauvaise remise, c’est un sac de clés et « tout fonctionne ». Le premier hiver révèle ce que « tout » voulait dire.

Le problème : une seule personne connaît la conception

Section intitulée « Le problème : une seule personne connaît la conception »

Amira a construit le contrôle d’accès de l’API de billets de soutien. L’équipe d’exploitation de Larkfield va maintenant l’exploiter. Deux courtes histoires montrent ce qui se passe sans remise du travail. Les deux utilisent des résultats réels de ce cours.

L’équipe transfère la connexion à Keycloak, le vrai fournisseur de Configuration du fournisseur. Chaque connexion échoue : « Ce n’est pas un jeton d’accès ». Les jetons d’accès de Keycloak indiquent typ: JWT, et l’API attend at+jwt, sauf si ACCESS_TOKEN_TYPES=JWT. Amira le sait. L’équipe passe une journée à le découvrir.

Plus tard, un développeur voit que l’API lit la table des adhésions à chaque requête. « Mettons-la en cache pendant 30 secondes. » Dans Changements de cycle de vie, ce réglage permettait à un membre retiré de continuer à lire pendant 30,3 s. Personne n’a écrit pourquoi le cache est désactivé. La personne suivante l’active donc.

Le même problème apparaît dans les preuves. « Nous l’avons testé » est une affirmation. Quels tests, sur quel code, avec quel résultat? Kwame veut obtenir chaque réponse par une commande, et non par un souvenir.

L’idée : des affirmations avec des preuves, et des questions ouvertes

Section intitulée « L’idée : des affirmations avec des preuves, et des questions ouvertes »

Un dossier de remise est un petit ensemble de documents qui répond à quatre questions. Chaque réponse a une commande qui la prouve :

Quatre questions pointent vers quatre documents. Qui peut faire quoi, dans quelle organisation : docs/access-matrix.md, rôles, règles 404 et 403, accès du soutien, changements au fil du temps. Comment la connexion est configurée : docs/identity-config.md, les deux côtés de l'inscription, .env, vrais fournisseurs. La conception tient-elle : docs/test-report.md, 213 vérifications sur 213, produit par matrix_report. Combien de temps l'accès dure, ce qui reste ouvert : docs/handoff.md, durées de validité, commandes de preuve, choix ouverts. Les quatre pointent vers les commandes qui prouvent chaque affirmation : pytest, matrix_report, attempts, audit --check, lifecycle all.
Le dossier de remise : quatre questions, quatre documents et les commandes qui prouvent chaque affirmation.
Description textuelle du diagramme

Le fichier docs/access-matrix.md répond à la première question : qui peut faire quoi, dans quelle organisation. Le fichier docs/identity-config.md répond à la deuxième : comment la connexion est configurée. Le fichier docs/test-report.md répond à la troisième : la conception tient-elle? Un script le rédige à partir des résultats des tests. Le fichier docs/handoff.md répond à la quatrième : combien de temps l’accès dure et ce qui reste ouvert. Chaque document se termine par des commandes : pytest, matrix_report, attempts, audit --check et lifecycle all.

Vous avez déjà rédigé deux des quatre documents : la matrice d’accès dans Utilisateurs et organisations, et la configuration d’identité dans Configuration du fournisseur. Cette leçon ajoute le rapport de tests et les documents de remise eux-mêmes.

Le rapport de tests : généré, jamais saisi à la main

Section intitulée « Le rapport de tests : généré, jamais saisi à la main »

Un rapport saisi à la main peut dire « tous réussis » alors que ce n’est pas vrai. Un script rédige donc le rapport à partir des résultats de l’exécution des tests. pytest écrit ses résultats au format JUnit XML, un fichier standard que les outils d’intégration continue lisent. scripts/matrix_report.py exécute la matrice avec --junitxml, lit le résultat de chaque cas de test et écrit deux fichiers : docs/test-report.md pour les personnes et reports/access-matrix.json pour les programmes.

  1. Copiez le script du rapport et les documents de remise depuis l’instantané end-of-m06 :

    Fenêtre de terminal
    S=../nextia-learning-labs/C23/snapshots/end-of-m06
    cp "$S/scripts/matrix_report.py" scripts/
    cp "$S/docs/handoff.md" docs/
  2. Exécutez-le :

    Fenêtre de terminal
    python -m scripts.matrix_report
    213 of 213 checks passed. Wrote docs/test-report.md and reports/access-matrix.json.

    Cela prend quelques secondes, car il exécute toute la matrice.

  3. Ouvrez docs/test-report.md. Voici son début :

    docs/test-report.md (start)
    # Security test report: access control
    **213 of 213 checks passed.** Generated by `python -m scripts.matrix_report` from the results of `tests/test_access_matrix.py` (made-up people and data).

    Viennent ensuite une ligne de tableau par personne et organisation, les quatre cas avec l’identifiant d’enregistrement de l’autre boutique, et les identités incorrectes :

    docs/test-report.md (end)
    ## Bad identities on every route
    - no identity: 11 of 11 routes answered 401
    - expired: 11 of 11 routes answered 401
    - wrong audience: 11 of 11 routes answered 401
    - ID token: 11 of 11 routes answered 401
    - no scope: 11 of 11 routes answered 403

    Le tableau a les mêmes lignes et colonnes que la carte thermique dans Tests négatifs.

Le script se termine avec le code 0 seulement si toutes les vérifications ont réussi. Une tâche d’intégration continue peut l’exécuter et bloquer une mise en production si le code est 1. Avec le bogue de la liste des membres de Tests négatifs, il affichait 211 of 213 checks passed. (211 vérifications sur 213 réussies). Il se terminait avec le code 1, et le tableau marquait les cellules : | Omar | larkfield | read_only | … | 403 **FAIL** | … | (échec).

Pendant la rédaction de cette leçon, l’auteur a lancé le rapport avec PostgreSQL arrêté, par accident. La première version du script affichait ceci :

213 of 213 checks passed. Wrote docs/test-report.md and reports/access-matrix.json.

Les 213 tests avaient tous été ignorés, et le script comptait « sans échec » comme « réussi ». C’est le pire type de rapport : vert, mais faux. Le script de l’instantané note maintenant une vérification ignorée comme SKIPPED (ignorée), jamais comme réussie. Quand la base de données est arrêtée, il affiche :

0 of 213 checks passed. Wrote docs/test-report.md and reports/access-matrix.json.
213 checks did not run (skipped). Is PostgreSQL running?

et se termine avec le code 1. Essayez-le : arrêtez la base de données (docker compose stop db), lancez le rapport, puis redémarrez la base de données (docker compose start db). La leçon pour tout rapport : comptez seulement ce qui a réussi, et indiquez ce qui n’a pas été exécuté.

La configuration d’identité : ce qui a été testé

Section intitulée « La configuration d’identité : ce qui a été testé »

docs/identity-config.md indique ce que chaque côté doit connaître : l’inscription chez le fournisseur (identifiant du client, URI de redirection, PKCE, portées, audience) et le fichier .env de l’API. Les documents de remise doivent aussi préciser quelle part est prouvée. Trois fournisseurs, trois niveaux de preuve :

Quatre cases sur une ligne. Fournisseur d'exercice, idp : testé, chaque test, chaque leçon. Keycloak 26.8.0 dans Docker : testé une fois, ACCESS_TOKEN_TYPES=JWT, mappage d'audience, portée basic. Microsoft Entra ID : décrit, non testé, audience api://, scp, oid ou sub. Choix ouverts propres au fournisseur : identifiant stable de la personne, noms des portées, prise en charge de la déconnexion, connexion multifacteur.
Trois fournisseurs, trois niveaux de preuve. Écrivez le niveau à côté de chacun : testé, testé une fois ou décrit.
Description textuelle du diagramme

Chaque test et chaque leçon testent le fournisseur d’exercice dans idp/. Keycloak 26.8.0 dans Docker a été testé une fois : il nécessitait ACCESS_TOKEN_TYPES=JWT, un mappage d’audience et sa portée basic. Microsoft Entra ID est décrit à partir de sa documentation et n’est pas testé. Son audience est une adresse api://, ses portées sont dans scp, et l’identifiant de la personne est oid ou sub. Certains choix restent ouverts pour tout vrai fournisseur.

Fournisseur Preuve Changements dans les paramètres de l’API
Fournisseur d’exercice (idp/) chaque test; chaque leçon aucun : les valeurs par défaut
Keycloak 26.8.0 une exécution : connexion 302 vers /app/, Larkfield 200, Bramble 404, check_identity 0 problème ACCESS_TOKEN_TYPES=JWT; un mappage d’audience et la portée basic chez Keycloak; une ligne d’adhésion pour le sub propre à Keycloak, un UUID
Microsoft Entra ID décrit, non testé ACCESS_TOKEN_TYPES=JWT; émetteur https://login.microsoftonline.com/<directory ID>/v2.0 avec des jetons de version 2; audience comme api://ticket-api

N’écrivez jamais « fonctionne avec Entra ID » si vous avez seulement lu sa documentation. Écrivez « décrit, non testé », comme le fait le cours. L’équipe suivante sait alors où effectuer la première vraie exécution.

Certaines questions restent sans réponse, car elles dépendent d’une décision qui ne revient pas à Amira. Les documents de remise les énumèrent pour que personne ne prenne le silence pour une décision. Voici celles de docs/identity-config.md :

  • Quelle revendication (claim) est l’identifiant stable de la personne. Cette API utilise sub. Le sub d’Entra ID diffère selon l’application; son oid est le même dans toutes les applications. Si l’identifiant change, il faut transférer chaque ligne d’adhésion.
  • Noms des portées. Certains fournisseurs n’autorisent pas : dans une portée. Certains nécessitent un mappage d’audience pour que le jeton contienne aud: ticket-api.
  • Déconnexion. Le fournisseur prend-il en charge la déconnexion lancée par le RP et la déconnexion par canal arrière? Sans déconnexion par canal arrière, une session dans l’API se termine à son prochain renouvellement, dans les 10 minutes.
  • Connexion multifacteur et récupération de compte. C’est le travail du fournisseur, pas de l’API : l’API ne voit que le résultat.

docs/handoff.md ajoute les choix de la conception elle-même. Le cache des adhésions est désactivé, et les documents de remise expliquent pourquoi. La piste d’audit nécessite une règle de conservation. Il faut aussi une règle précisant qui peut la lire à l’extérieur d’une organisation. Quelqu’un doit également décider du traitement des coûts d’exécution de l’IA d’une organisation supprimée.

Ouvrez les documents de remise et lisez leurs cinq sections : le modèle, la configuration d’identité, les sessions et les jetons au fil du temps, les tests et les preuves, et les choix ouverts. Le tableau des durées de validité résume Changements de cycle de vie :

docs/handoff.md (part)
| Access token | 10 minutes (+30 s leeway) | nothing: the API cannot recall a token. Membership checks make it useless for an organization at once. |
| Refresh token | 8 hours, one use each (rotation) | logout (revoked at the provider), a disabled account, a reused old token (the whole family ends) |

La vérification finale du plan de cours : réussir toutes les vérifications entre locataires et d’élévation de privilèges. Exécutez les commandes de preuve des documents de remise, dans un état neuf :

  1. Rechargez les données : python -m scripts.load --reset.

  2. Exécutez tous les tests :

    Fenêtre de terminal
    python -m pytest -q

    La dernière ligne :

    452 passed, 1 deselected in 43.50s

    Un test nécessite Azurite et est désélectionné par défaut. Exécutez-le avec python -m pytest -m azurite : 1 passed, 452 deselected (1 test réussi, 452 désélectionnés).

  3. Les vérifications entre locataires et d’élévation de privilèges, sous forme de rapport : python -m scripts.matrix_report affiche 213 of 213 checks passed. (213 vérifications sur 213 réussies).

  4. Les tentatives fictives :

    Fenêtre de terminal
    python -m scripts.attempts 2>/dev/null | tail -1
    0 of 10 attempts got what they should not get.
  5. La piste d’audit, après les tentatives : python -m scripts.audit --check affiche Checked 4 audit events: 0 with a token or a secret. (4 événements d’audit vérifiés : 0 avec un jeton ou un secret).

  6. Comparez votre projet à l’instantané. Les fichiers absents de git (.venv, .env, .idp, .tokens, reports) et les caches peuvent différer :

    Fenêtre de terminal
    diff -rq . ../nextia-learning-labs/C23/snapshots/end-of-m06 \
    -x .venv -x .env -x .idp -x .tokens -x reports -x __pycache__ -x .pytest_cache -x .ruff_cache

    diff n’affiche rien : votre projet correspond à la fin du cours. Dans PowerShell, la liste montre aussi les dossiers ignorés; seuls ceux-ci peuvent différer.

Si une vérification échoue, le cours n’est pas terminé, même si chaque page fonctionne. Un parcours normal qui fonctionne avec une cellule entre locataires en échec reste une fuite de données derrière une belle interface.

À vous : une démonstration avec deux locataires et un rapport de tests

Section intitulée « À vous : une démonstration avec deux locataires et un rapport de tests »

Préparez une démonstration de 10 minutes pour l’équipe d’exploitation de Larkfield, comme si vous remettiez l’API aujourd’hui. Utilisez seulement des personnes fictives et votre propre ordinateur.

  1. Deux locataires, en direct. Connectez-vous à http://127.0.0.1:8000/app/ en tant que Camille. Montrez les billets de Camille chez Larkfield, avec un bouton Reply (répondre). Passez à Bramble Books : la liste n’a pas de bouton Reply, car Camille a seulement un accès en lecture là-bas. Envoyez ensuite vous-même la réponse avec le jeton de Camille et curl, comme Omar dans L’autorisation dans l’API, et montrez le 403. Connectez-vous en tant que Tomás : montrez qu’il ne voit rien.
  2. Les preuves. Exécutez python -m scripts.matrix_report et montrez docs/test-report.md. Exécutez python -m scripts.attempts. Montrez les événements refusés avec python -m scripts.audit --result denied.
  3. Le rapport. Rédigez une page, docs/demo-notes.md. Nommez les trois questions et les endroits où l’API répond à chacune. Énumérez les résultats des tests avec leurs commandes, et le niveau de preuve de chaque fournisseur. Terminez par vos deux choix ouverts les plus importants et la personne qui doit trancher chacun.

Une bonne démonstration montre les refus, pas seulement les réussites. Le public devrait repartir en sachant ce qui échoue, comment il le sait et ce qui reste ouvert.

Erreur courante

Le rapport indique 0 sur 213

Symptôme : python -m scripts.matrix_report affiche 0 of 213 checks passed. (0 vérification réussie sur 213) et 213 checks did not run (skipped). Is PostgreSQL running? (213 vérifications ignorées. PostgreSQL fonctionne-t-il?).

Cause : la base de données de test est inaccessible. Chaque test a donc été ignoré.

Solution : démarrez la base de données (docker compose up -d db azurite) et relancez le rapport. Ne faites jamais de commit (enregistrement dans le dépôt) d’un rapport contenant des vérifications ignorées.

Erreur courante

ModuleNotFoundError : No module named 'tests' (aucun module nommé 'tests')

Symptôme : python -m scripts.matrix_report s’arrête immédiatement avec cette erreur.

Cause : la commande s’exécute en dehors du dossier du projet. Le script importe les routes et les personnes depuis tests/test_access_matrix.py.

Solution : exécutez la commande depuis le dossier du projet, avec l’environnement virtuel activé.

Vérifiez votre compréhension

Pourquoi générer le rapport de test à partir des résultats JUnit de pytest, plutôt que manuellement ou avec les valeurs attendues?

Afficher la réponse

Un rapport saisi manuellement ou construit avec les valeurs attendues peut indiquer « réussi » pour une vérification échouée ou jamais exécutée. Le fichier JUnit consigne ce que chaque cas de test a fait. Le rapport doit aussi compter les vérifications ignorées comme non réussies. La première version les comptait comme réussies et affichait 213 sur 213 alors que la base de données était arrêtée.

Vérifiez votre compréhension

La remise du travail indique que Keycloak a été « testé une fois » et Entra ID « décrit, non testé ». Donnez une raison pour laquelle cette différence compte pour la prochaine équipe.

Afficher la réponse

Une exécution réelle révèle ce que la documentation cache. L’exécution avec Keycloak a révélé deux bogues que le fournisseur d’exercice avait cachés : un vérificateur PKCE trop court et un courriel unique. Entra ID peut réserver ses propres surprises. La prochaine équipe doit prévoir sa première exécution réelle et ne pas considérer la description comme une preuve.

Vérifiez votre compréhension

Vérification du module : pour chaque requête, donnez le statut et indiquez si elle produit un événement d'audit, avec ou sans organisation. (a) Omar ajoute un message chez Larkfield; (b) Tomás lit T-30002 de Larkfield; (c) Sam lit T-40001 de Bramble par le chemin de Larkfield; (d) un jeton expiré liste les billets de Larkfield.

Afficher la réponse

(a) 403, un événement avec l’organisation larkfield, motif role_cannot. (b) 404, un événement sans organisation, motif not_a_member, avec le chemin dans ses détails. (c) 404 « Ticket T-40001 does not exist. » (le billet T-40001 n’existe pas), aucun événement : Sam réussit les vérifications chez Larkfield et la recherche ne trouve rien; seul le journal le consigne. (d) 401, un événement token.check avec le motif expired, sans acteur, sans organisation et sans jeton. La matrice de tests vérifie les quatre statuts; test_auditability.py vérifie (a), (b) et (d).

Vérifiez votre compréhension

Vérification pratique (achèvement) : quelles quatre commandes prouvent que votre projet réussit toutes les vérifications d'accès entre locataires et d'élévation des privilèges? Que doit afficher chacune?

Afficher la réponse

python -m pytest : 452 passed, 1 deselected (452 tests réussis, 1 désélectionné). python -m scripts.matrix_report : 213 of 213 checks passed. (213 vérifications réussies sur 213). python -m scripts.attempts : 0 of 10 attempts got what they should not get. (0 tentative sur 10 a obtenu ce qu’elle ne devait pas obtenir). python -m scripts.audit --check : 0 événement contenant un jeton ou un secret. Votre projet correspond alors à l’instantané end-of-m06.

Récapitulatif

  • Un dossier de remise du travail répond à quatre questions avec des documents et des commandes. Il contient la matrice d’accès, la configuration d’identité, le rapport de test et le document de remise, avec les durées de validité et les choix non résolus.
  • Le rapport de test est généré à partir des résultats des tests : 213 of 213 checks passed. Une vérification ignorée ne compte jamais comme réussie.
  • Indiquez le niveau de preuve de chaque fournisseur : le fournisseur d’exercice et Keycloak sont testés; Entra ID est décrit, non testé.
  • Notez les choix non résolus et qui doit les trancher. Exemples : l’identifiant stable de la personne, les noms des portées, la déconnexion, la connexion multifacteur, le cache des adhésions et la conservation des données d’audit.

Récapitulatif du module. Vous avez testé ce qui doit échouer pour chaque personne, organisation et route. Vous avez consigné chaque refus sans secret et remis la conception avec ses preuves. Votre projet correspond à end-of-m06, la fin du cours.

Récapitulatif du cours. L’API répond maintenant à trois questions pour chaque requête. Qui : un jeton ou une session vérifiés. Dans quelle organisation : le chemin et l’adhésion propre à l’appelant. Quoi : le rôle de l’adhésion et les portées du jeton. Chaque requête, lien de fichier, recherche et clé de cache contient l’organisation, et la base de données la vérifie à nouveau.

Leçon suivante : Auditer le contrôle d'accès d'une application multilocataire libre