Aller au contenu
English

Accès aux fichiers et à la récupération

  • Leçon 13
  • 55 min
  • Module 4 : Contrôler l'accès aux ressources

Ce que vous saurez faire

Gardez l’organisation liée aux données quand elles quittent les tables. Cela comprend les liens de fichiers, la recherche de billets similaires, les citations de documents et les réponses en cache. Prouvez ensuite qu’un identifiant deviné ne donne accès à rien.

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

Ce que vous devez savoir

Votre projet

  • ~/projects/ticket-api à l’instantané end-of-m04. Le fournisseur, l’API et Azurite fonctionnent. Obtenez les jetons comme dans la première leçon de ce module : I pour Ines, S pour Sam, O pour Omar, C pour Camille.
Fenêtre de terminal
I=$(python -m scripts.login --user usr-ines --print-access-token)

Sur Windows : $I = python -m scripts.login --user usr-ines --print-access-token, et tapez curl.exe. Créez les trois autres jetons de la même façon.

Trois situations courantes, une pour chaque partie de cette leçon.

Un billet de concert vous laisse entrer, peu importe qui vous êtes. Le personnel à l’entrée vérifie son authenticité et sa validité pour ce soir. Il ne vérifie pas votre nom. La billetterie doit donc décider qui reçoit un billet, car ensuite, c’est le billet qui décide.

Deux écoles partagent un catalogue de bibliothèque. Un élève de la première école cherche « chimie ». Si le catalogue ne filtre pas par école, le meilleur résultat peut être sur une tablette de l’autre école. L’élève ne peut pas emprunter ce livre.

Une salle du courrier contient des casiers portant seulement l’étiquette « réponse à la question 7 ». La première personne qui pose la question 7 reçoit une réponse. Une copie reste dans le casier. La personne suivante qui pose la question 7 reçoit cette copie, même si la bonne réponse pour elle est différente.

Chaque situation représente un endroit où les données quittent les tables protégées. L’API crée un lien de fichier, lance une recherche, cite un document et garde une réponse en cache. Chacun de ces éléments a aussi besoin de l’organisation.

Liens de fichiers : vérifier d’abord, signer ensuite

Section intitulée « Liens de fichiers : vérifier d’abord, signer ensuite »

Le cours sur les bases de données conserve les fichiers dans le stockage objet. L’API distribue des URL signées : des liens avec une signature et une heure d’expiration. Le stockage vérifie seulement la signature et l’heure. Comme le billet de concert, le lien fonctionne pour toute personne qui le possède.

Que se passe-t-il si une route signe avant de vérifier? La démonstration de fuite crée volontairement cette route incorrecte :

Fenêtre de terminal
python -m scripts.leak_demo file
3. A signed download link is a key on its own (constructed: the link is made for
Bramble Books' receipt, as a route would make it if it signed before checking):
GET http://127.0.0.1:10000/devstoreaccount1/attachments/tickets/T-40001/e7e54964-6167-47fa-8c59-b0b14b2d7e4a/receipt-BB-310729.pdf?... (no token, no session)
-> 200, 724 bytes. Whoever has the link has the file until 06:34:14 UTC.

La démonstration a récupéré le reçu de Bramble sans jeton ni session : 200, 724 octets. Votre heure d’expiration diffère. Personne ne peut reprendre le lien. Il fonctionne pendant 5 minutes pour quiconque le voit dans un journal, une conversation ou un courriel transféré.

La route de téléchargement de l’API fait trois choses, dans cet ordre. Elle effectue les quatre vérifications pour file.read. Elle cherche le fichier dans l’organisation : WHERE tenant_id = %s AND attachment_id = %s. Ensuite seulement, elle signe un lien valable 5 minutes. Essayez avec le véritable identifiant du reçu de Bramble :

Fenêtre de terminal
curl -H "Authorization: Bearer $I" http://127.0.0.1:8000/v1/tenants/bramble/attachments/e7e54964-6167-47fa-8c59-b0b14b2d7e4a/download
{"url":"http://127.0.0.1:10000/devstoreaccount1/attachments/tickets/T-40001/e7e54964-6167-47fa-8c59-b0b14b2d7e4a/receipt-BB-310729.pdf?se=2026-10-10T06%3A35%3A08Z&sp=r&sv=2026-10-06&sr=b&rscd=…&sig=…","expires_at":"2026-10-10T06:35:08Z"}

Ines est propriétaire, donc elle reçoit un lien. Sam connaît le même identifiant. Il fait la demande par sa propre organisation :

Fenêtre de terminal
curl -H "Authorization: Bearer $S" http://127.0.0.1:8000/v1/tenants/larkfield/attachments/e7e54964-6167-47fa-8c59-b0b14b2d7e4a/download
{"error":{"code":"not_found","message":"Attachment e7e54964-6167-47fa-8c59-b0b14b2d7e4a does not exist.","request_id":"d0f4e73a9a6c","fields":[]}}

L’API ne crée aucun lien. Par bramble, il reçoit le message Not found. (introuvable) destiné aux personnes externes, comme dans la première leçon.

Ines, propriétaire chez bramble, envoie son jeton à ticket-api. L'API 1. vérifie son adhésion et son autorisation file.read, 2. trouve le fichier avec WHERE tenant_id = bramble, 3. signe un lien valable 5 minutes. Le lien, …/receipt-BB-310729.pdf?se=…&sp=r&sig=…, mène au stockage objet, qui vérifie seulement la signature et l'heure d'expiration. Un trajet rouge en pointillés : quiconque obtient le lien, par exemple dans un courriel transféré, reçoit 200 et 724 octets, sans jeton ni session. Une note : Sam demande le même identifiant de fichier par larkfield et reçoit 404 Attachment … does not exist; aucun lien n'est créé. La vérification précède la signature, car ensuite le lien fonctionne pour tout le monde jusqu'à son expiration.
Un lien signé est une clé en soi. L'API vérifie l'appelant et trouve le fichier dans l'organisation avant de signer; ensuite, seule l'heure d'expiration protège le fichier.
Description textuelle du diagramme

Ines demande un téléchargement. L’API vérifie son adhésion et son rôle. Elle cherche le fichier en incluant l’organisation de Bramble dans la requête. Ensuite seulement, elle signe un lien valable 5 minutes. Le stockage vérifie la signature et l’heure, rien d’autre. Quiconque obtient le lien peut donc télécharger les 724 octets sans jeton, par exemple depuis un courriel transféré. La demande de Sam pour le même identifiant par Larkfield n’atteint jamais l’étape de signature. La recherche ne trouve rien, et la réponse est 404.

Les nouveaux fichiers utilisent un chemin qui contient l’organisation : tenants/bramble/tickets/T-40002/<id>/photo.png. Les fichiers des données chargées gardent les chemins du cours sur les bases de données, tickets/T-40001/…. Le lien ci-dessus le montre. Le chemin ne protège rien, car la vérification se fait dans l’API. Il aide les gens : une sauvegarde, une suppression ou un audit peut porter sur le dossier d’une seule organisation.

Recherche de billets similaires : filtrer dans la même requête

Section intitulée « Recherche de billets similaires : filtrer dans la même requête »

Ines demande des billets similaires à son T-40001, « Facturé deux fois pour la commande BB-310729 ». Larkfield a aussi des billets « Facturé deux fois », et leurs vecteurs sont proches. La démonstration lance la recherche vectorielle sans l’organisation, puis avec elle :

Fenêtre de terminal
python -m scripts.leak_demo search
1. Similar tickets of Bramble Books' T-40001, WITHOUT the organization filter (wrong):
T-40030 0.036 bramble: Charged twice for order BB-310465
T-40007 0.037 bramble: Charged twice for order BB-310771
T-40004 0.042 bramble: Charged twice for order BB-310181
T-30050 0.048 larkfield: Charged twice for order LK-710804
T-30076 0.051 larkfield: Charged twice for order LK-140806
WITH the filter (the API's code):
T-40030 0.036 bramble: Charged twice for order BB-310465
T-40007 0.037 bramble: Charged twice for order BB-310771
T-40004 0.042 bramble: Charged twice for order BB-310181
T-40002 0.061 bramble: Charged twice for my book order
T-40034 0.068 bramble: Charged twice for my book order

Les nombres sont des distances : une valeur plus petite indique un sens plus proche. Sans filtre, 2 des 5 résultats appartiennent à Larkfield. Leurs distances sont 0,048 et 0,051. Une page de soutien affichant des billets similaires montrerait à Ines la commande d’un client de Larkfield. Avec le filtre, les quatrième et cinquième places reviennent aux billets de Bramble, dont les vecteurs sont un peu plus éloignés.

Le filtre fait partie de la même instruction SQL que la recherche : e.tenant_id = %(tenant_id)s. Il accompagne les filtres d’équipe et de statut du cours sur les bases de données. Ne filtrez pas ensuite dans Python. Une recherche de 5 résultats qui en élimine 2 n’en renvoie que 3. L’index approximatif (HNSW) trouve des candidats de toutes les organisations. Le balayage itératif de pgvector poursuit alors la lecture jusqu’à ce que 5 lignes passent le filtre. Par l’API, Ines obtient les cinq billets de Bramble :

Fenêtre de terminal
curl -H "Authorization: Bearer $I" "http://127.0.0.1:8000/v1/tenants/bramble/tickets/T-40001/similar?k=5"

Les valeurs de ticket_id sont T-40030, T-40007, T-40004, T-40002 et T-40034.

Documents et citations : l’organisation et le niveau d’accès

Section intitulée « Documents et citations : l’organisation et le niveau d’accès »

Les documents d’aide ont deux autorisations. Chaque document appartient à une organisation. Certains sont aussi réservés au personnel, comme les limites d’approbation des remboursements de Larkfield. La route GET /v1/tenants/{tenant}/documents?q= les recherche et renvoie chaque résultat avec son doc_id et sa version. Ces deux valeurs forment une citation : une référence qu’une personne ou un assistant d’IA peut citer, et qu’un lecteur peut ouvrir.

Cherchez « refund approval » (approbation de remboursement) en tant qu’Omar, puis en tant que Sam :

Fenêtre de terminal
curl -H "Authorization: Bearer $O" "http://127.0.0.1:8000/v1/tenants/larkfield/documents?q=refund%20approval"
curl -H "Authorization: Bearer $S" "http://127.0.0.1:8000/v1/tenants/larkfield/documents?q=refund%20approval"

Les réponses sont longues. Voici donc leurs citations :

Appelant access_level Résultats (doc_id, version, accès)
Omar, read_only public returns-policy v4 public, refunds-and-payments v3 public
Sam, staff staff refund-approval-procedure v2 staff, returns-policy v4 public, refunds-and-payments v3 public, warranty-claims-procedure v3 staff

La réponse d’Omar ne contient aucun document réservé au personnel, donc elle ne peut pas en citer. C’est plus important qu’il n’y paraît. Un assistant d’IA qui rédige une réponse à partir de ces résultats ne peut citer que ce que l’appelant peut ouvrir. Imaginez que la recherche renvoie la procédure réservée au personnel. L’assistant pourrait alors divulguer « un agent peut approuver jusqu’à 100 dollars » dans une phrase, sans aucun lien.

Les deux boutiques ont un document nommé returns-policy. L’organisation fait partie de la clé du document, donc ils ne se mélangent jamais. Ines cherche « returns » (retours) et obtient la version 1 du document returns-policy de Bramble, « Retourner un livre ». Elle n’obtient jamais la version 4 de Larkfield.

L’API garde les réponses des recherches de billets similaires et de documents pendant 60 secondes (CACHE_SECONDS). Une réponse en cache évite la requête, et donc son filtre. La clé de cache, l’étiquette sous laquelle une réponse est conservée, doit donc contenir tout ce qui change la réponse.

La démonstration crée volontairement deux caches incorrects :

Fenêtre de terminal
python -m scripts.leak_demo cache
2a. A cache key without the organization (wrong): key = ('similar', ticket, k)
Ines gets: ['T-30050 Charged twice for order LK-710804', 'T-30168 Charged twice for order LK-919420', 'T-30064 Charged twice for order LK-167846']
With the organization in the key, Ines's key misses: None
2b. A key without the access level (wrong): Sam (staff) asks first, then Omar
Omar (read-only) gets: [('refund-approval-procedure', 'staff'), ('returns-policy', 'public'), ('refunds-and-payments', 'public'), ('warranty-claims-procedure', 'staff')]

Dans 2a, Grace demande des billets similaires à T-30002. Sa réponse est conservée sous ('similar', 'T-30002', 3). Ines demande ensuite le même identifiant de billet par Bramble Books. Elle passe la vérification d’adhésion à sa propre organisation. Le cache répond avant l’exécution de la requête, et elle obtient les trois billets de Larkfield. Dans 2b, Omar obtient la réponse de Sam destinée au personnel, avec deux documents réservés au personnel.

À gauche, incorrect : la clé ne contient aucune organisation. Grace demande d'abord larkfield et T-30002 et conserve la réponse sous la clé ('similar', 'T-30002', 3). Ines demande ensuite bramble et T-30002 et trouve la même clé. Ines obtient les billets T-30050, T-30168 et T-30064 de Larkfield; sa propre requête avait le filtre, mais le cache a répondu en premier. À droite : la clé commence par l'organisation. Grace conserve la réponse sous ('larkfield', 'similar', 'T-30002', 3); Ines cherche ('bramble', 'similar', 'T-30002', 3). La clé d'Ines ne trouve rien en cache. Sa requête s'exécute dans bramble, où T-30002 n'existe pas, donc la réponse est 404. En bas : la même règle s'applique au niveau d'accès; ('larkfield', 'documents', 'staff', 'refund approval') et ('larkfield', 'documents', 'public', 'refund approval') sont deux clés. Omar n'obtient donc jamais la réponse de Sam réservée au personnel.
Une clé de cache contient tout ce qui change la réponse : l'organisation d'abord, puis le niveau d'accès, puis les valeurs propres à la requête.
Description textuelle du diagramme

Sans organisation dans la clé, la réponse de Grace et la question d’Ines partagent une clé. Ines obtient donc les billets de Larkfield. Avec l’organisation en premier, ce sont deux clés. La recherche d’Ines ne trouve rien en cache, et sa requête s’exécute dans Bramble Books. La recherche de documents ajoute le niveau d’accès à sa clé. La réponse du personnel reste donc séparée de la réponse publique.

Le fichier ticket_api/cache.py du projet crée chaque clé avec une seule fonction :

def cache_key(tenant_id: str, name: str, *parts) -> tuple:
if not tenant_id:
raise ValueError("a cache key of an organization's data needs the organization")
return (tenant_id, name, *parts)

La route des billets similaires appelle cache_key(caller.tenant_id, "similar", ticket_id, k, team, status, version). La route des documents appelle cache_key(caller.tenant_id, "documents", level, q.lower()). Ici, level vaut staff (personnel) ou public, selon le rôle de l’appelant. Deux autres règles accompagnent le cache. Le cache vient après les quatre vérifications. Une personne qui perd son accès reçoit donc 404, même si une réponse est encore en cache. Chaque réponse ne reste que 60 secondes.

Ce cache se trouve dans un seul processus de l’API. Un cache partagé, comme Redis, suit la même règle pour les clés. Le cours Mise à l’échelle des API et des charges de travail d’IA en ajoute un.

Voici tout le module en une image. L’autorisation commence dans la requête et doit atteindre chaque endroit d’où proviennent les données.

Rangée du haut, quatre étapes : Requête, GET /v1/tenants/bramble/…, l'organisation est dans le chemin; Identité, jeton ou session vérifié, sub = usr-ines; Adhésion, Ines chez bramble : owner, aucune : 404; Rôle et portée, owner peut ticket.read, file.read, document.read_staff. Elles forment un seul Caller : utilisateur usr-ines, locataire bramble, rôle owner. Le Caller entre dans cinq espaces de stockage verts. Chacun a un encadré rouge en pointillés montrant la fuite sans lui. Lignes : WHERE tenant_id = bramble et sécurité au niveau des lignes; sans cela, une liste contenait 77 billets de Larkfield sur 100. Lien de fichier : chercher le fichier dans bramble, puis signer un lien valable 5 minutes; si la signature vient d'abord, quiconque possède le lien obtient 200, 724 octets. Recherche de billets similaires : e.tenant_id = bramble dans la requête vectorielle; sans cela, 2 sur 5 sont les billets T-30050 et T-30076 de Larkfield. Documents : locataire et niveau d'accès, documents réservés au personnel pour owner et staff; sans le niveau, Omar obtient 2 documents réservés au personnel. Clé de cache : ('bramble', 'similar', 'T-40001', 5, …); sans le locataire, Ines obtient T-30050 et d'autres billets de Larkfield.
Propagation des autorisations : l'appelant vérifié, avec son organisation et son rôle, accompagne chaque requête, lien de fichier, recherche, citation et clé de cache. Chaque encadré rouge représente une fuite mesurée dans ce cours quand l'organisation était omise.
Description textuelle du diagramme

Une requête nomme l’organisation dans son chemin. L’API vérifie l’identité, l’adhésion, le rôle et la portée, puis crée un appelant vérifié : Ines, Bramble Books, propriétaire. Cet appelant accompagne cinq endroits. Les requêtes sur les lignes incluent tenant_id et la sécurité au niveau des lignes. La route des fichiers cherche le fichier dans l’organisation avant de signer un lien. La recherche vectorielle filtre par organisation dans la même requête. La recherche de documents utilise l’organisation et le niveau d’accès. La clé de cache commence par l’organisation. Sous chaque endroit figure la fuite observée dans le cours sans cette protection. La liste contenait 77 billets de Larkfield sur 100. Le reçu s’ouvrait pour quiconque possédait le lien. Deux billets similaires sur cinq provenaient de Larkfield. Omar obtenait deux documents réservés au personnel. Une entrée de cache partagée donnait à Ines les billets de Larkfield.

Vous avez vu les exemples détaillés. Attaquez maintenant votre propre API d’exercice en tant que Camille et Omar, avec des enregistrements inventés. Prédisez chaque réponse avant d’envoyer la requête.

  1. Camille fait partie du personnel de Larkfield et connaît l’identifiant du reçu de Bramble. Faites la demande par Larkfield :

    Fenêtre de terminal
    curl -H "Authorization: Bearer $C" http://127.0.0.1:8000/v1/tenants/larkfield/attachments/e7e54964-6167-47fa-8c59-b0b14b2d7e4a/download

    Résultat attendu : 404, Attachment e7e54964-… does not exist. (la pièce jointe e7e54964-… n’existe pas). Par bramble, Camille a un accès en lecture seule et obtient un lien, car read_only peut utiliser file.read.

  2. Camille écrit un message sur le billet T-40001 de Bramble par Larkfield, où Camille fait partie du personnel. Résultat attendu : 404. Ce rôle de personnel s’applique à Larkfield, et T-40001 n’y existe pas.

  3. Omar téléverse un fichier vers le billet T-30002 de Larkfield :

    Fenêtre de terminal
    curl -X POST -H "Authorization: Bearer $O" -H "Content-Type: application/json" -d '{"file_name": "photo.png", "content_type": "image/png", "size_bytes": 1200}' http://127.0.0.1:8000/v1/tenants/larkfield/tickets/T-30002/attachments

    Résultat attendu : 403, Your role may not do this (read_only may not file.upload). (votre rôle ne permet pas cette action; read_only ne permet pas file.upload). L’API ne crée aucun lien de téléversement.

  4. Camille cherche « wholesale price » (prix de gros) dans les documents de Bramble. Ines obtient supplier-terms, « Conditions de vente en gros (personnel seulement) ». Prédisez d’abord la réponse de Camille : Camille fait partie du personnel de Larkfield.

    Résultat attendu : {"access_level":"public","results":[]}. Le niveau d’accès vient du rôle dans cette organisation, et chez Bramble Books, Camille a un accès en lecture seule.

Appliquez ces règles partout où les données quittent la base de données : fichiers, index de recherche, caches, exportations et requêtes d’IA. Le coût est faible. Un lien de fichier attend une recherche supplémentaire. La recherche vectorielle lit quelques candidats de plus. Un cache garde une entrée par organisation et niveau d’accès, donc il trouve moins souvent une réponse. C’est le bon compromis : une absence en cache coûte des millisecondes, et une fuite coûte la confiance d’un client.

Une expiration rapide du lien a aussi un coût. Une personne avec une connexion lente peut avoir besoin d’un nouveau lien. Cinq minutes suffisent pour commencer un téléchargement. C’est aussi assez court pour qu’un lien divulgué cesse rapidement de fonctionner.

Erreur courante

Le lien de téléchargement cesse de fonctionner après quelques minutes

Symptôme : un lien qui fonctionnait reçoit maintenant une erreur du stockage au lieu du fichier. L’auteur a demandé le lien d’Ines à 7 h 32 min 00 s UTC. curl l’a récupéré aussitôt : 200 724. L’auteur a conservé le url de la réponse de l’API dans URL. Il a récupéré le même lien à 7 h 37 min 06 s (vos heures diffèrent; sous Windows, tapez curl.exe) :

Fenêtre de terminal
curl -s -w '\n%{http_code}\n' "$URL"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Error>
<Code>AuthorizationFailure</Code>
<Message>Server failed to authenticate the request. Make sure the value of the Authorization header is formed correctly including the signature.
RequestId:2a19621e-…
Time:2026-10-10T07:37:06.469Z</Message>
</Error>
403

Cause : le lien a expiré à 7 h 37 min 00 s (expires_at dans la réponse de l’API). C’est voulu : il dure 5 minutes. Le message parle d’un en-tête, mais la requête n’en avait aucun. Seule l’heure d’expiration du lien était passée. Cette réponse vient d’Azurite; le vrai service Azure Storage pourrait la formuler autrement (non testé).

Solution : demandez un nouveau lien à l’API. L’API refait les vérifications. Une personne qui a perdu l’accès reçoit donc 404 au lieu d’un lien. Pendant la même exécution, Ines a refait la demande à 7 h 37 min 06 s. Elle a reçu un nouveau lien, "expires_at":"2026-10-10T07:42:06Z", et curl l’a récupéré : 200 724.

Erreur courante

Moins de billets semblables que demandé

Symptôme : une recherche de 5 billets de soutien semblables en renvoie 3. Pourtant, l’organisation a plus de billets de ce type.

Cause : un filtre a été appliqué après la recherche. La recherche a trouvé 5 voisins, puis le code a éliminé ceux d’une autre organisation.

Solution : ajoutez l’organisation à la même instruction SQL que la recherche vectorielle, comme le fait repository.similar_tickets. La base de données poursuit alors la lecture jusqu’à ce qu’assez de lignes passent le filtre.

Erreur courante

Une clé de cache construite à la main

Symptôme : une nouvelle route avec cache fonctionne dans tous les tests avec une seule boutique. Avec deux boutiques, une personne voit parfois la réponse d’une autre organisation, mais seulement pendant les 60 secondes du cache.

Cause : la clé était un tuple construit à la main, comme ("export", ticket_id), sans organisation.

Solution : construisez chaque clé avec cache_key(caller.tenant_id, …). Ajoutez le niveau d’accès quand le rôle change la réponse. cache_key("", …) déclenche ValueError. L’absence d’organisation provoque donc une erreur visible.

Réinitialisation. Les démonstrations ne changent rien. L’API refuse l’étape 3, donc elle n’ajoute rien. Si vous avez téléversé vos propres fichiers, exécutez python -m scripts.load --reset.

Vérifiez votre compréhension

Expliquez pourquoi l'API doit vérifier l'appelant avant de signer un lien de téléchargement, pas seulement lors de son utilisation.

Afficher la réponse

Le stockage vérifie seulement la signature et l’heure d’expiration d’un lien, pas la personne. Une fois le lien signé par l’API, toute personne qui le possède peut télécharger le fichier jusqu’à son expiration, sans jeton. La démonstration a récupéré le reçu de Bramble sans jeton : 200, 724 octets. Il faut donc décider avant de signer : les quatre vérifications, puis une recherche du fichier dans l’organisation.

Vérifiez votre compréhension

Prédisez : la clé de cache de la route des documents est (locataire, 'documents', niveau, requête). Omar cherche 'refund approval' (approbation de remboursement) dans les documents de Larkfield, 10 secondes après Sam. Omar trouve-t-il une réponse en cache? Que reçoit-il?

Afficher la réponse

Non. La réponse de Sam se trouve sous ('larkfield', 'documents', 'staff', 'refund approval'). Omar a un accès en lecture seule. Sa clé est donc ('larkfield', 'documents', 'public', 'refund approval'). L’API exécute sa recherche et renvoie 2 documents publics, returns-policy et refunds-and-payments.

Vérifiez votre compréhension

Vérification du module : prouvez que l'accès est refusé même si quelqu'un devine un identifiant d'enregistrement valide. Utilisez un vrai identifiant de billet et un vrai identifiant de pièce jointe de Bramble Books, avec le jeton de Sam. Quelles requêtes envoyez-vous, que répond chacune et quels tests vérifient la même chose?

Afficher la réponse

Les identifiants T-40001 et e7e54964-6167-47fa-8c59-b0b14b2d7e4a correspondent à de vrais enregistrements de Bramble.

  • GET /v1/tenants/larkfield/tickets/T-40001 : 404, « Ticket T-40001 does not exist. » (le billet T-40001 n’existe pas), comme pour le billet absent T-49999.
  • GET /v1/tenants/bramble/tickets/T-40001 : 404, « Not found. » (introuvable), comme pour une organisation inventée.
  • GET /v1/tenants/larkfield/attachments/e7e54964-…/download : 404, « Attachment … does not exist. » (la pièce jointe … n’existe pas), et l’API ne crée aucun lien.
  • python -m scripts.attempts se termine par 0 of 10 attempts got what they should not get. (0 tentative sur 10 a obtenu un accès interdit).
  • python -m pytest tests/test_access_control.py tests/test_tenant_queries.py tests/test_row_security.py réussit 25 tests, dont test_another_organizations_ticket_looks_like_a_ticket_that_does_not_exist. La base de données ajoute une deuxième preuve. Avec ticket_app pour Larkfield, SELECT count(*) FROM tickets WHERE ticket_id = 'T-40001' vaut 0.

Récapitulatif

  • Un lien signé fonctionne pour n’importe qui jusqu’à son expiration. L’API vérifie donc l’appelant et trouve le fichier dans l’organisation avant de signer. Les nouveaux fichiers vont sous tenants/<organization>/.
  • La recherche de billets semblables filtre par organisation dans la même instruction SQL. Sans ce filtre, 2 des 5 résultats pour T-40001 de Bramble appartenaient à Larkfield.
  • Les résultats de documents contiennent l’organisation et le niveau d’accès. Leurs citations (doc_id, version) pointent seulement vers des documents que l’appelant peut ouvrir.
  • Une clé de cache contient tout ce qui change la réponse : l’organisation, le niveau d’accès, puis la requête. Le cache vient après les vérifications.

Récapitulatif du module. Chaque route se trouve sous son organisation et effectue quatre vérifications : identité, adhésion, rôle et portée, puis enregistrement dans l’organisation. Les personnes externes reçoivent le même code 404 pour les organisations réelles et inventées; les membres reçoivent 403. Chaque requête exige l’organisation comme argument, et la sécurité au niveau des lignes forme un deuxième verrou. Les fichiers, la recherche, les citations et les caches contiennent aussi l’organisation. scripts.attempts : 0 sur 10. Votre projet correspond à end-of-m04.

Ensuite, le travail exécuté plus tard : les tâches en arrière-plan doivent retenir qui a fait la demande et pour quelle organisation. Elles doivent revérifier les deux lors de leur exécution.

Leçon suivante : Contexte d'identité des tâches