Comment stocker les configurations d'accès aux environnements de façon sécurisée

icon Tags de l'article : , , ,

Septembre 13, 2019
Bonjour à tous et toutes,

Aujourd'hui on va parler fichiers de configuration, variables d'environnement, stockage, sécurité et contrôleur de source.

Quand je parle de variables d'environnement, je parle par exemple des identifiants pour se connecter à la BDD, des clefs analytics, ou encore d'autres identifiants critiques.
Il s'agit en général de variables que les développeurs doivent posséder pour les environnements intermédiaires (développement, qualification, préproduction) mais pas pour certains environnements (production, analytics, BI, etc.).
Ces variables sont, en général, stockées dans un gros fichier de configuration qui sera utilisé par le logiciel pour ses différentes connexions.

Du coup la question qui revient à CHAQUE fois : comment stocker de façon sécurisée quelque part ces variables, afin que les développeurs puissent y avoir accès facilement, sans pour autant les rendre récupérables par n'importe qui de passage dans la société ?
Exit donc la feuille collée au mur avec les mots de passe.

Dans ces 5 dernières années, je suis passé par plusieurs solutions :
  • Le service production/exploitation qui s'occupe lui-même de renseigner les clefs de production dans le fichier de configuration. Avantage : sécurité, inconvénient : mise à jour des clefs de configuration ultra complexe. (on doit spécifier à chaque fois les champs ajoutés, les champs modifiés, les champs supprimés, etc. et les fails arrivent souvent)
  • Les développeurs ont toutes les clefs de tous les environnements, et ils les remplacent eux-même pour faire des builds en production. Avantage : simple, inconvénient : sécurité très faible.
  • Les fichiers de configuration sont archivés avec la solution. Avantage : ultra simple, inconvénient : aucune sécurité, n'importe qui ayant accès aux sources a accès à tous les environnements.
  • Les clefs sont enregistrés dans un keepass archivé avec la solution, et seules les personnes concernées connaissent son mot de passe. Avantage : sécurité, inconvénient : très chiant à modifier et saisie constante du mot de passe.

Afin de simplifier les choses sur le projet sur lequel je suis, j'ai proposé la solution suivante :
1) Nous avons un fichier config.zip archivé et chiffré avec mot de passe à la racine du projet
2) Nous avons développé un script nommé init.sh, qui a pour mission d'initialiser le projet.
3) Lorsqu'on lance le script init.sh, il va se charger :
  • De vérifier les versions des packages globaux installés (par exemple les versions de NPM, d'Ionic, de Cordova, ...)
  • De demander le mot de passe du fichier config.zip
  • De dézipper le fichier config.zip grâce au mot de passe renseigné, pour récupérer le fichier de configuration .config
  • De supprimer tous les dossiers générés (nodes_modules, www, plugins, etc.)
  • De lancer les commandes nécessaires à l'initialisation du projet (npm i par exemple)
4) Nous avons ajouté une commande "zip:config" qui sert à chiffrer notre fichier de configuration (en saisissant à nouveau le mot de passe)
5) Nous avons ajouté dans le gitignore notre fichier de configuration.

Ainsi, nous pouvons archiver le fichier config.zip qui n'est dézippable qu'avec le mot de passe.
Dès qu'une personne doit mettre à jour le fichier de configuration, elle fait un "npm run zip:config", tape le mot de passe, et archive ensuite le nouveau fichier config.zip.
Dès qu'une personne change de branche ou merge la branche principale dans sa branche, elle a juste à faire un "npm run init", puis de saisir le mot de passe.

Cette solution nous permet de garder un process très simple sans pour autant sacrifier la sécurité du projet !

Voici le fichier init.sh que nous utilisons :

#!/usr/bin/env bash
expectedIonicVersion="5.2."
expectedCordovaVersion="9.0."

echo "Config file password (ask your colleagues):"
read -s password

ionicVersion="$(ionic -v)"

if [[ "$ionicVersion" != "$expectedIonicVersion"* ]]; then
    echo "Wrong Ionic version, you need to install the $expectedIonicVersion"
    exit;
else
    echo Ionic version OK"
fi

cordovaVersion="$(cordova -v | cut -c-5)"

if [[ "$cordovaVersion" != "$expectedCordovaVersion"* ]]; then
    echo "Wrong Cordova version, you need to install the $expectedCordovaVersion"
    exit;
else
    echo "Cordova version OK"
fi

unzip -o -P "$password" config.zip > /dev/null
if [[ $? == 0 ]]; then
  echo "Successfully unzipped the .env file"
else
  echo "Wrong Password for env file"
  exit;
fi

echo "Now removing generated folders..."

rm -rf ./node_modules/
echo "nodes_modules folder removed"

rm -rf ./plugins/
echo "plugins folder removed"

rm -rf ./platforms/
echo "platforms folder removed"

rm -rf ./www/
echo "www folder removed"

npm i

Enfin, voici les commandes qu'on peut trouver dans notre package.json :
{
  "scripts": {
    "zip:config": "zip -o -re config.zip .config",
    "init": "./build-scripts/init.sh"
  }
}

A noter :
  • zip -o -re target origin permet de zipper un fichier de façon chiffrée en demandant un mot de passe
  • read -s password dans le fichier .sh permet de lire un texte saisi sans qu'il s'affiche à l'écran
  • unzip -o -P "$password" config.zip > /dev/null permet de décompresser et déchiffrer le fichier config.zip à l'aide du mot de passe récupéré préalablement
  • Si vous voulez gérer plusieurs fichiers de configuration, par exemple pour l'environnement dev & l'environnement de production, vous pouvez avoir un fichier dev.config.zip et prod.config.zip, et deux scripts d'initialisation, chaque zip ayant son propre mot de passe. Ainsi seules les personnes habilitées pourront dézipper le fichier prod.config.zip :)

En espérant que ça vous soit utile, bon dev à tous et toutes !

Merge request : la checklist

icon Tags de l'article : , ,

Septembre 10, 2019
Hello,

Comme vous le savez peut-être, en plus d'être développeur, je suis le Scrum Master de mon équipe.

Afin d'améliorer la qualité et la stabilité de notre branche de dev principale, j'ai rédigé une petite checklist à utiliser par les développeurs lors des merge requests.

Pourquoi ?
  • Les listes de choses à faire sont souvent oubliées par l'équipe au moment des merge requests. Parfois partiellement (ah mince je n'ai pas testé sur iPhone), et parfois complètement (ah mince, comme c'était un petit dev je pensais que ça n'avait pas d'intérêt de tester).
  • En effet, une checklist a un impact psychologique bien plus puissant qu'une simple liste de choses à faire. Car en cochant, on indique qu'on a, personnellement, bien réalisé la tâche indiquée.
  • Dans l'idéal cette checklist serait à remplacer par un process CI automatisé. Ou en tout cas les étapes automatisables (build, tests, etc.).
  • La checklist peut aussi faire office de code review cheat sheet.
  • La checklist permet d'avoir un historique des merges, et d'en tirer des tendances.
  • Attention cependant : il ne faut pas que la checklist devienne une trop grosse contrainte. Il faut que le rapport besoin de qualité / contraintes soit optimal. Eviter donc d'avoir une liste de 20 étapes de tests avec 20 cases à cocher. Ca servirait juste à gaver votre équipe.
  • Egalement, il faut que l'équipe accepte et valide cette checklist. Vous devez éviter d'imposer ce type de process de force auprès d'une équipe qui n'en voit pas l'intérêt. A vous de sensibiliser votre équipe sur l'importance de la stabilité de vos branches de développement, du temps que ça fera gagner sur le développement, du nombre de bugs que vous éviterez, du fait que ça protégera des builds qui finissent à 21h, etc.

Voici donc cette petite checklist que j'espère avoir suffisamment teasé (à noter que plein d'éléments dans la partie optionnelle ne concernent qu'Angular/Ionic) :

------------------------------- MERGE REQUEST CHECKLIST -------------------------------

Date :
Dev name :
Reviewer name :
Branch :

MANDATORY
☐ It's a product story ? If yes, the PO validated the dev and gave his/her GO.
☐ The target branch (master/dev) was merged into the current branch
☐ No code / lint warnings or errors
☐ It compiles, starts, and was tested on all target devices/browsers
☐ It builds for all target environments (dev, qual, prod)
☐ The CI pipeline is green
☐ The wiki documentation was updated
☐ If the environment changed (global packages, .env file, …), the CI was updated and the team was warned by email

OPTIONAL (code reminders)
☐ PRODUCT : Every complicated choices are explained somewhere (documentation, specifications, comments, etc.)
☐ CODE QUALITY : No //todo or useless logs
☐ CODE QUALITY : No refactoring planned for later
☐ CODE QUALITY : The code is easy to read and understand
☐ COMPONENTS : OnPush on each component
☐ COMPONENTS : Smart/dumb pattern used
☐ PERFORMANCES : takeUntil($onDestroy) pattern applied on subscriptions
☐ PERFORMANCES : the app is fluid to use, no performances drop
☐ ROUTING : the pages routing is consistent and hierarchical
☐ SCSS : Each component is responsible of its look (not its parents)
☐ SCSS : No size calculations in typescript (only CSS for sizes)
☐ TESTS : tested with 360/375/414/768px of width
☐ TESTS : Unit tests written / updated
☐ ACCESSIBILITY : It works with bigger fonts

------------------------------- /MERGE REQUEST CHECKLIST -------------------------------

En espérant que ça vous soit utile,

Bon dev à tous et toutes !

Ionic et développement mobile : bonnes pratiques tests/ux/performances

icon Tags de l'article : , , , ,

Aout 28, 2019
Hello,

Quelques petites astuces / bonnes pratiques en vrac pour celles et ceux qui se mettent à Ionic ou au développement mobile en général.

Déjà, voici comment je teste ce que je développe pour mobile (Ionic) :
  • Je teste avec Firefox en général, car c'est le navigateur le plus rigoureux (si ça marche sur Firefox, y'a 99,9% de chances que tout fonctionne nickel sur Chrome et Safari)
  • Je teste avec 3 largeurs d'écran : 360, 375 et 415. (360 = Galaxy S, 375 = iPhone 6+, 415 = grands smartphones 6")
  • Je teste avec 320px de large. Ca ne doit pas être parfait, mais ça doit être utilisable (pour les utilisateurs de vieux smartphones)
  • Je teste avec 600/700px de large. L'idée est que ce soit exploitable sur tablette / PC.
  • Je teste ensuite sur Chrome et Safari, pour être sûr
  • Je teste, enfin, sur iOS puis Android

Avec ce process de test, j'ai rarement de mauvaises surprises. En développant via Chrome j'ai eu plusieurs soucis sur iPhone. En développant sur Safari j'ai eu des soucis sur Android. En commençant par Firefox, j'ai un truc ultra stable.

Et pourquoi ces tests de tailles ? Ben déjà ça apprend à maitriser le flex et les alignements.
Ensuite ça permet d'avoir un truc qui pourrait, sur un coup de tête, être sorti sur tablette ou en build web.
Enfin, du CSS bien conçu fonctionnera dans toutes ces configurations... et du CSS bien conçu diminue les risques que notre application soit inexploitable sur un vieux smartphone ou un smartphone pourri.

Quelques astuces CSS/UX :
  • Toujours les tailles en REM, on évite les pixels.
  • Eviter les éléments clickables de moins de 40px de haut
  • Un article ultra clair sur comment bien créer un formulaire pour application mobile
  • Chaque élément avec lequel on peut interagir doit le montrer via son style (encadré, souligné, bordure, ombre, ...)
  • Pensez à checker d'autres applications et regardez comment elles font les choses.
  • Jamais de listes déroulantes, on est sur mobile !
  • Privilégiez une UI simple réutilisant les composants tout faits d'Ionic (qui sont pensés pour iPhone et Android) plutôt que de faire du maison qui va déstabiliser et gêner ... tout le monde.
  • Pas de couches de popups ! Si vous avez besoin de faire ça, privilégiez plutôt un formulaire en étapes.
  • Pensez navigation de gauche à droite (j'avance vers la droite, je reviens en arrière vers la gauche). Surtout avec les derniers iphones qui permettent le retour via un scroll à gauche.
  • Si vous avez un doute sur une façon originale de faire quelque chose (par exemple le bouton retour en haut à droite), essayez de trouver une application utilisée en masse qui fait ça. Si vous n'en trouvez pas, c'est que c'est probablement une mauvaise idée.
  • Pensez mobile et utilisation du pouce ! On ne met pas d'onglets en haut. On ne met pas de burger menu en bas à gauche. On évite d'avoir 6+ onglets. Scroller est naturel sur mobile, mais évitez les scroll verticaux/horizontaux mélangés. etc.
  • Réfléchissez aux cas d'utilisations critiques de votre application. Designez votre application autour d'eux. Ajoutez ensuite les options pour les scénarios moins critiques, sans rendre l'application trop compliquée.
  • Si vous avez des doutes, checkez les composants ionic et autres projets de démo Ionic. Vous tomberez probablement sur un exemple pertinent pour votre besoin.
  • Attention au routing en Ionic 4. Pensez le comme un routing Web. Exemple : j'ai un onglet Settings, dedans j'ai une option mon profil. Le lien de la page profil doit être tabs/tab-settings/my-profile et pas /my-profile.
  • Privilégiez les modales quand vous êtes sur une action où le routing n'aurait aucun sens (exemple : la modification de votre nom/prénom dans l'application, vous ne feriez pas un routing pour ça, si la personne refresh, elle est censée retomber sur son profil, pas sur une "page" de modification de son profil)
  • Chaque composant doit gérer ce à quoi il ressemblera. Le parent ne doit gérer que le positionnement. Si le parent veut donner un rendu différent (exceptionnellement), on ajoute une classe sur le composant et dans ce composant, on exploite cette classe pour appliquer un CSS différent.
  • Pensez à tester votre application avec les polices augmentées sur un smartphone (par défaut c'est 16px, mais si la personne règle 20px dans son Android, votre application sera-t-elle toujours utilisable ?)

Enfin niveau perfs :
  • Evitez les trucs "réutilisables/bidouilles" pour "gagner du temps". Genre les composants transverses qui gèrent le ion-header et ion-content avec le titre et des booléens pour les boutons à afficher. Ca ruine les perfs en ionic 4.
  • ChangeDetection: Onpush sur TOUS vos composants. Si vous avez besoin de dynamisme : Observables et | Async.
  • Pendant vos tests n'hésitez pas à mettre un console log sur vos actions de services et composants en bas des grappes. Si vous voyez que les appels se font par 2, 5, 10 ou 100, vous avez un souci de perfs potentiel.
  • N'oubliez pas qu'Ionic c'est du HTML/JS embarqué et affiché via une webview. Evitez tout ce qui peut être facultatif mais qui boufferait les performances (par exemple charger une carte par défaut alors que 90% de vos utilisateurs se fichent de la carte)
  • On évite les calculs de tailles / rendu / affichage en typescript, il faut essayer de tout faire en CSS
  • takeUntil($onDestroy) pattern sur les souscriptions, on ne doit pas avoir d'accumulation de souscriptions à l'infini.
  • Si vous voulez gérer l'accessibilité (couleurs, tailles, etc.) ajoutez une classe CSS à la racine de votre application, et dans vos composants (ou dans un CSS spécifique) overridez l'affichage de base en CSS si la classe CSS est présente à la racine (évitez d'appeler un service dans chaque composant).
  • Le pattern Smart vs Dumb reste une référence : https://medium.com/@jtomaszewski/how-to-write-good-composable-and-pure-components-in-angular-2-1756945c0f5b
  • Même dans une page qui ne contient presque rien, on met un container qui contiendra un composant. (réutilisabilité / performances / sécurité)
  • Attention à ngrx. C'est génial, mais il ne faut pas en abuser. C'est là pour gérer des états au niveau de l'application. Evitez de tout faire avec, sinon non seulement vous allez compliquer à mort la relecture du code, mais en plus vous perdrez un temps monstre à développer (car à chaque refresh vous perdez tout votre state).

Enfin je finirais cette note sur le point le plus important pour moi : Pensez votre code pour qu'il soit ultra simple à relire, reprendre et débugguer par un autre développeur.
Si c'est trop compliqué, y'a un souci.
Si vous êtes obligé de commenter le code, y'a un souci.
Si vous découpez des trucs en des tas de morceaux interdépendants, y'a un souci.
Si un dev qui doit reprendre votre code est obligé de venir vous voir pour comprendre le code, y'a un souci.

Egalement, n'oubliez pas une chose : vous n'aurez pas deux chances de faire une bonne première impression.
Si vous sortez une application à la va vite, sans faire de tests utilisateurs, avec une mauvaise UX, votre application va accumuler les notes négatives et ce sera la merde direct.

Privilégiez : Fluidité = UX > UI > masse fonctionnalités.
Les applications mobiles doivent surtout séduire et être agréables à utiliser. Mais si vous accumulez trop de fonctionnalités et que votre application devient lente/inutilisable/moche, vous perdrez bien plus d'utilisateurs que si vous aviez laissé moins de features mais plus d'UX/fluidité. Largement.

Un dernier ultime rappel : une bonne UX c'est comme une blague. Si vous devez l'expliquer, c'est qu'elle n'est pas bonne.
Donc si votre application a besoin d'un tutoriel de 20 pages pour être utilisée... posez vous des questions.

Bon dev à toutes/tous !