Opérations privilégiées
- Leçon 15
- 50 min
- Module 5 : Travail en arrière-plan et administration
Leçon 15 sur 19 · Authentification, autorisations et applications multilocatairesOpérations privilégiées
1. Bases de l'identité et des autorisations
2. Comprendre la connexion et les jetons
3. Sessions et intégration à l'application
4. Contrôler l'accès aux ressources
5. Travail en arrière-plan et administration
6. Valider et documenter la conception
Ce que vous saurez faire
Permettez à une personne du soutien d’aider une organisation sans lui donner une clé qui ouvre toutes les organisations. Stockez le rôle de plateforme dans la base de données. Accordez un accès temporaire en lecture seule, avec une raison. Consignez chaque utilisation là où le propriétaire de l’organisation peut la voir.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Les tâches en arrière-plan et la copie des fichiers du module 5, dans Contexte d’identité des tâches.
- Pourquoi une personne externe reçoit
404et un membre au rôle insuffisant reçoit403, dans L’autorisation dans l’API.
Votre projet
~/projects/ticket-apià l’instantanéend-of-m05, avec le fournisseur et l’API en marche, comme dans la dernière leçon.
Cette leçon n’ajoute aucun fichier. Elle utilise ticket_api/admin.py, ticket_api/audit.py et la migration 019_audit_and_support_access.sql, copiés dans la dernière leçon.
Un laissez-passer de visiteur, pas une clé universelle
Section intitulée « Un laissez-passer de visiteur, pas une clé universelle »Imaginez un immeuble de bureaux qui accueille plusieurs entreprises. Un spécialiste des ascenseurs doit vérifier un ascenseur au quatrième étage. L’immeuble pourrait lui donner une clé universelle. Cette clé ouvre tous les bureaux, à toute heure, tant que le spécialiste la garde. Personne ne sait quelles portes il a ouvertes.
La plupart des immeubles font autrement. À l’accueil, le spécialiste inscrit son nom, l’entreprise qu’il visite et la raison. Il reçoit un laissez-passer pour cet étage, valide jusqu’à 17 h aujourd’hui. Le registre des visiteurs consigne la visite, et l’entreprise peut le lire. Demain, le laissez-passer n’ouvre plus rien.
Le laissez-passer donne moins d’accès que la clé, mais assez pour le travail. Et tout le monde peut vérifier ensuite ce qui s’est passé.
Le problème : Kwame ne peut pas aider, et les solutions rapides sont pires
Section intitulée « Le problème : Kwame ne peut pas aider, et les solutions rapides sont pires »Ines appelle le service de soutien. La boutique a facturé un client deux fois, dans le billet de soutien T-40001. La page du billet lui semble incorrecte. Kwame gère le service pour toutes les boutiques. Il est administrateur de plateforme : il exploite toute la plateforme, sans être membre d’une boutique. Le module 1 ne lui a donné aucune adhésion. Quand il ouvre le billet, il reçoit donc la réponse destinée aux personnes externes. Voici l’exécution de GET /v1/tenants/bramble/tickets/T-40001 par l’auteur, avec le jeton de Kwame. Vous envoyez la même requête ci-dessous et obtenez le même 404 après la fin de son accès de soutien :
HTTP/1.1 404 Not Found{"error":{"code":"not_found","message":"Not found.","request_id":"bd6e3f55175c","fields":[]}}C’est correct, mais cela n’aide pas Ines. Amira propose trois solutions rapides, et chacune échoue :
- Ines partage ses identifiants de connexion. Kwame agit alors comme propriétaire de Bramble Books. Il peut modifier les membres et les billets, et chaque enregistrement indique « Ines l’a fait ».
- Faire de Kwame un propriétaire de chaque boutique. Il peut maintenant tout lire et tout modifier, partout, pour toujours. Personne ne pense à retirer les adhésions.
- Une clé universelle, ou
is_admin: truedans son jeton. Une seule valeur ouvre toutes les organisations. Les Exemples de menaces du module 1 ont montré l’effet d’une clé partagée : les 5 tentatives inventées sur 5 ont obtenu un accès interdit. Une revendication (claim) dans un jeton reste aussi valide jusqu’à son expiration. Personne ne peut donc la retirer rapidement.
Description textuelle du diagramme
Une clé universelle, ou une revendication is_admin dans un jeton, ouvre toutes les organisations pour toutes les actions, jusqu’à son retrait. Elle ne consigne aucune raison, et personne ne voit son utilisation. La clé universelle est une comparaison construite : le projet n’en contient aucune. Une autorisation de soutien dans ce projet ouvre une organisation, Bramble Books, pour ticket.read (lecture de billet) seulement, pendant au plus 60 minutes. Kwame peut y mettre fin plus tôt. Elle stocke une raison d’au moins 10 caractères. Ines peut voir l’autorisation et chacune de ses utilisations.
L’idée : un rôle distinct et une autorisation limitée avec une raison
Section intitulée « L’idée : un rôle distinct et une autorisation limitée avec une raison »Le laissez-passer comporte deux parties, et le projet reprend les deux.
D’abord, le rôle de plateforme est distinct des rôles des boutiques, et la base de données le stocke. users.platform_role vaut platform_admin (administrateur de plateforme) pour Kwame et reste vide pour tous les autres. Il n’est pas dans le jeton. Le fournisseur signe l’identité de Kwame; l’API de billets décide ce qu’il peut faire, comme pour chaque membre. Un changement prend donc effet à la prochaine requête. Le jeton ne contient jamais un pouvoir que personne ne peut retirer. Le rôle de plateforme seul n’ouvre aucune boutique : il permet seulement à Kwame de demander une autorisation.
Ensuite, l’accès à une boutique passe par une autorisation de soutien. Une autorisation de soutien est une ligne dans support_grants. Elle indique que cet administrateur peut lire les billets de cette organisation, pour cette raison, jusqu’à cette heure. Les équipes de sécurité appellent cette idée le moindre privilège (seulement ce que le travail exige). Elles parlent aussi d’accès juste-à-temps (seulement pendant le travail). Le code et la base de données contiennent les règles :
- Une organisation. L’autorisation désigne
bramble. Chez Larkfield, Kwame reste une personne externe. - Une raison d’au moins 10 caractères. Le modèle et une contrainte
CHECKdans la table refusent toute raison plus courte. Le propriétaire la lit. - Au plus 60 minutes. Le modèle accepte de 1 à 60 minutes. Une contrainte
CHECKrefuse toute durée supérieure, même depuis psql.POST …/revokemet fin à une autorisation plus tôt. ticket.readseulement. Dansticket_api/access.py, le rôlesupport(soutien) possède une seule action. Aucun fichier, aucune écriture, aucun membre.- Chaque utilisation laisse une trace. Chaque requête qui utilise l’autorisation écrit un événement d’audit
support_access.used, qu’elle soit permise ou refusée. L’API ne met jamais une autorisation en cache. Elle vérifie donc l’heure à chaque utilisation. - Le propriétaire la voit. Ines peut lister les autorisations de Bramble Books et lire le journal d’audit.
Un court exemple détaillé
Section intitulée « Un court exemple détaillé »L’autorisation de Kwame porte la raison « Ines a demandé de l’aide pour T-40001 (appel du 9 octobre) ». Le projet l’a créée à 6 h 45 min 37 s pour 15 minutes. Elle expire donc à 7 h 00 min 37 s. Voici ce que role_of() et decide() renvoient dans le projet pour cinq requêtes :
| Heure | Requête | Rôle trouvé | Réponse |
|---|---|---|---|
| 6 h 45 | lire bramble T-40001, avant l’autorisation | aucun | 404 |
| 6 h 50 | lire bramble T-40001 | support |
200, événement allowed (permis) |
| 6 h 51 | ajouter un message à T-40001 | support |
403, événement denied (refusé) |
| 6 h 52 | lire larkfield T-30002 | aucun (l’autorisation vise bramble) | 404 |
| 7 h 01 | lire bramble T-40001 | aucun (autorisation expirée) | 404 |
L’adhésion passe en premier. Seule une personne sans adhésion obtient le rôle de soutien. Et seulement pendant qu’une autorisation pour cette organisation est active.
Événements d’audit : le registre des visiteurs
Section intitulée « Événements d’audit : le registre des visiteurs »Le registre des visiteurs a aussi besoin d’une table. Un événement d’audit est une ligne qui indique qui a fait, ou tenté, quoi, dans quelle organisation, et le résultat. La migration 019 crée audit_events avec les colonnes at, actor_id, tenant_id, action, target, result (allowed, denied, done ou failed), reason, request_id et details.
Un événement d’audit ne contient jamais de jeton, de témoin (cookie), de mot de passe ou de texte d’un message. ticket_api/audit.py retire toute clé de details qui désigne un secret et remplace tout ce qui ressemble à un JWT. L’API écrit un changement et son événement dans la même transaction. Si le changement échoue, l’événement n’apparaît pas, et inversement.
Un registre dont un visiteur peut arracher les pages ne vaut rien. La table permet donc uniquement l’ajout : un déclencheur refuse chaque UPDATE et DELETE, pour chaque rôle de base de données. Vous testez cela ci-dessous.
Kwame aide Ines, étape par étape
Section intitulée « Kwame aide Ines, étape par étape »Obtenez un jeton pour chacune des trois personnes. Sous Windows, utilisez $KWAME = python -m scripts.login --user usr-kwame --print-access-token, et faites de même pour les autres. Utilisez aussi $V = "http://127.0.0.1:8000/v1" et curl.exe.
KWAME=$(python -m scripts.login --user usr-kwame --print-access-token)INES=$(python -m scripts.login --user usr-ines --print-access-token)GRACE=$(python -m scripts.login --user usr-grace --print-access-token)V=http://127.0.0.1:8000/v1D’abord, examinez le jeton de Kwame avec python -m scripts.token decode --user usr-kwame. Voici quelques revendications :
sub "usr-kwame" aud "ticket-api" client_id "ticket-cli" scope "tickets:read tickets:write members:manage"Il n’y a aucun rôle ni is_admin. L’API lit son rôle de plateforme dans la base de données. curl -s -H "Authorization: Bearer $KWAME" $V/me l’affiche :
{"user_id":"usr-kwame","kind":"user","name":"Kwame","platform_role":"platform_admin","client_id":"ticket-cli","scopes":["members:manage","tickets:read","tickets:write"],"token_expires_at":"2026-10-10T06:55:37Z","memberships":[]}memberships est vide : aucune boutique. Suivez maintenant le schéma ci-dessous, une requête à la fois.
Description textuelle du diagramme
Avant l’autorisation, la lecture de Kwame reçoit 404. Il demande un accès de soutien avec une raison et 15 minutes : 201, et un événement support_access.granted. Avec l’autorisation, il lit T-40001 : 200, et un événement au résultat permis. Il tente d’ajouter un message : 403, et un événement au résultat refusé. Chez Larkfield, il reçoit encore 404, car l’autorisation désigne seulement Bramble Books. Il révoque l’autorisation : 200, et un événement support_access.revoked. Ensuite, sa lecture reçoit de nouveau 404.
Tout le monde ne peut pas faire la demande. Grace est propriétaire, mais pas administratrice de plateforme :
curl -s -X POST -H "Authorization: Bearer $GRACE" -H "Content-Type: application/json" -d '{"reason": "I want to see Bramble Books tickets"}' $V/admin/tenants/bramble/support-access{"error":{"code":"forbidden","message":"Only a platform administrator may do this.","request_id":"49f06fe9b262","fields":[]}}L’API consigne aussi ce refus : un événement admin POST /v1/admin/tenants/bramble/support-access pour usr-grace, avec le résultat denied et aucune organisation.
Une raison et une courte durée. Kwame essaie la raison « help » (aide), puis 90 minutes. Les deux reçoivent 422, et fields (champs) indique le problème :
{"error":{"code":"invalid_request","message":"The request is not valid.","request_id":"c638fb5b5611","fields":["reason"]}}{"error":{"code":"invalid_request","message":"The request is not valid.","request_id":"414e71cad421","fields":["minutes"]}}L’autorisation. Une vraie raison et 15 minutes :
curl -s -X POST -H "Authorization: Bearer $KWAME" -H "Content-Type: application/json" -d '{"reason": "Ines asked for help with T-40001 (call on 9 October)", "minutes": 15}' $V/admin/tenants/bramble/support-access{"grant_id":"cab29c4e-36b0-44d0-9f73-1cd3170d5271","tenant_id":"bramble","user_id":"usr-kwame","reason":"Ines asked for help with T-40001 (call on 9 October)","created_at":"2026-10-10T06:45:37.820639Z","expires_at":"2026-10-10T07:00:37.820639Z","revoked_at":null}Lire, oui. Écrire, non. Une autre boutique, non. Exécutez ces trois requêtes :
curl -s $V/tenants/bramble/tickets/T-40001 -H "Authorization: Bearer $KWAME"curl -s -X POST -H "Authorization: Bearer $KWAME" -H "Content-Type: application/json" -d '{"author": "agent", "body": "Kwame from support here."}' $V/tenants/bramble/tickets/T-40001/messagescurl -s $V/tenants/larkfield/tickets/T-30002 -H "Authorization: Bearer $KWAME"La première réponse contient le billet complet : T-40001, Eleanor Price, « Charged twice for order BB-310729 » (facturé deux fois pour la commande BB-310729). Voici les deux autres :
{"error":{"code":"forbidden","message":"Your role may not do this (support may not ticket.write).","request_id":"5490405fc39f","fields":[]}}{"error":{"code":"not_found","message":"Not found.","request_id":"895df84d2427","fields":[]}}L’écriture reçoit 403, pas 404. Avec l’autorisation, Kwame peut savoir que le billet de Bramble Books existe; il ne peut simplement pas le modifier.
Ce qu’Ines voit
Section intitulée « Ce qu’Ines voit »Passez maintenant à Ines. Elle liste les accès de soutien à sa boutique avec curl -s -H "Authorization: Bearer $INES" $V/tenants/bramble/support-access :
{"items":[{"grant_id":"cab29c4e-36b0-44d0-9f73-1cd3170d5271","tenant_id":"bramble","user_id":"usr-kwame","reason":"Ines asked for help with T-40001 (call on 9 October)","created_at":"2026-10-10T06:45:37.820639Z","expires_at":"2026-10-10T07:00:37.820639Z","revoked_at":null}]}Puis l’événement le plus récent de son journal d’audit, avec curl -s -H "Authorization: Bearer $INES" "$V/tenants/bramble/audit?limit=1" :
{"items":[{"event_id":4,"at":"2026-10-10T06:45:37.871754Z","actor_id":"usr-kwame","actor_kind":"user","tenant_id":"bramble","action":"support_access.used","target":"POST /v1/tenants/bramble/tickets/T-40001/messages","result":"denied","reason":"Ines asked for help with T-40001 (call on 9 October)","request_id":"5490405fc39f","details":{"action":"ticket.write","grant_id":"cab29c4e-36b0-44d0-9f73-1cd3170d5271"}}]}C’est l’écriture refusée de Kwame. Votre event_id est plus élevé si le journal d’audit contient déjà les événements de la dernière leçon. L’événement indique qui (usr-kwame), où (bramble), quoi (POST …/messages, ticket.write), le résultat et la raison de l’autorisation. Son request_id correspond à celui de la réponse 403 ci-dessus et de la ligne du journal de l’API. Une personne peut donc suivre une requête depuis la réponse jusqu’au journal et à l’audit. Aucun jeton n’y figure. Sans limit=1, Ines voit aussi la lecture permise et l’événement support_access.granted.
Quand il a terminé, Kwame met fin à l’autorisation plus tôt. Utilisez le grant_id de votre autorisation :
curl -s -X POST -H "Authorization: Bearer $KWAME" $V/admin/support-access/<grant_id>/revokecurl -s $V/tenants/bramble/tickets/T-40001 -H "Authorization: Bearer $KWAME"La première réponse montre l’autorisation avec revoked_at défini. La seconde est de nouveau 404 : la requête suivante n’a trouvé aucune autorisation active.
Personne ne peut modifier le journal d’audit
Section intitulée « Personne ne peut modifier le journal d’audit »Ouvrez psql (docker compose exec db psql -U tickets -d tickets). Essayez de faire passer un refus pour une réussite, puis de supprimer les événements de Kwame :
UPDATE audit_events SET result = 'allowed' WHERE result = 'denied';DELETE FROM audit_events WHERE actor_id = 'usr-kwame';ERROR: audit events cannot be changed or deletedCONTEXT: PL/pgSQL function audit_events_append_only() line 3 at RAISEERROR: audit events cannot be changed or deletedCONTEXT: PL/pgSQL function audit_events_append_only() line 3 at RAISEVotre utilisateur psql, tickets, est un superutilisateur, et le déclencheur refuse quand même. Le rôle de base de données de l’API, ticket_app, a encore moins d’autorisations :
SELECT p AS privilege, has_table_privilege('ticket_app', 'audit_events', p) AS ticket_app FROM unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE']) AS p; privilege | ticket_app-----------+------------ SELECT | t INSERT | t UPDATE | f DELETE | f TRUNCATE | f(5 rows)La limite de durée se trouve aussi dans la base de données. Une autorisation de huit heures, écrite à la main, échoue :
INSERT INTO support_grants (grant_id, tenant_id, user_id, reason, expires_at) VALUES (gen_random_uuid(), 'bramble', 'usr-kwame', 'Just looking', now() + interval '8 hours');ERROR: new row for relation "support_grants" violates check constraint "support_grants_at_most_one_hour"Une limite à reconnaître. Un déclencheur de ligne ne s’exécute pas pour TRUNCATE, qui vide toute une table d’un coup. python -m scripts.load --reset utilise TRUNCATE comme superutilisateur. Cette commande vide donc encore le journal d’audit pour vos exercices. Le rôle de l’API ne peut pas le faire. En production, évitez le superutilisateur pour le travail quotidien. Copiez les événements vers un second stockage où l’application ne peut pas écrire. Auditabilité poursuit ce sujet.
Quand l’utiliser et ce que cela coûte
Section intitulée « Quand l’utiliser et ce que cela coûte »Utilisez un rôle de plateforme distinct et des autorisations quand votre personnel doit voir les données des clients : soutien, exploitation, réponse aux incidents. Limitez l’autorisation aux besoins de la tâche et permettez au client de la voir.
Cela a des coûts :
- Friction. Kwame doit s’arrêter et écrire une raison avant de pouvoir consulter les données. Certaines équipes ajoutent une seconde personne qui approuve l’autorisation. Cela demande plus de travail, et ce cours ne le met pas en place.
- Les raisons doivent être lues. Une raison est utile seulement si le propriétaire ou un réviseur la lit. Des journaux non lus ne protègent personne.
- Les autorisations courtes prennent fin pendant le travail. La limite est de soixante minutes ici. Kwame fait une nouvelle demande, ce qui crée un second enregistrement.
- La lecture seule ne suffit pas toujours. Si le soutien doit modifier des données, créez une action distincte et plus limitée, avec ses propres événements. N’élargissez pas
supportà toutes les actions. - Le rôle d’administrateur lui-même est puissant. Kwame peut aussi supprimer une organisation, ce que la prochaine leçon exécute. Il devrait y avoir très peu d’administrateurs de plateforme, et leurs actions devraient être auditées de la même façon.
Erreurs courantes
Section intitulée « Erreurs courantes »Erreur courante
422 avec les champs reason ou minutes
Symptôme : {"error":{"code":"invalid_request", … "fields":["reason"]}} ou "fields":["minutes"].
Cause : la raison compte moins de 10 caractères, ou la durée est hors de l’intervalle de 1 à 60 minutes.
Solution : écrivez la vraie raison, par exemple le billet et l’appel, et demandez au plus 60 minutes.
Erreur courante
403 : seul un administrateur de plateforme peut faire cela
Symptôme : la route d’accès de soutien répond 403 avec ce message.
Cause : le jeton n’est pas celui de Kwame. Seul un utilisateur dont users.platform_role vaut platform_admin peut faire la demande.
Solution : utilisez --user usr-kwame quand vous obtenez le jeton.
Erreur courante
Kwame reçoit 404 même s'il a créé une autorisation
Symptôme : une lecture chez Bramble Books reçoit {"error":{"code":"not_found","message":"Not found.", …}}.
Cause : l’autorisation a expiré, ou Kwame l’a révoquée. Ou le chemin désigne une autre organisation que celle de l’autorisation.
Solution : listez les autorisations comme Ines (…/tenants/bramble/support-access) et vérifiez expires_at et revoked_at. Demandez une nouvelle autorisation au besoin.
Erreur courante
token decode : aucun fichier ou dossier de ce nom : '.tokens/usr-kwame.json'
Symptôme : python -m scripts.token decode --user usr-kwame s’arrête avec une erreur FileNotFoundError.
Cause : le script décode le jeton enregistré par scripts.login. Kwame ne s’est pas encore connecté.
Solution : exécutez d’abord python -m scripts.login --user usr-kwame.
Réinitialisation. python -m scripts.load --reset vide les autorisations et le journal d’audit, puis recharge les deux boutiques.
Vérification des connaissances
Section intitulée « Vérification des connaissances »Vérifiez votre compréhension
Pourquoi le rôle de plateforme de Kwame est-il une colonne de la table des utilisateurs, et non une revendication dans son jeton d'accès? Donnez deux raisons.
Afficher la réponse
D’abord, un changement prend effet à la requête suivante : l’API lit la colonne chaque fois. Une revendication reste dans un jeton jusqu’à son expiration (10 minutes ici), et personne ne peut la retirer plus tôt. Ensuite, le jeton indique qui est la personne, et l’API décide ce qu’elle peut faire, comme pour les adhésions. Le fournisseur, qui peut appartenir à quelqu’un d’autre, ne devrait pas décider des pouvoirs dans l’API de billets. De plus, le rôle de plateforme seul ne donne accès à aucune boutique : il permet seulement à Kwame de demander une autorisation.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Kwame a une autorisation active pour Bramble Books. Prédisez la réponse et l'événement d'audit pour : (a) GET bramble/tickets/T-40001, (b) POST d'un message à T-40001, (c) GET larkfield/tickets/T-30002.
Afficher la réponse
(a) 200, avec un événement support_access.used (utilisation de l’accès de soutien), résultat allowed (autorisé). (b) 403 (support may not ticket.write, le soutien ne peut pas écrire dans les billets), avec un événement support_access.used, résultat denied (refusé). (c) 404 : l’autorisation vise seulement Bramble Books, donc Kwame est un utilisateur externe chez Larkfield. L’API n’écrit aucun événement de soutien, car Kwame n’a utilisé aucune autorisation.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Votre collègue dit : « La table d'audit permet seulement les ajouts, donc personne ne peut jamais supprimer un événement. » Est-ce tout à fait vrai dans ce projet? Qu'ajouteriez-vous en production?
Afficher la réponse
Pas tout à fait. Le déclencheur refuse UPDATE (mise à jour) et DELETE (suppression) pour tous les rôles. Le rôle de l’API, ticket_app, peut seulement effectuer SELECT (sélection) et INSERT (insertion). Mais un déclencheur de ligne ne s’exécute pas pour TRUNCATE (vidage de la table). Un superutilisateur (l’utilisateur d’exercice tickets) peut vider la table ou supprimer le déclencheur, et scripts.load --reset la vide volontairement. En production, n’utilisez pas l’accès de superutilisateur pour le travail quotidien. Copiez les événements dans un stockage distinct que l’application ne peut pas modifier.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Un administrateur de plateforme exploite le service. Le rôle est
users.platform_roledans la base de données, et non une revendication du jeton. À lui seul, il ne donne accès à aucune boutique. - Une autorisation de soutien vise une seule organisation, avec
ticket.read(lecture des billets) seulement, pendant au plus 60 minutes. Elle exige une raison d’au moins 10 caractères. La révocation y met fin à la requête suivante. - Chaque utilisation écrit un événement d’audit
support_access.used, que l’accès soit autorisé ou refusé. Le propriétaire consulte les autorisations et l’audit. audit_eventspermet seulement les ajouts : un déclencheur refuseUPDATEetDELETE, même pour un superutilisateur.TRUNCATEfonctionne toujours, etload --resetl’utilise.
Ensuite, des personnes partent, des rôles changent et une boutique ferme complètement. Vous mesurez combien de temps l’ancien accès reste valide après chaque changement.
Leçon suivante : Changements de cycle de vie