Aller au contenu
English

Trois questions distinctes

  • Leçon 1
  • 45 min
  • Module 1 : Bases de l'identité et des autorisations

Ce que vous saurez faire

Posez trois questions distinctes pour chaque requête à l’API de billets de soutien : qui appelle, dans quelle organisation et que peut cette personne y faire? Préparez le projet du cours. Vous verrez que sa seule clé partagée ne répond bien à aucune de ces questions.

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

  • Python 3.12 ou une version ultérieure, et Docker Desktop, comme dans le cours sur les bases de données. Installer et vérifier dans le cours sur Docker présente l’installation de Docker.
  • Environ 2 Go d’espace disque libre. Aucun compte, aucune clé et rien à payer.

Vous créez un nouveau dossier de projet, ~/projects/ticket-api, à partir de l’instantané start. C’est le projet du cours sur les bases de données à sa fin, sans aucun changement.

Imaginez un immeuble de bureaux avec un seul code de porte. Toutes les personnes qui y travaillent connaissent le code. La porte s’ouvre pour quiconque le tape.

Le code fonctionne bien tant qu’une petite entreprise occupe l’immeuble. Puis une deuxième entreprise loue le deuxième étage. Le code présente maintenant trois problèmes. La porte ne peut pas dire qui est entré : le personnel d’entretien, le gestionnaire et les visiteurs tapent les mêmes chiffres. Elle ne peut pas dire quelle entreprise la personne visite. Et elle ne peut pas dire ce que cette personne peut faire à l’intérieur : le code ouvre toutes les pièces, à chaque étage.

Un bon service d’accueil pose plutôt trois questions distinctes. Qui êtes-vous? Montrez votre carte. Quelle entreprise visitez-vous? Votre nom figure-t-il sur sa liste? Que pouvez-vous y faire? Un visiteur peut s’asseoir dans la salle de réunion, mais pas ouvrir la caisse.

L’API de billets a un code de porte. Dans cette leçon, vous voyez exactement à quelles questions il peut répondre, et ses limites.

Vous connaissez le service de soutien de Larkfield grâce aux cours précédents. Grace dirige les agents de soutien, et Sam répond aux billets. Omar, le gestionnaire de produit, lit les billets pour planifier les prochaines fonctionnalités. Amira développe l’API de billets. Tomás est un ingénieur en IA qui aime les raccourcis.

Larkfield offre maintenant son service de soutien à un deuxième commerce, Bramble Books, une librairie indépendante. Sa propriétaire est Ines. Bramble Books conservera ses clients, ses billets et ses fichiers dans la même API et la même base de données. Camille, une personne d’expérience dans l’équipe de soutien de Larkfield, aidera Ines pendant ses premières semaines.

Kwame rejoint l’équipe comme architecte de solutions. Il guide la conception dans ce cours. Avant tout code, il pose ces questions pour chaque requête : « Qui appelle, que peut cette personne faire et dans quelle organisation? »

Le projet est ticket-api, tel qu’il était à la fin du cours sur les bases de données. Si vous avez terminé ce cours, votre dossier est identique à start. Faites quand même une nouvelle copie. Ce cours le modifie étape par étape, et les leçons supposent que vous avez les mêmes fichiers.

  1. Obtenez les fichiers du cours. Si vous avez cloné le dépôt des exercices pour un cours précédent, mettez-le à jour dans ~/projects :

    Fenêtre de terminal
    cd ~/projects/nextia-learning-labs
    git pull
    cd ..

    Si vous ne l’avez pas encore, clonez-le dans ~/projects avec git clone https://github.com/Nextia-Intelligence/nextia-learning-labs.git. Sans Git, téléchargez le fichier ZIP et décompressez-le dans ~/projects.

  2. Si un dossier ticket-api existe déjà, venant du cours sur les bases de données, arrêtez d’abord ses conteneurs. Supprimez aussi leurs données. Docker nomme les conteneurs et les volumes de données d’après le dossier. Votre nouveau dossier porte le même nom. Il réutiliserait donc l’ancienne base de données, avec son ancien mot de passe et ses anciennes données :

    Fenêtre de terminal
    cd ticket-api
    docker compose down --volumes
    cd ..

    Vos anciens fichiers restent, et vous pouvez recharger les données du cours à tout moment. Renommez ensuite l’ancien dossier, par exemple en ticket-api-databases. Copiez l’instantané start, ouvrez ce dossier dans le terminal et créez votre .env :

    Fenêtre de terminal
    cp -R nextia-learning-labs/C23/snapshots/start ticket-api
    cd ticket-api
    cp .env.example .env
  3. Ouvrez le dossier dans VS Code. Dans .env, remplacez change-me-local-only par votre propre mot de passe, aux trois endroits.

  4. Démarrez Docker Desktop. Démarrez ensuite PostgreSQL et Azurite, le stockage objet pour les fichiers :

    Fenêtre de terminal
    docker compose up -d db azurite

    Exécutez docker compose ps jusqu’à ce que la ligne db indique (healthy) (en bon état).

  5. Créez l’environnement virtuel et installez les paquets :

    Fenêtre de terminal
    python3 -m venv .venv
    source .venv/bin/activate
    python -m pip install -r requirements.txt
  6. Chargez les données :

    Fenêtre de terminal
    python -m scripts.load
    applied 001_create_classifications
    applied 002_add_score
    …
    applied 012_erasure
    Database is up to date (12 migrations).
    Loaded the small data in 1.9 s:
    customers 40
    tickets 200
    messages 331
    attachments 43
    documents 36
    ai_runs 294
    ticket_embeddings 200
    43 files uploaded to the object storage.
    3 messages have no ticket: they are in orphaned_messages.

    Les lignes 003 à 011 sont omises ici. Les douze migrations viennent du cours sur les bases de données. Votre durée sera différente.

  7. Exécutez les tests :

    Fenêtre de terminal
    python -m pytest

    La dernière ligne est 108 passed, 1 deselected (108 tests réussis, 1 non sélectionné). Le test non sélectionné exige Azurite et s’exécute seulement avec -m azurite, comme dans le cours sur les bases de données.

Si vous voyez ces deux résultats, votre installation fonctionne. Chaque étape du cours a été testée sur macOS (puce Apple). Les commandes pour Windows et Linux sont fournies, mais n’ont pas été exécutées.

Posez maintenant les trois questions de Kwame à l’API elle-même. Démarrez-la avec une clé d’API, comme dans le cours sur les API. Ouvrez un deuxième terminal dans le dossier du projet, activez l’environnement virtuel et exécutez :

Fenêtre de terminal
API_KEY=local-dev-key fastapi dev

Dans le premier terminal, demandez le billet T-30002 sans la clé. Sous Windows, tapez curl.exe plutôt que curl :

Fenêtre de terminal
curl -i http://127.0.0.1:8000/v1/tickets/T-30002
HTTP/1.1 401 Unauthorized
content-type: application/json
x-request-id: 7bbff66891b8
{"error":{"code":"unauthorized","message":"A valid X-API-Key header is required.","request_id":"7bbff66891b8","fields":[]}}

Certains en-têtes sont omis. Envoyez la même requête avec la clé :

Fenêtre de terminal
curl -H "X-API-Key: local-dev-key" http://127.0.0.1:8000/v1/tickets/T-30002
{"ticket_id":"T-30002","customer_id":"C-0022","customer_name":"Hugo Garcia",
"subject":"Charged twice for order LK-182074","team":"billing","priority":1,"status":"open",
"message_count":1,"messages":[{"message_id":2,"author":"agent","body":"Could you send the receipt …"}], …}

La réponse est abrégée ici. Ajoutez maintenant un message au billet, comme le fait un agent :

Fenêtre de terminal
curl -X POST -H "X-API-Key: local-dev-key" -H "Content-Type: application/json" -d '{"author": "agent", "body": "We refunded one payment."}' http://127.0.0.1:8000/v1/tickets/T-30002/messages
{"message_id":332,"author":"agent","body":"We refunded one payment.","created_at":"2026-10-10T06:09:21.800162Z"}

Dans PowerShell, les guillemets à l’intérieur d’une chaîne fonctionnent différemment. Placez le corps dans un fichier, msg.json, et envoyez-le avec -d "@msg.json".

Regardez le terminal de l’API. L’API a écrit une ligne pour chaque requête :

INFO ticket_api: GET /v1/tickets/T-30002 401 10ms queries=0 id=7bbff66891b8
INFO ticket_api: GET /v1/tickets/T-30002 200 27ms queries=3 id=3b9d4f4fea83
INFO ticket_api: POST /v1/tickets/T-30002/messages 201 11ms queries=3 id=89aa010fe214

Posez maintenant les trois questions pour la dernière requête.

Qui a écrit le message? Le journal indique POST, le chemin, 201, une durée et un identifiant de requête. La ligne de la base de données indique author: agent (auteur : agent). Grace, Sam et Omar envoient tous la même clé. La requête est donc identique, octet pour octet, peu importe qui l’envoie.

Trois personnes envoient chacune la même requête : POST /v1/tickets/T-30002/messages avec X-API-Key : local-dev-key. Ce sont Grace, qui dirige les agents de soutien, Sam, un agent de soutien, et Omar, un gestionnaire de produit qui lit seulement. ticket-api vérifie une seule chose : la clé est-elle correcte? Elle répond 201 Created (créé). Ce qu'elle enregistre : une ligne de journal POST …/messages 201, et un message avec author = agent. Une note indique que l'API ne peut pas distinguer les trois personnes, car les octets sont identiques.
Avec une seule clé partagée, une requête de Grace, de Sam ou d'Omar est la même requête. L'API peut seulement vérifier la clé.
Description textuelle du diagramme

Grace, Sam et Omar envoient chacun POST /v1/tickets/T-30002/messages avec l’en-tête X-API-Key: local-dev-key. L’API compare la clé à celle de sa configuration. Elles correspondent, donc elle enregistre le message et répond 201 Created (créé). Elle enregistre une ligne de journal et un message dont l’auteur est « agent ». Rien dans la requête ou dans les données enregistrées ne nomme la personne.

Dans quelle organisation? La question n’a pas encore de réponse. Il n’y a qu’un commerce, donc l’API n’a aucune notion d’« organisation ». Regardez dans psql : docker compose exec db psql -U tickets -d tickets, puis \d tickets. La table contient ticket_id, customer_id, subject et dix autres colonnes. Aucune colonne n’indique à quel commerce appartient un billet.

Que peut faire la personne qui appelle? Tout ce que la clé permet. Omar a seulement besoin de lire les billets. Avec la clé, il peut aussi écrire des messages, téléverser des fichiers et lire tous les billets. L’API ne peut pas lui accorder moins de droits, car elle ne sait pas que c’est Omar qui appelle.

Tapez \q pour quitter psql. Arrêtez l’API avec Ctrl+C.

Kwame écrit les trois questions au tableau. Chacune porte un nom.

L’authentification répond à « Qui appelle? » La personne prouve son identité, et l’API vérifie cette preuve. Une clé partagée prouve seulement que « la personne qui appelle a la clé ». À partir du module 2, une personne se connecte, et l’API reçoit un identifiant d’utilisateur. C’est un nom stable pour cette personne, comme usr-sam.

Le locataire répond à « Dans quelle organisation? » Un locataire est une organisation cliente dont les données se trouvent dans une application partagée. Le mot vient des immeubles : plusieurs locataires y louent de l’espace, et chacun garde ses propres pièces. Larkfield et Bramble Books sont les deux locataires de l’API de billets. « Multilocataire » signifie qu’une seule application en sert plusieurs.

L’autorisation répond à « Que peut cette personne y faire? » L’API vérifie si cette personne peut effectuer cette action dans cette organisation. L’authentification vient d’abord : vous ne pouvez pas décider ce qu’une personne peut faire avant de connaître son identité.

Un tableau présente trois questions dans l'ordre : 1. Qui appelle? (authentification), 2. Dans quelle organisation? (locataire), 3. Que peut cette personne y faire? (autorisation). L'API de départ, avec une seule clé partagée, répond : quelqu'un qui a la clé, pas une personne précise; aucune réponse, l'API n'a pas d'organisation; tout ce que la clé permet, lire et modifier tous les billets. À la fin du cours, les réponses sont : usr-sam, d'après un jeton que l'API a vérifié; larkfield, nommée dans le chemin, et Sam en est membre; staff (personnel), lire et modifier les billets, téléverser des fichiers.
Les trois questions de chaque requête. La clé de départ répond mal à la première, et pas du tout aux deux autres. Ce cours construit les réponses de la ligne du bas.
Description textuelle du diagramme

Les trois questions, dans l’ordre où l’API les pose. Premièrement, qui appelle : l’authentification. Deuxièmement, dans quelle organisation : le locataire. Troisièmement, que peut cette personne y faire : l’autorisation. L’API actuelle répond « quelqu’un qui a la clé », « aucune réponse » et « tout ». À la fin du cours, la première réponse est « usr-sam, d’après un jeton vérifié ». La deuxième est « larkfield : le chemin la nomme, et Sam en est membre ». La troisième réponse est « staff : lire et modifier les billets, téléverser des fichiers ».

Sam pose la question évidente : « Pourquoi trois? Pourquoi pas une seule vérification : “Sam peut-il faire ça?” »

Kwame montre le nom de Camille sur le plan. Camille fait partie du personnel de Larkfield et peut donc lire les billets de Larkfield et y répondre. Chez Bramble Books, Camille aide seulement Ines et peut donc lire, mais rien modifier. La même personne obtient deux réponses différentes à « Que peut cette personne faire? » La réponse dépend de l’organisation. L’API doit donc demander « dans quelle organisation? » avant « que peut cette personne faire? » C’est pourquoi le schéma numérote les questions dans cet ordre.

Tomás a déjà un raccourci : « Le navigateur sait pour quel commerce la personne travaille. Il peut envoyer l’identifiant du commerce dans un en-tête. » Kwame secoue la tête. Une personne peut modifier tout ce que le navigateur envoie. L’API doit trouver l’organisation dans ses propres données. La requête nomme une organisation, et l’API vérifie que la personne qui appelle en est membre. Vous construisez cette vérification au module 4.

Question L’API de départ Pourquoi cela ne suffit pas pour deux commerces
Qui appelle? « Quelqu’un qui a la clé. » Chaque personne et chaque programme ont la même clé. Un journal ne peut pas montrer qui a fait quoi.
Dans quelle organisation? Aucune réponse. Les billets de Bramble seront à côté de ceux de Larkfield, et rien ne les sépare.
Que peut cette personne faire? Tout ce que la clé permet. Omar peut seulement lire, mais la clé lui permet d’écrire.

La clé n’est pas un mauvais choix. C’est le bon outil pour une autre question. Le cours sur les API l’utilisait pour un programme qui en appelle un autre : une vérification entre services. La question est alors « Est-ce l’un de nos programmes? », et une clé y répond bien. À la fin de ce cours, la clé protège encore /v1/classify et /v1/history, que d’autres programmes appellent. Chaque route utilisée par une personne passe à des utilisateurs connectés.

Les personnes ont besoin de plus. Chacune a besoin de sa propre identité, d’une liste de ses organisations et d’un rôle dans chacune. Cela demande un vrai travail, et ce cours le fait une étape à la fois. Vous ajoutez un fournisseur d’identité, qui connecte les personnes et émet des jetons. Vous ajoutez des vérifications de jetons dans l’API, une table d’adhésions et une vérification sur chaque route. Ensuite, vous écrivez des tests qui essaient chaque route avec chaque personne.

Dans la prochaine leçon, vous construisez les données qui répondent aux deuxième et troisième questions : personnes, organisations, adhésions et rôles. Puis Exemples de menaces montre ce qui se passe mal tant que l’API n’a que la clé. Cette leçon teste cinq tentatives inventées contre votre propre API d’exercice. Les cinq obtiennent ce qu’elles ne devraient pas obtenir.

Erreur courante

docker compose up : le port est déjà attribué

Symptôme : docker compose up -d db azurite affiche ceci, et db ne démarre pas :

Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint ticket-api-db-1 (…): Bind for 127.0.0.1:5432 failed: port is already allocated

Cause : un autre conteneur utilise déjà le port 5432 sur votre ordinateur. C’est généralement la base de données d’un autre dossier de projet, par exemple celle d’un autre cours.

Diagnostic : exécutez docker ps. Cherchez un conteneur avec 127.0.0.1:5432->5432/tcp. La première partie de son nom est le nom de son dossier de projet.

Solution : dans ce dossier, exécutez docker compose down. Ses données restent dans son volume. Exécutez ensuite de nouveau docker compose up -d db azurite dans ticket-api.

Erreur courante

Chaque requête avec la clé reçoit 401

Symptôme : curl -H "X-API-Key: local-dev-key" … reçoit 401 et A valid X-API-Key header is required. (un en-tête X-API-Key valide est requis).

Cause : l’API utilise une autre clé, ou le nom de l’en-tête est incorrect. Si l’API n’utilise aucune clé, une requête sans clé reçoit 200. Le terminal de l’API affiche alors API_KEY is not set: /v1/classify accepts requests without a key (API_KEY n’est pas défini : /v1/classify accepte les requêtes sans clé).

Solution : arrêtez l’API avec Ctrl+C, puis redémarrez-la avec API_KEY=local-dev-key fastapi dev (PowerShell : définissez d’abord $env:API_KEY). Vérifiez le nom de l’en-tête : X-API-Key, suivi d’un deux-points.

Réinitialisation. python -m scripts.load --reset supprime les données du service de soutien, y compris votre nouveau message, puis les recharge. docker compose down --volumes supprime toute la base de données et les fichiers. Ensuite, exécutez docker compose up -d db azurite et python -m scripts.load.

Vérifiez votre compréhension

Nommez les trois questions auxquelles l'API doit répondre pour chaque requête, et le nom technique de chacune. Pourquoi l'API demande-t-elle l'organisation avant de décider ce que l'appelant peut faire?

Afficher la réponse

Qui est l’appelant? C’est l’authentification. Dans quelle organisation? C’est le locataire. Que peut-il y faire? C’est l’autorisation. L’organisation vient avant l’autorisation, car une personne peut avoir des rôles différents dans différentes organisations. Camille fait partie du personnel chez Larkfield et a un accès en lecture seule chez Bramble Books. La question « Camille peut-elle ajouter un message? » reste donc sans réponse tant que l’API ne connaît pas l’organisation.

Vérifiez votre compréhension

Prédisez : Omar lit seulement des billets de soutien dans son travail. Il envoie POST /v1/tickets/T-30002/messages avec X-API-Key : local-dev-key à l'API de départ. Que répond l'API, et que note la base de données sur l'auteur du message?

Afficher la réponse

201 Created (créé). La clé est valide, et l’API ne vérifie que la clé. La ligne du message indique author: agent (auteur : agent). La ligne du journal indique POST /v1/tickets/T-30002/messages 201. Aucune des deux ne nomme Omar. L’API ne peut pas lui refuser l’accès, car elle ne peut pas distinguer sa requête de celle de Sam.

Vérifiez votre compréhension

Vérification pratique : montrez que votre projet fonctionne. Quelles commandes exécutez-vous, et que doivent-elles afficher?

Afficher la réponse

python -m scripts.load --reset se termine par les nombres d’enregistrements (customers 40, tickets 200, messages 331, attachments 43, documents 36, ai_runs 294, ticket_embeddings 200). Elle affiche aussi 43 files uploaded to the object storage. (43 fichiers téléversés vers le stockage objet). python -m pytest se termine par 108 passed, 1 deselected (108 tests réussis, 1 exclu). Pendant que API_KEY=local-dev-key fastapi dev s’exécute, curl http://127.0.0.1:8000/v1/tickets/T-30002 reçoit la réponse 401. La même requête avec -H "X-API-Key: local-dev-key" reçoit le billet.

Études de cas de ce cours

À la fin du cours, vous appliquez ce que vous avez appris à un projet réaliste, des données jusqu’à la recommandation. Terminez-en au moins une pour obtenir le certificat; les autres sont facultatives.

Récapitulatif

  • Chaque requête doit répondre à trois questions : qui est l’appelant (authentification), dans quelle organisation (le locataire), et que peut-il y faire (autorisation).
  • L’API demande l’organisation avant l’autorisation, car le rôle d’une personne dépend de l’organisation.
  • La clé partagée de départ dit seulement « l’appelant possède la clé ». Grace, Sam et Omar envoient les mêmes octets, et l’API enregistre author: agent.
  • Une clé reste un bon choix pour un programme qui en appelle un autre. Les personnes ont besoin de leur propre identité, de leurs adhésions et de leurs rôles.
  • Votre projet ~/projects/ticket-api correspond à l’instantané start (départ) : 108 passed, 1 deselected.

Ensuite, vous ajoutez Bramble Books, les personnes et leurs adhésions à la base de données. Vous notez aussi qui peut faire quoi.

Leçon suivante : Utilisateurs et organisations