ZEP-17 : Elaboration de l'API des membres

a marqué ce sujet comme résolu.

Maintenant est-ce-que c'est vraiment un point de blocage majeur pour la spec' ?

Javier

Je régis au fur et a mesure de ma lecture.

Y'a beaucoup, beaucoup plus important dans la spec que ce point de détail (Oauth ? ETag ?)

Javier

J'ai déjà donné mes remarques sur le point OAuth, et il me semble que ça a été décidé qu'on est bon pour l'OAuth2.

pour l'ETag, il faut encore définir comment sauvegarder cette donnée.

Andr0

L'ETag n'a pas besoin d'être sauvegardé, car il est tout simplement calculé.

D'ailleurs il existe un paquet d'extensions à djangorestframework qu'on va certainement utiliser pour travailler sur ces aspects de cache et etag. C'est intégré presque naturellement dans la lib (cache, la pagination, etc.).

Tant mieux si vous avancez, bon courage.

Javier

Faut pas te fermer comme ça. Absolument rien n'est avancé. Je n'ai fais que quelques lignes en local pour voir comment ça serait sur le terrain et firm1 n'a fait que consulter la documentation d'implémentation. :)

L'ETag n'a pas besoin d'être sauvegardé, car il est tout simplement calculé.

Je n'en connais pas la mise en oeuvre mais il me semble que tu dois sauvegarder cette information sinon comment veux-tu que le cache opère ?

Pour finir, où en sommes-nous avec cette ZEP ? Est-ce que quelqu'un veut encore discuter de points figurant sur le premier message du sujet ou nous pouvons passer à l'état "validation" ?

Personnellement, pour cette question autour de l'ETag, je propose ce que nous avions décidé sur le précédent topic, à savoir : Pour l'instant on le re-calcul (comme mentionné dans l'une des versions de l'API (0.4 de tête)) et nous aviserons plus tard si nous rencontrons des problèmes (puisque d'après la documentation, cet ETag est géré par la librairie).

Juste, j'avais oublié ce point là. Si nous reprenons ta liste (que je trouve pas mal) :

  1. On ne renvoi que les textes au format markdown : dans ce cas précis, les clients de l'api de type html doivent se taper le parsing zmd -> html
  2. On ne renvoi que les textes au format html : on consomme beaucoup trop de reseau à chaque fois, et on a pas la source markdown.
  3. On renvoi les deux formats systématiquement : encore beaucoup trop de bande passante consommée inutilement.
  4. On renvoi le format html sans les balises htmls. Par exemple si la signature en html donne <p>Cliquez <a href="perdu.com">ici</a></p> on renverrait Cliquez ici : le problème là c'est que du coup les liens n'auront plus de sens
  5. On renvoi uniquement le format demandé (en précisant la nature du format dans les entêtes http) : perso, c'est la solution que je pense être la meilleure

Clairement, je suis du même avis que toi. Proposer la 5ème solution.

J'ai repris un peu de lecture par ici, et :

v0.5 : gestion du PUT

Routes

400 si un champ n'est pas formaté comme attendu (400x à définir : email invalide, …) s'inspirer de la gestion d'erreur déjà présente sur la page de profil. 401 si non authentifié. 403 si essai de modifier un profil autre que le sien. 404 si id du membre n'existe pas. 405 si un PUT est fait sur /membres/ puisqu'on ne peut pas modifier la liste des membres.

ZEP-17

la partie que j'ai graissé signifie que le staff ne pourra pas modifier un autre profil que le sien via l'API, et pourtant il le devrait.

Autre chose aussi, la ZEP ne préciser pas les champs obligatoires à renseigner via /v0.5/membres/{id}. Est-ce qu'on peut modifier son email ainsi ? son mot de passe ? son pseudo ? etc…

la partie que j'ai graissé signifie que le staff ne pourra pas modifier un autre profil que le sien via l'API, et pourtant il le devrait.

firm1

C'est juste, je modifie ça ce midi.

Autre chose aussi, la ZEP ne préciser pas les champs obligatoires à renseigner via /v0.5/membres/{id}. Est-ce qu'on peut modifier son email ainsi ? son mot de passe ? son pseudo ? etc…

firm1

J'aurais tendance à dire que tout ce qui est modifiable par le site doit pouvoir l'être par l'API. Je peux le renseigner aussi si tu veux.

J'aurais tendance à dire que tout ce qui est modifiable par le site doit pouvoir l'être par l'API. Je peux le renseigner aussi si tu veux.

Andr0

Je pense aussi. Mais dans ce cas il faut soit préciser ce qui est modifiable sur le site dans la ZEP ou renvoyer vers un lien qui l'explique.

Est-ce-que vous êtes sûrs de vouloir que les actions d'administration soient disponibles sur la même API que les méthodes publiques ?

Y'a deux approches concernant la gestion des rôles/droits d'accès aux ressources.

  • Faire plusieurs APIs (plusieurs paths REST) reposant sur un même socle côté serveur

  • Tout coller dans la même API et router en fonction du token passé dans les headers HTTP

Les deux se discutent et ça dépend de ce qu'on offre dans l'API d'administration.

J'ai plutôt tendance à préférer la première méthode, moins "masquée". C'est plus facile de repérer les appels à l'API d'administration, voire faire la distinction dans des logs, dans le code serveur, d'ouvrir / fermer certaines routes directement dans un fichier de routes "lisible" plutôt quand dans un if/else ou un le code d'un intercepteur pas très lisible, etc.

Ça permet aussi assez souvent de mapper de façon plus naturelle sur le code serveur, plutôt que de router en fonction du token passé dans les headers HTTP. Aussi, la seconde approche "casse" une fonctionnalité simple mais parfois nécessaire : un membre du staff souhaite "voir" des ressources comme un simple membre.

Y'a pas de vérité absolue pour traiter ce genre de problèmes, adoptez la solution qui vous convient le mieux.

+0 -0

Moi j'aurai plus tendance à préférer la deuxième approche que tu décris Javier, car elle permet de rester flexible. Aujourd'hui on a un groupe Staff avec des permissions associées. Si demain, nous ou un autre site utilisant le logiciel ZdS décide de créer un groupe dédié à la validation de contenu, l'api mappera les permissions aux éléments autorisés.

Premièrement, les deux permettent de rester flexible, c'est justement l'intérêt. Dans les deux cas "L'API mappera" (c'est plutôt vague comme formule), dans un cas à travers le routage des paths (fichier routes j'imagine dans Django si ça marche comme RoR et consorts), dans l'autre cas à travers un espèce d'intercepteur ou de filtre (suivant de quel framework on parle) lisant l'en-tête HTTP de la requête et routant sur la bonne classe côté serveur.

J'aurais tendance à penser tout l'inverse de toi.

Le jour où tu as N groupes avec plein de permissions différentes :

  • en approche 2 tu te retrouves avec du code spaghetti côté serveur (si on parle Seam : généralement un espèce de router en intercepteur de tes requêtes API, si on parle RoR/Spring ou assimilés : un filtre assez moche pour les requêtes API). Et ce n'est pas naturel pour le client que d'avoir des résultats différents en fonction du contexte d'appel. C'est pénible aussi pour la recherche de bugs (les bugs à la con où t'as pas de stacktrace, du style "ça renvoie rien") : "ça buggait ?" "ah je reproduis pas" "ah oui mais t'étais loggé en tant que quoi ?". Si t'as le path dans les logs, t'es sauvé. Une requête == un scénario à rejouer.

  • en approche 1 tu as N ensembles de routes mappées assez proprement sur des méthodes de classes côté serveur. Tu as plus de routes. Donc un fichier routes plus "long" mais plus lisible. Typiquement ce genre de trucs : "/api/1/public/membres":[Controller:"MembresInPublicContext", Action:"List"] Si jamais les deux routes mappent sur la même "ressource API" alors le fichier de routage est simple.

En gros l'approche 1 est déclarative, et honnêtement, plus tu as de groupes (voire de clients utilisant l'API dans des contextes assez différents) plus l'approche 1 donne des résultats intéressants.

En outre, on peut citer la documentation, dans laquelle c'est beaucoup plus simple d'associer un ensemble de paths/fonctionnalités disponibles section par section. Eg. Section 1 : API publique. 1.1 Ressources disponibles. 1.2 Méthodes autorisées, ... plutôt que de décrire "Si vous êtes loggés en tant que staff alors vous avez en plus accès à".

Idem pour les logs que j'ai cités plus haut.

Après si ça se joue sur un pauvre PUTperdu tout seul alors dans ce cas pas la peine de reconstruire toute une série de paths. Mais au contraire de ce que tu énonces dans ton message, je pense que plus tu as de clients et de contextes d'appel différents, mieux vaut s'orienter vers une approche déclarative "chemin par chemin" plutôt que "tout le monde le même chemin, on routera en fonction du token"

+0 -0

En te lisant, je pense que j'ai mal saisi la distinction entre tes deux approches. Est-ce possible d'avoir un exemple de ce que tu décris dans notre cas présent ?

Pour préciser ma vision, on aurait bien l'url de PUT /v0.5/membres/{id} via laquelle on peut mettre à jour un profil d'utilisateur. Lorsqu'un utilisateur (de l'API) est loggué, son token est mappé avec certaines permissions (update_other_profile, can_view_ip_adress, etc.). Si l'utilisateur essaye de faire un PUT sur un profil différent du sien, alors qu'il n'a pas la permission update_other_profile, il tombe sur une 403, sinon la mise à jour est faite.

Voila comment je le vois en pratique. Donc au final 1 seul chemin API, mais qui renvoi une 403 ou une 200 en fonction du profil.

Oui oui, j'ai bien compris ce que tu proposes. J'ai mis en place cette approche plusieurs fois.

Ce que je veux dire c'est que si c'est juste pour une fonctionnalité perdue comme ça, alors ton approche suffit. (et je l'ai souvent fait).

Par contre si effectivement on considère que les "staff" peuvent faire plein de trucs (dont modifier des profils), il vaut mieux saucissonner l'API en plusieurs "contextes" :

  • public (celle dont on écrit la ZEP)

  • administrateur

  • … (validateur ?)

Chaque API ayant un prefixe dans le chemin (ou rien dans le cas de l'API publique).

Tu pars du principe qu'un membre du staff modifie un membre de la même façon que ce dernier le ferait. Ce n'est pas nécessairement vrai. Par exemple, on pourrait imaginer qu'il peut modifier tous les champs à l'exception de l'adresse e-mail, tandis que le membre, lui, peut modifier son email. Et là, ton système de permissions ne suffit plus, il faut vraiment faire un if / switch etc. sur le rôle de l'utilisateur. Ce n'est PLUS la même fonctionnalité exactement.

Mon "avertissement" (si je puis dire) c'est que dans ce genre de cas (API administrateurs en fait hein, ça ressemble à ça) écrire une nouvelle API, sur un PATH différent permet de s'en sortir de façon plus "lisible". Rien n'interdit de mettre en commun du code par ailleurs côté serveur mais simplement, tu as découpé tes fonctionnalités :

  • le public peut faire XXX

  • les administrateurs peuvent se connecter sur une API spécifique (sur-ensemble de l'API précédente) pour effectuer des actions spécifiques. Et là ton PUT est facile à tracer dans des logs par exemple.

Est-ce-que c'est plus clair ?

+0 -0

Est-ce-que c'est plus clair ?

Javier

Oui, c'est plus clair, je vois mieux. :)

Mais le point justement qui m'embête c'est celui-ci :

les administrateurs peuvent se connecter sur une API spécifique pour effectuer des actions spécifiques.

ça marche bien parce qu'aujourd'hui on a 2 niveaux d'autorisations (membre et staff, quoique super utilisateur aussi).

Si demain, on rajoute un groupe de validateurs, ou si on décide que les membres du groupe dev ont la permission d’accéder à certaines infos sur le site pour des raison spécifique a debug, il faudra (d'après ton méthode) créer autant de path API que l'on rajoute de groupe. Et surtout ça oblige un dev (certes minime) en plus et une nouvelle mise ne prod de l'API pour prendre en compte les nouveaux ajouts.

Mmh pas tout à fait.

En fait c'est bien tout le problème du compromis à trouver.

Jusqu'à présent j'ai fait la chose suivante :

  • Si les données affichées (et uniquement les données affichées) changent en fonction du contexte alors je reste sur le même PATH, et je route les Marshaller (Serializer dans notre contexte si j'ai bien suivi) en fonction de l'utilisateur qui est loggé

  • Si les méthodes changent (méthodes supplémentaires notamment, ou alors méthode se comportant différemment suivant le contexte) et qu'il y en a quelques unes déjà identifiées (genre pas UNE SEULE comme ici) je pars sur une API ad hoc (/guest , /admin, /member , /manager , … ce genre de paths) en essayant de justifier leur présence

La plupart du temps (mais vraiment c'est un "ressenti" j'ai pas de chiffres à l'appui) la seconde approche finit par arriver un jour ou l'autre. Parce qu'une API "superuser" c'est hyper pratique, et tu peux avoir envie de retracer un token volé, ou ce genre de conneries. Ca simplifie les tests d'intégration aussi, tu testes path par path plutôt que générer un token, puis un autre token, puis faire deux fois la même requête, y'en a une des deux qui plantent mais tu sais pas laquelle, …

Parfois (quasiment tout le temps) le process de login est différent entre "public" et "superuser" (certificat client par exemple, ou restriction hostname, etc.). Bref, "au fil du temps" on regrette assez peu souvent le choix numéro 2. Mais j'ai pas vraiment d'autres d'arguments que "c'est comme ça que je le sens" c'est assez faible. Du coup, vraiment, faîtes comme vous le sentez, mais au moins vous avez les cartes en main pour faire le bon choix je pense.

+0 -0

Désolé pour ma longue absence, j'avoue que la ZEP commence à devenir bien longue en discussion. Je me suis donc mis à jour dans le débat et j'avoue préférer la solution de Javier. Avoir un routage naturel plutôt que des conditions partout dans chaque path de l'API, il y a juste pas photo pour moi.

Bon de toute façon On arrivera jamais a une spec complète qui contentera tout le monde dans les moindres détails.

Kje

C'est le but des ZEP il me semble, sinon on reporte juste le débat ailleurs (sur GitHub). ça ne me déplait pas forcément mais ça veut dire qu'on part en développement avec des specs incomplètes.

Je me suis donc mis à jour dans le débat et j'avoue préférer la solution de Javier.

Andr0

ça ne me dérangerai pas des masses, tant qu'on est bien conscient des tenant et aboutissants (mon petit doigt me dit qu'on va le regretter plus tard, mais ça c'est mon petit doigt :) ). Aussi ça signifie que cette ZEP en l'état ne fait pas de gestion des permissions autre que "invité" et "membre". Pour les staff (et peut-être d'autres nouveaux profils qu'on créera un jour) à voir.

En effet, j'émet un doute à la solution de Javier concernant les droits d'un staff. Comment avoir un routage naturel sachant qu'un membre pourra éditer les informations X, Y et le staff les informations X, Y, Z (et dans le futur un autre groupe qui pourra éditer simplement les informations X et Z) ?

@Kje : Je trouve aussi mais comme le dit firm1, c'est le but des ZEP et l'API est suffisamment importante pour bien y réfléchir. Toutes les contraintes relevées aujourd'hui seront des contraintes à ne pas relever demain pour cette API mais pour d'autres aussi potentiellement.

C'est le but des ZEP il me semble, sinon on reporte juste le débat ailleurs (sur GitHub). ça ne me déplait pas forcément mais ça veut dire qu'on part en développement avec des specs incomplètes.

Cette ZEP est déjà largement la plus complète et détaillé de toutes. Je veux bien croire que tout n'est pas parfait mais de toute façon il risque d'avoir d'autres soucis qui ne vont apparaitre que pendant le dev.

L'API est clairement une killer-feature, je préfère voir cette fonctionnalité avancé de manière imparfaite que la transformer en vaporware. Il y aura probablement des defauts, soit. Au moins avec le découpage ces defauts seront principalement limités à l'API sur les membres.

Apres ce n'est pas moi qui vais la developper mais personnellement je pense qu'il faut penser à avancer.

Mon petit doigt me dit l'inverse ^^

Trêve de plaisanteries, ça fait partie des décisions très difficiles à prendre a priori. Surtout tant qu'on a une vue partielle de l'API et de son usage.

J'ai cité vaguement plein de cas qui pourraient poser des soucis, sans vraiment savoir s'ils peuvent se produire.

La question déterminante selon moi c'est "Est-ce-qu'un membre du staff a accès à plus de fonctions sur l'API qu'un simple membre authentifié" "Est-ce-que certaines fonctions (primitives) de l'API sont différentes en fonction de la nature de la personne authentifiée". Troisième question déterminante : est-ce-qu'en tant que staff, il se peut que je veuille exécuter des actions ou consulter des données en tant que simple membre ? (c'est rare, mais ça arrive, et c'est un choix crucial)

Si la réponse à ces questions est oui, alors je pense qu'il est indispensable d'avoir une API à part pour éviter :

  • un plat de spaghetti côté serveur

  • des cas d'utilisation / rapports de bugs assez complexes à analyser "j'étais loggé en tant que XXX, j'ai fait tel appel API"

C'est presque dommage qu'on soit si peu à débattre de ce problème, parce qu'il relève (malheureusement) de l'intuition et de l'anticipation de futures utilisations de l'API. Et je serais ravi de voir quelqu'un débarquer en disant "la solution 1 (ou 2) ne fonctionnera pas parce que […]".

Cela dit c'est vrai que ce n'est pas bloquant, mais c'est un vrai choix de design. N'hésitez pas à donner vos avis, c'est assez important.

@Andr0 : je ne comprends pas ton doute, est-ce-que tu peux détailler ?

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte