From 2dd28a37ab3aa170e7eb9b00209b800d23559de9 Mon Sep 17 00:00:00 2001 From: imperosol Date: Tue, 20 May 2025 21:25:43 +0200 Subject: [PATCH] api key doc for developers --- .../{connect-api.md => api/connect.md} | 4 +- docs/tutorial/api/dev.md | 132 ++++++++++++++++++ mkdocs.yml | 4 +- 3 files changed, 137 insertions(+), 3 deletions(-) rename docs/tutorial/{connect-api.md => api/connect.md} (98%) create mode 100644 docs/tutorial/api/dev.md diff --git a/docs/tutorial/connect-api.md b/docs/tutorial/api/connect.md similarity index 98% rename from docs/tutorial/connect-api.md rename to docs/tutorial/api/connect.md index 72c01a85..8ce52bdd 100644 --- a/docs/tutorial/connect-api.md +++ b/docs/tutorial/api/connect.md @@ -41,7 +41,7 @@ et de la méthode d'authentification. Vous pouvez vous connecter directement sur l'interface Swagger, en cliquant sur ce bouton, en haut à droite : -![Swagger auth (1)](../img/api_key_authorize_1.png) +![Swagger auth (1)](../../img/api_key_authorize_1.png) /// caption Bouton d'autorisation sur Swagger /// @@ -50,7 +50,7 @@ Puis rentrez votre clef d'API dans le champ prévu à cet effet, et cliquez sur authorize : -![Swagger auth (2)](../img/api_key_authorize_2.png) +![Swagger auth (2)](../../img/api_key_authorize_2.png) /// caption Saisie de la clef d'API /// diff --git a/docs/tutorial/api/dev.md b/docs/tutorial/api/dev.md new file mode 100644 index 00000000..200b0730 --- /dev/null +++ b/docs/tutorial/api/dev.md @@ -0,0 +1,132 @@ + +Pour l'API, nous utilisons `django-ninja` et sa surcouche `django-ninja-extra`. +Ce sont des librairies relativement simples et qui présentent +l'immense avantage d'offrir des mécanismes de validation et de sérialisation +de données à la fois simples et expressifs. + +## Schéma de données + +Le cœur de django-ninja étant sa validation de données grâce à Pydantic, +le développement de l'API commence par l'écriture de ses schémas de données. + +Pour en comprendre le fonctionnement, veuillez consulter +[la doc de django-ninja](https://django-ninja.dev/guides/response/). + +Il est également important de consulter +[la doc de pydantic](https://docs.pydantic.dev/latest/). + +Notre surcouche par-dessus les schémas de django-ninja est relativement mince. +Elle ne comprend que [UploadedImage][core.schemas.UploadedImage], qui hérite de +[`UploadedFile`](https://django-ninja.dev/guides/input/file-params/?h=upl) +pour le restreindre uniquement aux images. + +## Authentification et permissions + +### Authentification + +Notre API offre deux moyens d'authentification : + +- par cookie de session (la méthode par défaut de django) +- par clef d'API + +La plus grande partie des routes de l'API utilisent la méthode par cookie de session. + +Pour placer une route d'API derrière l'une de ces méthodes (ou bien les deux), +utilisez l'attribut `auth` et les classes `SessionAuth` et +[`ApiKeyAuth`][apikey.auth.ApiKeyAuth]. + +!!!example + + ```python + @api_controller("/foo") + class FooController(ControllerBase): + # Cette route sera accessible uniquement avec l'authentification + # par cookie de session + @route.get("", auth=[SessionAuth()]) + def fetch_foo(self, club_id: int): ... + + # Et celle-ci sera accessible peut importe la méthode d'authentification + @route.get("/bar", auth=[SessionAuth(), ApiKeyAuth()]) + def fetch_bar(self, club_id: int): ... + ``` + +### Permissions + +Si l'utilisateur est connecté, ça ne veut pas dire pour autant qu'il a accès à tout. +Une fois qu'il est authentifié, il faut donc vérifier ses permissions. + +Pour cela, nous utilisons une surcouche +par-dessus `django-ninja`, le système de permissions de django +et notre propre système. +Cette dernière est documentée [ici](../perms.md). + +### Limites des clefs d'API + +Le système des clefs d'API est apparu très tard dans l'histoire du site +(en P25, 10 ans après le début du développement). +Il s'agit ni plus ni moins qu'un système d'authentification parallèle fait maison, +devant interagir avec un système de permissions ayant connu lui-même +une histoire assez chaotique. + +Assez logiquement, on ne peut pas tout faire : +il n'est pas possible que toutes les routes acceptent +l'authentification par clef d'API. + +Cette impossibilité provient majoritairement d'une incompatibilité +entre cette méthode d'authentification et le système de permissions +(qui n'a pas été prévu pour l'implémentation d'un client d'API). +Les principaux points de friction sont : + +- `CanView` et `CanEdit`, qui se basent `User.can_view` et `User.can_edit`, + qui peuvent eux-mêmes se baser sur les méthodes `can_be_viewed_by` + et `can_be_edited_by` des différents modèles. + Or, ces dernières testent spécifiquement la relation entre l'objet et un `User`. + Ce comportement est possiblement changeable, mais au prix d'un certain travail + et au risque de transformer encore plus notre système de permissions + en usine à gaz. +- `IsSubscriber` et `OldSubscriber`, qui vérifient qu'un utilisateur est ou + a été cotisant. + Or, une clef d'API est liée à un client d'API, pas à un utilisateur. + Par définition, un client d'API ne peut pas être cotisant. +- `IsLoggedInCounter`, qui utilise encore un autre système + d'authentification maison et qui n'est pas fait pour être utilisé en dehors du site. + +## Créer un client et une clef d'API + +Le site n'a actuellement pas d'interface permettant à ses utilisateurs +de créer une application et des clefs d'API. + +C'est volontaire : tant que le système ne sera pas suffisamment mature, +toute attribution de clef d'API doit passer par le pôle info. + +Cette opération se fait au travers de l'interface admin. + +Pour commencer, créez un client d'API, en renseignant son nom, +son propriétaire (l'utilisateur qui vous a demandé de le créer) +et les groupes qui lui sont attribués. +Ces groupes sont les mêmes que ceux qui sont attribués aux utilisateurs, +ce qui permet de réutiliser une partie du système d'authentification. + +!!!warning + + N'attribuez pas les groupes "anciens cotisants" et "cotisants" + aux clients d'API. + Un client d'API géré comme un cotisant, ça n'a aucun sens. + + Evitez également de donner à des clients d'API des droits + autres que ceux de lecture sur le site. + + Et surtout, n'attribuez jamais le group Root à un client d'API. + +Une fois le client d'API créé, créez-lui une clef d'API. +Renseignez uniquement son nom et le client d'API auquel elle est lié. +La valeur de cette clef d'API est automatiquement générée +et affichée en haut de la page une fois la création complétée. + +Notez bien la valeur de la clef d'API et transmettez-la à la personne +qui en a besoin. +Dites-lui bien de garder cette clef en lieu sûr ! +Si la clef est perdue, il n'y a pas moyen de la récupérer, +vous devrez en recréer une. + + diff --git a/mkdocs.yml b/mkdocs.yml index 7ad3da91..992cc38e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,7 +66,9 @@ nav: - Gestion des permissions: tutorial/perms.md - Gestion des groupes: tutorial/groups.md - Les fragments: tutorial/fragments.md - - Connexion à l'API: tutorial/connect-api.md + - API: + - Développement: tutorial/api/dev.md + - Connexion à l'API: tutorial/api/connect.md - Etransactions: tutorial/etransaction.md - How-to: - L'ORM de Django: howto/querysets.md