Bonjour,
J’ai récemment eu une nouvelle dispute avec mon CTO (la deuxième en 1 mois1).
Je travaille sur un projet PHP (Laravel) et devais mettre en place un QR Code redirigeant vers une page contenant un formulaire de vérification d’identité (sans mot de passe), qui, si le traitement se fait avec succès, renvoie au front un Bearer Token pour pouvoir requêter des fonctionnalités protégées. (Nous devons nous-mêmes gérer cela en mode "custom" et non avec une librairie comme Passport par exemple, pour des raisons techniques que je saute car hors-sujet).
J’ai donc créé un Middleware (abrév. "MW") en amont des routes de ces fonctionnalités protégées, chargé de laisser passer ou de refuser toute requête de ces fonctionnalités protégées en fonction de si le token envoyé est correct ou pas. Ce MW vérifie donc que le token envoyé par le front existe bien en base ET qu’il n’est pas expiré ET qu’il appartient bien à l’utilisateur envoyé par l’appli (cette dernière envoie le type d’utilisateur ainsi que son UUID, ces deux données étant retournées par le traitement du formulaire de vérification d’identité, en plus du bearer token).
Le code ressemble donc à cela :
$locataire = Locataire::query()->where('uuid', $request->uuid')->first();
$token = Token::query()->where('value', $request->value)->where('expi', '>', 'NOW()')->where('locataire_id', $locataire->id)->first();
if (!$token) {
throw new AuthenticationException('Invalid token.');
}
Le problème selon le CTO et son "argumentation"
Selon lui, je cite, "ça n’a aucun sens" de mettre ça dans un middleware. (Il s’est une nouvelle fois montré très direct voire extrémiste dans ses propos2).
-
Pour lui, ce n’est pas une vérification de sécurité qui a sa place dans le middleware car "c’est du requêtage métier" puisque je récupère un locataire (donc une donnée métier). Or d’après lui, aucun MW du projet ne récupère d’objets métier (ce qui est faux puisqu’il en a fait un lui-même).
-
Dans le même ordre d’idée, selon lui, il faut que les MW fassent des trucs sans requêter les données métier de la DB, mais uniquement des données "core". (Idem nous avons pourtant des contre-exemples dans notre projet, notamment écrits par lui-même).
-
Selon lui, il faudrait que je déplace les notions de locataire et de vérification "token<->locataire" dans chaque contrôleur, dans leur fonction
authorize
. Pourtant, la fonctionauthorize
que nous déclarons dans les contrôleurs sert juste à établir des autorisations (= permissions à base de rôle du genre "tel utilisateur a le rôle de Lecteur de telle ou telle donnée donc il peut y accéder en lecture"). Moi ce que j’ai fait, ça relève de l’authentification : on vérifie que le token est bien associé au user envoyé par l’appli. C’est donc en amont de la notion d’autorisation. -
Autre argument qu’il avance :
S’il y avait un bug concernant l’appel des routes protégées à cause d’un souci d’exploitation du token ou quoi, où regarderaient les collègues, sachant que la logique "permissive" est historiquement écrite dans la fonction
authorize
des contrôleurs ?
Réponse que je lui ai donnée : bah vu qu’il s’agit du système de QR Code, on sait bien qu’il y a un MW de sécurité, et de plus on aurait probablement une erreur "Invalid token" ou équivalente. Donc la probabilité est grande pour qu’il regarde dans ce MW et non dans la fonction authorize
des middlewares (sachant encore une fois que c’est de l’authentification et non de l’autorisation, de mon point de vue). Puis bon normalement on est censé regarder les back traces des logs d’erreur, c’est le point de départ de tout débogage…
- Dernier argument qu’il avance : là on vérifie le token<->locataire mais quid de quand on généralisera ce système à plein de types d’utilisateurs différents (propriétaires, agences, jsp quoi) ?
Ma réponse : alors déjà je ne savais pas du tout qu’on allait généraliser (ce à quoi il a répondu "il faut TOUJOURS penser à tout et TOUT anticiper", ce qui est en partie contraire au KISS mais passons). Mais de toute façon, comme l’appli envoie le type dans la requête, il suffirait de faire un truc du genre : $user = $request->type::query()->where('uuid', $request->uuid')->first();
au lieu de l’actuel : $locataire = Locataire::query()->where('type', $request->type)->where('uuid', $request->uuid')->first();
. Eventuellement, en cas de type plus "complexe", il y aurait un switch
(ou match
) ou des conditions, ce à quoi il a répondu "Non mais ton MW va faire 10k lignes de long".
- Dernier-Ultime argument : "Non mais arrête de toujours tout remettre en question, tu es clairement têtu, tu dois te conformer à ce que nous avons décidé, je me suis pas fait chier à partir sur l’architecture du
authorize
pour qu’on fasse une exception ici, c’est tout" (au mot près à 90%). Je n’ai pourtant pas du tout l’habitude de le contredire, au contraire je me tais 90% du temps et je fais juste ce qu’il dit, 90% du temps je m’écrase.
Ma question
Cette vérif de sécurité "token <-> locataire/user" est-elle à faire dans le MW ou dans chaque contrôleur (méthode authorize
ou bien méthode asController
) ?
1 : la première "dispute" (enfin j’y ai mis rapidement un terme en m’écrasant au plus tôt) : il voulait récupérer les données d’un jour donné, donc il a écrit : Truc::query()->whereBetween('date', '2022-01-01 00:00:00', '2022-01-01 23:59:59')->get()
. Moi je lui ai dit que conceptuellement il manque la dernière seconde (la précision de stockage étant de l’ordre de la milliseconde), donc ma solution était : Truc::query()->where('date', '>=', '2022-01-01 00:00:00')->where('date', '<', '2022-01-02 00:00:00')->get()
. C’est légèrement moins lisible mais conceptuellement vrai alors que son truc est légèrement plus lisible mais faux. Il m’a répondu qu’en base, on n’a pas de millisecondes et donc qu’il ne changera "de toutes façons" (je cite) pas sa solution pour la mienne.
2 : il a en effet :
-
Tendance à me prendre de haut et à être condescendant, à surestimer son avis par rapport au mien. Quand il m’explique son point de vue, c’est toujours sous la forme de questions. Par exemple pour le point " 1 ", il n’a pas répondu "qu’en base, on n’a pas de millisecondes et donc qu’il ne changera pas sa solution pour la mienne". Mais à la place il a fait exprès pendant 20 minutes de me poser plein de questions en ce sens pour arriver à cette conclusion.
-
Il a aussi l’insulte facile puisqu’il y a de nombreux mois il m’avait traité d’hypocrite devant le stagiaire, et hier d’hypocrite devant l’équipe tech, et de têtu avant-hier.
- Hier il m’a traité d’hypocrite devant les techs parce que j’ai osé ouvrir ma bouche puisqu’il leur expliquait devant moi que j’avais commis une grosse bourde sur le MW en requêtant de la logique métier et que c’était une mauvaise direction technique qui ralentissait toute l’équipe etc etc. Bref il pourrissait mon dev et ma "mauvaise initiative" (je cite), sans leur expliquer précisément ce dont il s’agissait (donc l’authentification, à savoir vérifier que le token est bien associé au bon locataire, ce qui selon moi a toute sa place dans un MW et non dans les fonctions
authorize/asController
des contrôleurs). Quand j’ai essayé de me défendre devant les techs, il a essayé plusieurs fois de me faire taire en disant "on en a déjà parlé hier tous les deux donc stop" alors même que c’est lui qui a ramené le sujet sur la table.
- Hier il m’a traité d’hypocrite devant les techs parce que j’ai osé ouvrir ma bouche puisqu’il leur expliquait devant moi que j’avais commis une grosse bourde sur le MW en requêtant de la logique métier et que c’était une mauvaise direction technique qui ralentissait toute l’équipe etc etc. Bref il pourrissait mon dev et ma "mauvaise initiative" (je cite), sans leur expliquer précisément ce dont il s’agissait (donc l’authentification, à savoir vérifier que le token est bien associé au bon locataire, ce qui selon moi a toute sa place dans un MW et non dans les fonctions
Il a tendance à balayer d’un revers de mains mes arguments techniques, c’est assez fou, et à faire passer ses pseudo-arguments ("tu dois faire comme ça parce que j’ai décidé, arrête de tout contredire") pour des arguments. Quand il fait cela, il utilise des termes extrémistes du genre "ça n’a aucun sens".
Bref il ne supporte pas que je le contredise. Le pire c’est que je suis souvent là à défaire/refaire du dev car il oublie ce qu’il a dit quelques jours ou semaines auparavant (et il ne me croit pas quand je le lui dis) ! Je ne suis d’ailleurs pas le seul à avoir ce souci ! Bon à ce sujet moi aussi j’oublie parfois des trucs au niveau métier / implémentation technique par exemple, donc on est quittes
PS : j’ajouterais aussi qu’il disait aussi être "constamment contredit" par au moins trois développeurs qui ont quitté la boîte (soit eux-mêmes lors de leur période d’essai soit non-renouvellement de contrat pour le freelance). Je vous laisse libre interprétation de ce PS .