IMA5 2018/2019 P06
Sommaire
Présentation générale
Conception et développement d'un système de gestion d'un réseau de capteur.
Description
Notre projet s'inscrit dans le cadre d'un projet de recherche à l'IRCICA sur un réseau de capteurs à large échelle (100 noeuds). Face à un réseau de cette ampleur, il est intéressant de développer une solution de maintenance à distance de ce réseau. Il sera bien plus aisé pour les chercheurs de tester leurs hypothèses en déployant rapidement leurs logicielles sur tout ou une partie des noeuds disponibles grâce à des fonctionnalités telles que la sélection d'une partie du réseau ou encore la mise à jour d'un ensemble de capteurs de même type / architecture.
Objectifs
L'objectif de notre travail consiste pour cela à développer simultanément le front-office et le back-office. La partie front sera réalisée en utilisant les technologies web classiques. La partie back-end sera constituée d'un ensemble de webservices en différents langages permettant la communication réseau et la gestion d'une base de données de noeuds et capteurs. Il faudra y développer des services classiques comme la gestion des utilisateurs et des services spécifiques (compilation du code source, téléchargement à distance du logiciel sur les noeuds). La partie front-end aura vocation à être utilisée par des utilisateurs externes au projet et devra donc être ergonomique et facile de compréhension. Elle devra également être fonctionnel et modifiable facilement.
Préparation du projet
Cahier des charges
Choix techniques : matériel et logiciel
Matériel
- Un Serveur dédié accueillant l'ensemble des services
- Un réseau de capteurs orchestré par des Raspberry Pi
Logiciel
- Ansible pour faciliter le déploiement des binaires sur les noeuds
- La plateforme Java EE pour la partie back-end couplé au framework Spring
- Une base de données PostgreSQL dans un premier temps pour assurer la persistance des données
- Une partie front-end moderne basée sur le framework Vue.js et sur un framework CSS tel que Bootsrap, Semantic UI ou Foundation pour ne citer que les plus connus
Liste des tâches à effectuer
- Comparaison et documentation sur les frameworks web de création d'interface (Angular, React, Vue ...)
- Documentation sur Spring et Java EE en général
- Configuration des routes du projet Spring (racine, connexion dans un premier temps)
- Création d'une interface utilisateur (front-end)
- Page de connexion pour commencer
- Implémentation d'un gestionnaire d'utilisateurs
- Création d'une base de données pour gérer les noeuds
- Implémentation du service d’envoi des fichiers de mise à jour
Calendrier prévisionnel
Réalisation du Projet
Semaine 1
Comme pour tout début de projet, nous avons passé une bonne partie de cette semaine à nous documenter et à évaluer des solutions potentielles. Le monde du front-end évoluant rapidement, il n'est pas toujours facile de s'y retrouver et de faire les choix adéquats. En l'occurrence, nous avons comparé les trois solutions qui sont Angular, React et Vue pour concevoir une interface web dynamique et moderne. Notre choix s'est finalement porté sur le framework Vue.js pour les raisons suivantes :
- Une prise en main rapide : sans même avoir suivi un cours spécifique à Vue.js, le choix pertinent des mot-clés aide la compréhension
- Des performances supérieures à ses concurrents sur bien des aspects
- Sa légèreté principalement causée par son fonctionnement par plugins
Concernant le framework CSS, nous avons fait simple et avons choisi Bootstrap, un standard du web depuis quelques années et qui reste d'actualité face à des alternatives en vogue telles que Foundation ou Semantic UI. Les classes proposées par ces feuilles de styles se démarquent quasi-exclusivement par la pâte graphique qu'elles apportent et non par les technologies employées qui sont identiques.
Avec ces outils en main, nous avons commencé par créer la page sur laquelle l’utilisateur arrivera s'il n'est pas déjà enregistré, la page de connexion. Rien de particulier sur cette page, un champ pour le login et un pour le mot de passe. Nous allons tout de même ajouter un peu de dynamisme en affichant un message d'erreur en d'échec de la connexion pour mauvais login ou mot de passe.
En parallèle, nous nous sommes intéressés à Spring et de manière plus générale à Java EE. Pour pratiquer et prendre ce vaste écosystème en main, nous avons créé quelques projets avec Spring comme une API REST basique ou encore un controller plus classique capable de servir des pages web statiques. Une fois cette étape de découverte effectuée, nous sommes rentrés dans le vif du sujet.
L'idée dans un premier temps et de concevoir le gestionnaire d'utilisateurs et de prouver son fonctionnement en affichant le login de la personne enregistrée sur une page d'accueil. Pour ce faire, nous avons parcouru quelques projets existants sur Internet afin d'avoir une idée de la structure globale des projets de plus grande taille et également pour identifier les outils que nous allons devoir utiliser. En cette fin de première semaine, nous avons un bon aperçu des bibliothèques et frameworks que nous allons utiliser. A titre d'exemple, nous pensions devoir implémenter nous même le système de session et donc gérer les cookies émis et reçus mais il s'est avéré que Spring dispose d'un framework nommé Spring Security qui fournit des fonctionnalités d’authentification et d'autorisation pour les applications web. Les objectifs de la semaine suivante seront de se renseigner sur ce framework, de pratiquer l'utilisation d'une base de données et finalement d'associer ces travaux pour stocker des utilisateurs dans la base et permettre l'accès à une page sécurisée au moyen d'un formulaire de connexion.
Semaine 2
Durant cette deuxième semaine nous avons décidé de reprendre les bases de Spring pour mieux comprendre le fonctionnement des dépendances utilisées. Notamment, nous nous sommes concentrés sur la communication avec une base de données en ProgreSQL via la Java Persistence API (ou JPA) couplée à l'interface Java Database Connectivity (JDBC) qui permet justement de communiquer avec une base de données, quelque soit le type (MySQL, PostgreSQL). Plus précisément, JPA permet de s'affranchir des créations de tables, de l'ajout et des modifications dans celles-ci. Il est également possible de faire des requêtes en JPQL, un langage proche de SQL mais orienté objet. Ce qu'il faut retenir de tout ça c'est que cette API va nous permettre de définir des classes vu comme des entités dans le projet et qui serviront à créer et remplir une table de données.
Grâce à ces recherches que nous avons effectuées, nous avons maintenant une meilleur idée du squelette du projet. Ainsi, pour un projet comme le nôtre il sera nécessaire d'avoir :
- Pour la base de données :
- une classe par table de données
- une classe qui permet à la JPA d’interagir avec la première classe décrite comme une table
- un fichier application.properties pour indiquer au projet la localisation de la BDD et les identifiants pour y accéder
- De manière général pour un projet Spring Boot :
- une classe pour gérer les routes de notre site Web (et donc les requêtes, qu'elles soient de type GET ou POST)
- le fichier pom.xml dans le cas de l'utilisation de Maven pour spécifier la liste des dépendances
- des fichiers de ressources comme des .html pour afficher les informations nécessaires à l'utilisateur
- une classe Application qui permet de lancer l'application depuis le projet (il sera ultérieurement utile d'utiliser une exportation en Jar ou War pour utiliser le projet de manière indépendante)
Pour la suite du projet il est également nécessaire d'intégrer le framework Spring Security qui permet justement de gérer l'accès à différentes pages. Il est possible d'utiliser ce framework sans base de données mais il serait compliquer dans ce cas d'assurer la persistance en mémoire des utilisateurs et des rôles qui leur sont associées. Il va donc être important de faire interagir le framework Spring Security avec la base de données.
Commit 1 et 2
Intitulé : "Projet Spring Boot avec gestion de base données simple et interface web pour l'ajout d'utilisateurs avec affichage des clients ajoutés"
Ce commit utilise les connaissances que nous avons acquis sur l'utilisation des bases de données. Ce projet utilise l'outil de gestion d'automatisation de production Maven et a les dépendances suivantes :
- Spring Boot
- JPA pour la gestion des bases de données externes, dans notre cas
- Thymeleaf, un moteur de template
Il permet d'accéder à une page d'accueil qui demande à un utilisateur (quelconque pour le moment mais par la suite il devra être Admin) d'ajouter des utilisateurs à la base de données. Lorsque le bouton est pressé, les informations (pseudo et mot de passe) sont passés dans le corps d'une requête POST. Coté Java, une méthode permet de gérer les requêtes POST de cette page. Pour le moment, nous créons un objet "Customer" en lui affectant le pseudo et le mot de passe rentré par l'utilisateur. Grâce ) la méthode save fournie par notre objet CustomerRepository, nous faisons persister ces informations en base de données. Enfin, nous effectuons une redirection sur la page du formulaire avec un paramètre "ok" pour avertir du bon enregistrement. Ainsi, quand le navigateur effectuera son GET, il ajoutera "?ok" à la destination permettant donc à la template Thymeleaf grâce à une condition d'afficher un message.
Pour le commit 2, nous avons souhaité vérifier si le pseudo entré dans le formulaire n'est pas déjà présent dans la base de données. Pour ce faire, nous avons au niveau du traitement du POST ajouté quelques lignes qui vont faire une requête à la BDD. Si le résultat est "null", tout se passe comme précédemment. Dans le cas contraire, la redirection est effectué sur le formulaire avec comme paramètre "?error". Une autre condition dans la vue permet de gérer ce cas d'erreur.
Commit 3
Intitulé : "Restructuration du site web avec une page d'accueil, de login et d'ajout d'utilisateur et ajout de Spring Security pour l'instant pas opérationnel"
Nous avons implanté la possibilité d'ajouter des utilisateurs avec le rôle Admin pour l'instant. En se rendant sur la page "registration", il est ainsi possible d'ajouter un utilisateur avec un mot de passe. Le serveur vérifie que le pseudo de l'utilisateur n'existe pas avant de l'ajouter. Un message permet d'indiquer à l'administrateur si l'utilisateur a bien été ajouté ou s'il existe déjà.
Les pages ont également été remaniées afin de parfaire l'expérience de la navigation. Spring Security a également été ajouté même si pour le moment il ne fonctionne pas. En effet, on indique au framework que la page login est celle à utiliser pour vérifier l'utilisateur. On indique également que seul un utilisateur avec le rôle Admin peut se connecter à la page d'ajout d'utilisateurs.
Enfin, grâce à des requêtes JPQL on précise quels sont les paramètres de la base de données à utiliser pour identifier l'utilisateur qui essaie de se connecter. L'enregistrement d'utilisateurs fonctionne mais pour l'instant l'entrée du rôle "ADMIN" est dupliquée ce qui sera à corriger.
Commit 4
Intitulé : "Front-end : page de login et page d'accueil"
La page de login n'a pas changé depuis la semaine, elle a juste été ajoutée au Git. Néanmoins, nous avons travaillé sur une ébauche de la page de d'accueil, la page qui s'affichera après que l’utilisateur se soit enregistré.
Dans l'ordre, on y voit un header en haut de la page qui permettra de se déconnecter ainsi qu'un autre lien qui affichera une page de configuration avec notamment la possibilité pour un Admin de d'ajouter un utilisateur et de lui affecter un rôle. Plus bas, il s'agit d'un tableau ou chaque ligne représente un nœud avec ses informations. Il ne s'agit ici que d'un exemple, tant pour le choix des informations qui seront affichées que pour le contenu actuel des lignes. Ce tableau utilise le plugin DataTables pour jQuery et offre des fonctionnalités prêtes à l'usage comme le tri par colonne, la recherche ou encore la sélection de ligne. A ce propose, pour le moment quand des lignes sont sélectionnées ou désélectionnées, un message s'affiche dans la console avec le contenu de la ligne, montrant ainsi qu'il est possible de les recueillir.
La seconde partie de cette page concerne l'upload d'un fichier. Il sera possible prochainement de déposer un fichier dans la zone ou de cliquer dessus pour choisir un fichier dans l'explorateur. La barre de progression montrera après l'appui sur un bouton le pourcentage déjà transféré. Ce bouton n'est pas encore présent mais sera très probablement situé au dessous de la zone d'upload.
L'objectif sera de communiquer le fichier au serveur non pas seul mais avec des métadonnées (liste des nœuds concernés), ce qui fait une potentielle difficulté pour cette étape.
Semaine 3
Commit 5
Intitulé : "Spring Security Opérationnel"
Dans l'état actuel du projet, tout le monde peut se créer un compte et se connecter au site avec et la page d'accueil affiche le pseudo de l'utilisateur. Lorsque l'utilisateur n'est pas connecté, toutes les requêtes le renvoient vers la page de login. D'un point de vu quantité de lignes, peu de changements majeurs ont été apportés par rapport au commit 3. En effet, les dysfonctionnements étaient plutôt causés par des éléments de configuration et plus précisément ces 2 points :
- La classe Customer doit pour être reconnu comme un utilisateur par Spring Security posséder un attribut "active" de type entier valant 0 ou 1.
- Ensuite, un changement a eu lieu dans la dernière version de Spring Security. Le mot de passe d'un utilisateur doit être par défaut haché avec Bcrypt dans la base de données. Apporter une telle sécurité nous paraissait futile durant cette phase d'apprentissage du framework mais finalement, il s'est avéré que de laisser des mots de passe en clair demande plus de configuration. Nous avons donc utilisé Bcrypt.
Pour terminer cette partie consacrée à la gestion des utilisateurs il va falloir travailler sur les points suivants :
- Fixer un utilisateur admin en BDD
- Autoriser la création de comptes seulement aux admins
- Créer un rôle utilisateur avec moins de privilèges (création de comptes)
Commit 6 et 7
Intitulé : "Upload d'un fichier"
On continue toujours parallèlement le développement de la partie front. Ce commit intègre la possibilité d'envoyer un fichier unique associé à une liste de nœuds via une requête POST. Après avoir pris connaissance des événements écoutables au niveau de l'action de drag & drop, nous avons fait en sorte d'afficher le nom du fichier dans le cadre une fois celui-ci déposé. Dans une optique d'interactivité, nous avons fait en sorte que la bordure de la zone s'épaississe quand un fichier survole la zone. Pour stocker les données qui seront envoyées, nous avons utilisé l'objet FormData qui permet de construire un ensemble de paires clé / valeur qui représenteront les données que nous enverrons. L'avantage étant que cet objet peut être envoyé via la méthode send() de l'objet XMLHttpRequest. Ainsi au moment de l’événement "drop", nous stockons le nom du fichier et l'objet "File" qui sera converti en binaire à l'envoie. Lorsque l'utilisateur déposent plus d'un fichier dans l'espace prévu, un pop-up d'avertissement apparaît pour indiquer qu'un seul fichier ne peut être transféré.
Nous avons ensuite ajouté la possibilité de cliquer sur cette zone pour choisir le fichier dans l'explorateur. Pour ce faire, nous avons rendu invisible via le CSS un input de type file. En JS, nous observons l’événement "click" sur la zone de dépôt puis nous simulons un clic sur le input invisible faisait apparaître la fenêtre de l'explorateur. Un autre événement "change" sur l'élément input permet de recueillir le fichier et ses infos.
Il a fallu également s'attaquer aux quelques cas d'usage particuliers comme lorsque que l'utilisateur dépose ou choisi un nouveau fichier. Pour ce cas, nous avons fait le choix de remplacer le fichier existant par le nouveau.
Avant de procéder à l'envoie, il faut récupérer la liste des nœuds sélectionnés et pour ce faire, nous avons créé un "Set" qui sera passé intégré à notre instance de FormData grâce la méthode append() de cet objet.
Enfin, pour procéder à l'envoie, il suffit de cliquer sur le bouton en bas de page. Si aucun nœud n'a été sélectionné ou si aucun fichier n'a été fourni, un pop-up apparaîtra pour stipuler l'erreur. En revanche si tout se passe bien, la requête POST peut être envoyée. Dernière petite fonctionnalité, XMLHttpRequest nous permet de récupérer le nombre d'octets total à transmettre ainsi que le nombre déjà transmis ce qui est parfait pour intégrer une barre de progression. Une fois le transfert opéré, un message apparaît et la page est rechargée.
Commit 8
Intitulé : "Optimisation de la communication avec la BDD"
Pour ce commit, nous nous sommes concentré uniquement sur la base de données et ses interactions avec Spring. Premièrement, grâce au fichier data.sql il est possible d'ajouter des entrées au lancement du serveur. Le problème étant qu'ensuite, lorsqu'on ajoute un utilisateur depuis le site internet, Spring commence par l'id '1' pour l'utilisateur alors qu'il est déjà utilisé vu que des utilisateurs ont été entrés dans la base de données au lancement. Pour corriger ce problème, il est nécessaire de modifier la manière dont Hibernate va générer les id. En fait, on précise dans notre classe "Customer" que la génération est en mode automatique alors qu'il faut en fait la paramétrer.
Malgré toutes les tentatives sur les différents type de génération, nous ne sommes pas parvenu à changer dynamiquement la valeur initial de l'id. Nous avons donc choisi d'utiliser une méthode setId() qui va être appelée en même temps que setPassword() et setPseudo(). Nous utilisons la méthode count() déjà implémenter dans JpaRepository (et donc CrudRepository). Celle-ci permet de connaître le nombre d'entrées dans la base de données à chaque fois que la requête "registration" est émise. L'assignation de l'id est donc dynamique.
L'erreur qui dupliquait le champ "ADMIN" a également été corrigée. Plutôt que de créer un nouvel utilisateur à chaque requête vers "registration", on recherche le rôle "ADMIN" dans la table rôle et assigne la ligne trouvé à l'utilisateur.
Enfin, nous avons ajouté un utilisateur par défaut : pseudo : user / password : user, et un administrateur par défaut : pseudo : admin / password : admin.
Remarque : il est obligatoire de stocker le password haché dans le fichier data.sql pour que Spring puisse l'utiliser (en effet, depuis Spring Boot 5.0, il est obligatoire d'encoder le mot de passe).
Commit 9
Intitulé : "Routage du site"
Nous avons implémenté la distinction entre un utilisateur classique et un administrateur. Les pages auxquelles peut accéder chaque rôle sont donc différentes : seul l'administrateur peut accéder à la page "registration"; les deux utilisateurs doivent se connecter pour accéder aux différentes pages du site. L'administrateur peut désormais préciser quel rôle aura l'utilisateur ajouté
Les templates sont également codés pour n'afficher que les informations dédiées au rôle qui est connecté au site internet.
Commit 10
Intitulé : "Intégration du front au projet"
Ce commit consiste en l'intégration de la partie front au projet. Pour que les templates puissent utiliser les ressources dont elles ont besoin (CSS et JS), nous avons créé un dossier static contenant deux autres dossiers JS et CSS pour lesquels nous avons autorisé l'accès à tout le monde dans la configuration de Spring Security. Pour la page de login, aucun changement majeur si ce n'est la présence d'un message d'erreur qui s'affiche lorsque que le compte n'existe pas. Son apparition ou non est gérée dans la template Thymeleaf grâce à une condition sur le paramètre "erreur" de l'URL.
La page d'accueil a elle aussi été mise à jour. Seul les liens "déconnexion" et "paramètres" de la barre de navigation ont été changés. Des tests sur mobile, nous ont révélé quelques soucis d'adaptation du contenu, il faudra veiller à régler ces petits défauts d'affichage. Il serait intéressant également de travailler sur la template "registratio" consacrée à l'ajout d'utilisateurs. Enfin, il faudra être capable au niveau du controller Spring de récupérer le fichier envoyé depuis l'interface via la requête POST.
Semaine 4
Lors de cette semaine nous nous sommes concentrés sur le fonctionnement des différents frameworks que nous utilisons. Plus particulièrement nous avons approfondi nos connaissance sur Spring Security pour mieux établir la gestion de la sécurité et de l'accès aux pages webs grâce à ce framework.
Semaine 5
Commit 11
Intitulé : "Sauvegarde des fichiers envoyés"
Ce commit ajoute l'enregistrement du fichier uploadé depuis l'interface web. Ce fichier se retrouve stocker dans un répertoire nommé <pseudo>_<timestamp> qui lui même est dans le dossier files. Ceci permet d'avoir un dossier unique pour chaque envoie de fichier par l'utilisateur tout en gardant une trace de son nom pour d'éventuels futurs besoin. Techniquement, la sauvegarde du fichier s'effectue en copiant par bloc de 1024 octets le binaire contenu dans la requête vers sa destination au moyen des flux Java. Pour la suite nous avons envisagé la création d'un programme annexe daemon dont le but serait de scruter la présence de nouveaux fichiers et de les transférer sur les nœuds concernés. Cette liste de nœuds sera dès le prochain commit incluse dans le dossier du fichier. On peut ensuite imaginer l'utilisation d'Ansible pour faciliter le déploiement du binaire sur les nœuds.
Annexes
Ferions-nous pas une partie un plus générale sur des sujets qui ont moins leur place dans les rapports hebdomadaires ?
Exemples :
- Détail de la classe de configuration de Spring Security
- Détail de l'architecture du projet
- Parler de Thymeleaf