* Période : Décembre 2023 - Janvier 2024
* Description :
- Projet universitaire tutoré, avec production technique.
- Conception d'une application sécurisée visant à gérer des réservations de chambres d'hôtel, adaptée aux supports mobile et conçue à partir de Python et KivyMD.
* Axes d'amélioration :
- Parvenir à construire un fichier APK à l'aide de l'outil Buildozer et ainsi effectivement porter l'application sur Android.
- Améliorer le design global et l'ergonomie de l'interface utilisateur.
- Mieux organiser et structurer les programmes produits et médias utilisés par l'application.
Ce projet consistait à concevoir par binôme l’intégralité d’une application de gestion de réservations de chambres d’hôtel destinée au support mobile, et spécifiquement Android.
Nous devions alors :
- Présenter un cahier des charges mentionnant les technologies à utiliser.
- Développer une interface logicielle back-end adaptée au fonctionnement du serveur visant à être utilisé par notre application.
- Développer une interface logicielle front-end accessible et ergonomique, adaptée aux besoins des utilisateurs.
- Proposer un système d’authentification des utilisateurs, permettant ainsi une traçabilité des réservations effectuées par chaque client et de proposer des interfaces utilisateur personnalisées.
- Sauvegarder les données échangées dans une base de données, au travers d’une API.
Au-delà du sujet initialement donné, notre binôme a souhaité concevoir une application de réservation de chambres d’hôtel couvrant une franchise d’hôtels plutôt qu’un unique hôtel. En effet, en reprenant les noms de ville donnés à chaque salle de notre établissement (IUT de Luminy), nous souhaitions davantage personnaliser notre travail tout en proposant quelque chose de plus poussé au niveau des traitements algorithmiques produits.
Tout au long du projet, la sécurité des traitements de données opérés par notre application fut le thème central de nos préoccupations.
Parmi les solutions effectivement implémentées dans le but de sécuriser notre application, on retrouve alors :
- Un accès à notre gestionnaire de bases de données restreint à la machine locale, afin de réduire au mieux la surface d’attaque de notre base de données.
- L’utilisation de prepared statements dans les requêtes SQL soumises à notre gestionnaire de base de données.
- Des utilisateurs dédiés aux requêtes de lecture et d’écriture dans notre base de données, disposant de droits d’accès mesurés et adaptés.
- L’implémentation du protocole de sécurité TLSv1.2 pour sécuriser les échanges de données entre une instance client et l’interface logicielle du serveur.
Dans ce cadre de projet étudiant, l’implémentation de TLS a été réalisée à l’aide de certificats auto-signés, ce qui est fortement déconseillé en situation de production. - L’enregistrement des condensats (hash) associés aux mots de passe des compte client, soumis à un salage dynamique.
Inclus également l’enregistrement des sels générés pour chaque compte client. - Au niveau du système de fichiers du serveur, une configuration adaptée des droits de lecture/écriture/exécution sur les scripts et fichiers utilisés par l’interface logicielle du serveur.
Parmi les principaux obstacles et questionnements rencontrés au cours de ce projet, nous avions :
- La gestion des instances client (front-end) de l’application :
Notre interface logicielle back-end devait pouvoir traiter des connexions concourantes, provenant de multiples instances client.
A cet effet, du multithreading (module threading de Python) fut implémenté dans l’interface logicielle du serveur afin de gérer et traiter de manière parallèle les connexions établies avec les instances client.
Cependant, cette solution s’arrêtait seulement à un semblant de traitement parallèle des instances client, car le Global Interpreter Lock (GIL) de Python empêche par sa nature de produire un véritable parallélisme dans les traitements réalisés avec des solutions de multithreading, surtout lorsque ces traitements sollicitent l’usage du processeur.
Et d’un autre côté, implémenter du multiprocessing (module multiprocessing de Python) pour contourner le GIL et tenter d’améliorer le parallélisme des traitements aurait simplement allourdi l’entièreté des traitements opérés, sans produire de vrai gain de temps sur les temps de traitement (voire en allongeant les temps de traitement).
En définitive, utiliser du multithreading représentait la solution la plus adaptée pour gérer les connexions des instances client car les seules tâches à réaliser concernaient de la gestion d’entrées et sorties sur les sockets réseau gérés par l’interface logicielle du serveur. - La gestion des médias chargés de manière dynamique :
Notre interface utilisateur devait afficher des images récupérées et chargées de manière dynamique, afin d’assurer à chaque démarrage de l’application un affichage cohérent avec les données présentes dans notre base de données.
Trois problèmes majeurs en ont résulté :- Comment échanger une image entre deux machines distantes sans intermédiaire applicatif ?
Après avoir tatônné un moment, nous avons favorisé l’échange du nom de fichier, du type de fichier (PNG, JPG, etc.) et du contenu binaire du fichier encodé en Base64 pour la transmission d’une image entre le serveur et une instance client.
Les transmissions étaient ensuite directement contrôlées par l’envoi de chaînes de caractères binaires encodées en Base64 sur les sockets réseau utilisés. - Comment retrouver sur le serveur les données associées à une certaine image ?
Dans un premier temps, il nous semblait intuitif d’enregistrer dans notre base de données le contenu binaire des images chargées par notre application, en utilisant le type de données MEDIUMBLOB, ainsi que les noms de fichier et les types de fichier associés.
Puis en cherchant à simplifier les traitements, nous nous sommes rendus compte qu’enregistrer nos images dans notre base de données n’était pas idéal, en raison du gaspillage que cela pouvait entraîner concernant les ressources du gestionnaire de base de données (espace utilisé, récupération des données, etc…).
Notre base de données fut donc adaptée pour enregistrer les chemins absolus des images utilisées, dans le but de retrouver le contenu de chaque image à partir du système de fichiers du serveur. - Comment optimiser les chargements d’images dans les instances client ?
En effet, il est inconcevable d’aller systématiquement récupérer sur le serveur les images nécessaires à l’affichage demandé par une instance client.
Cela produirait beaucoup trop de traitements redondants et surchargerait inutilement la pile d’opérations à réaliser par le serveur.
Nous avons ainsi mis en place des solutions algorithmiques permettant de restreindre les échanges de données chargées de manière dynamique tout en assurant un affichage cohérent et une expérience utilisateur optimisée.
- Comment échanger une image entre deux machines distantes sans intermédiaire applicatif ?
- La gestion d’opérations contextuelles en cascade :
Notre application nécessitait de traiter et d’afficher aux utilisateurs des informations sélectionnées selon des menus dont le comportement et l’affichage dépendaient d’autres menus.
Typiquement, notre application devait proposer aux clients de choisir d’abord une ville de destination, puis un hôtel de séjour dépendant de la ville de destination précédemment sélectionnée, puis un type de chambre d’hôtel dépendant des chambres disponibles dans l’hôtel sélectionné, puis des choix de combinaisons de caractéristiques reliées au type de chambre d’hôtel choisi dans l’hôtel sélectionné… et ainsi de suite.
Nous avons ainsi implémenté sur chaque interface logicielle des traitements logiques adaptés au traitement de menus contextuels en cascade, selon les besoins de chaque menu/contexte établi. - La gestion des messages échangés entre une interface logicielle front-end et l’interface logicielle back-end :
Dans le cadre d’échanges de données entre le serveur et les instances client de l’application, nous avons dû définir des messages précis pour traiter différents types de données, menant alors à différentes opérations de transmission.
Voici alors les quelques pistes que nous aurions souhaité explorer davantage dans l’éventualité où plus de temps nous aurait été accordé pour concevoir notre application :
- Implémenter des écrans de chargement distrayants dans l’interface utilisateur lors d’échanges de données volumineuses avec le serveur.
- Améliorer l’accessibilité de l’application en ajoutant des fonctionnalités adaptées aux personnes en situation de handicap visuel.
- Ajouter des politiques de confidentialité détaillées et conditions générales d’utilisation en libre accès dans l’application.
- Permettre davantage d’opérations de gestion du compte et des réservations dans l’interface de compte client.
- Permettre davantage d’opérations de gestion des chambres d’hôtel dans l’interface de compte concierge.
- Améliorer le design de l’interface des concierges d’hôtel.
- Uniformiser et organiser les champs de texte pour prévoir des changements de langage, qui auraient certainement impliqué l’utilisation de fichiers JSON.
- Produire des mécanismes de sauvegarde de la base de données et de resalage dynamique des mots de passe associés aux comptes utilisateur.
- Implémenter TLSv1.3 pour sécuriser les échanges de données entre les instances client et l’interface logicielle du serveur.
- Utiliser des certificats SSL/TLS signés par une autorité de certification reconnue.
Enfin, notre plus gros regret reste de ne pas avoir réussi à proposer un rendu fonctionnel sur support mobile.
Les erreurs rencontrées lors de la construction d’un fichier APK n’ont cessé d’apparaître, et finalement aucun fichier APK fonctionnel n’a pu être produit pour effectivement déployer notre application sur Android.
Il convient alors de noter que dans le cas où notre application aurait pu être testée sur Android, les méthodes et instructions utilisées pour gérer les médias chargés de manière dynamique dans notre application auraient certainement dû être ajustées.
Ce travail passionnant fut réalisé avec mon collègue Aymane El Moutaouakkel.
Mais bien que celui-ci ait été pensé et présenté dans le but d’être réalisé en binôme, j’ai conçu et développé l’ensemble du projet seul, en raison des lacunes constatées en début de projet chez mon camarade.
Il m’aurait été impossible de produire un rendu sérieux en révisant systématiquement ses productions algorithmiques.
Au travers de ce projet, mon objectif personnel devint alors d’aider mon camarade à progresser et à développer ses pratiques algorithmiques en utilisant mes propres pratiques et programmes comme base d’apprentissage.
Ainsi, mon camarade pu réellement expérimenter et prendre le temps de constituer une base solide en programmation, tout en s’appropriant les concepts et mécanismes implémentés par l’application produite.
Documents et livrables