changes to project dossier to make it more logical
|
@ -6,7 +6,7 @@ local root="$(dirname "${file}")"
|
||||||
local name="main"
|
local name="main"
|
||||||
local input="${root}/${name}.md"
|
local input="${root}/${name}.md"
|
||||||
local output="${root}/${name}.pdf"
|
local output="${root}/${name}.pdf"
|
||||||
local style="tango"
|
local style="espresso"
|
||||||
pandoc \
|
pandoc \
|
||||||
"${input}" \
|
"${input}" \
|
||||||
--highlight-style "${style}" \
|
--highlight-style "${style}" \
|
||||||
|
|
Before Width: | Height: | Size: 270 KiB After Width: | Height: | Size: 284 KiB |
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 360 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 286 KiB |
BIN
dossier de projet/images/webhook.png
Normal file
After Width: | Height: | Size: 150 KiB |
|
@ -175,20 +175,20 @@ Pour l'environnement général, nous utilisons :
|
||||||
|
|
||||||
## Déploiement
|
## Déploiement
|
||||||
|
|
||||||
*Le code*
|
**Le code**
|
||||||
Notre groupe ayant choisi d’avoir un dépôt par module, nous hébergeons sur github le code source dans une organisation commune avec :
|
Notre groupe ayant choisi d’avoir un dépôt par module, nous hébergeons sur github le code source dans une organisation commune avec :
|
||||||
|
|
||||||
* Un dépôt pour le back-end
|
* Un dépôt pour le back-end
|
||||||
* Un dépôt pour le front-end, dépendant du back, avec un docker-compose.yml
|
* Un dépôt pour le front-end, dépendant du back, avec un docker-compose.yml
|
||||||
|
|
||||||
*Le build*
|
**Le build**
|
||||||
Nous utilisons github actions, pour effectuer à chaque mise à jour du code back ou front :
|
Nous utilisons github actions, pour effectuer à chaque mise à jour du code back ou front :
|
||||||
|
|
||||||
* les tests de la base de code
|
* les tests de la base de code
|
||||||
* la fabrication de l’image docker
|
* la fabrication de l’image docker
|
||||||
* la publication de cette image dans notre groupe sur dockerhub
|
* la publication de cette image dans notre groupe sur dockerhub
|
||||||
|
|
||||||
*Le déploiement*
|
**Le déploiement**
|
||||||
Nous avons mis en place un service webhook, déclenchant via http, pour staging ou production :
|
Nous avons mis en place un service webhook, déclenchant via http, pour staging ou production :
|
||||||
|
|
||||||
* la récupération de la dernière image docker publiée sur dockerhub
|
* la récupération de la dernière image docker publiée sur dockerhub
|
||||||
|
@ -204,7 +204,7 @@ Le serveur étant une machine virtuelle mise à disposition temporairement par n
|
||||||
|
|
||||||
Notre formateur nous a proposé plusieurs sujets pendant notre alternance à la Wild Code School. Mon groupe et moi avons fait le choix de travailler un sujet de location, où il fallait développer un site qui met en relation des internautes avec un magasin de location, l’idée étant de faire un site pour un magasin qui fait des locations saisonnières pour les sports de montagne. Pour nous le cœur du sujet était la simplicité, pour rendre la location des biens le plus facile possible.
|
Notre formateur nous a proposé plusieurs sujets pendant notre alternance à la Wild Code School. Mon groupe et moi avons fait le choix de travailler un sujet de location, où il fallait développer un site qui met en relation des internautes avec un magasin de location, l’idée étant de faire un site pour un magasin qui fait des locations saisonnières pour les sports de montagne. Pour nous le cœur du sujet était la simplicité, pour rendre la location des biens le plus facile possible.
|
||||||
|
|
||||||
Notre application permet aux visiteurs de parcourir la catalogue et voir ce que le magasin a de disponible pour louer. Si un visiteur voudrait faire une réservation de matériel, il est obligé de créer un compte. Une fois le compte créé, il peut confirmer la disponibilité des produits, ajouter des produits dans son panier, puis en retirer ou modifier si besoin. Une fois décidé, il peut consulter un récapitulatif de son panier, puis créer une réservation pour les produits sélectionnés et finaliser la réservation par paiement. Pour le magasin, le rôle d’administrateur existe en plus du client. Seul un administrateur peut créer des nouveaux produits, en supprimer ou modifier si besoin, ainsi que consulter toutes les réservations en cours. Un utilisateur ne peut accéder à la partie administrative, mais un administrateur a accès à toutes les rubriques du site.
|
Notre application permet aux visiteurs de parcourir le catalogue et voir ce que le magasin a de disponible pour louer. Si un visiteur voudrait faire une réservation de matériel, il est obligé de créer un compte. Une fois le compte créé, il peut confirmer la disponibilité des produits, ajouter des produits dans son panier, puis en retirer ou modifier si besoin. Une fois décidé, il peut consulter un récapitulatif de son panier, puis créer une réservation pour les produits sélectionnés et finaliser la réservation par paiement. Pour le magasin, le rôle d’administrateur existe en plus du client. Seul un administrateur peut créer des nouveaux produits, en supprimer ou modifier si besoin, ainsi que consulter toutes les réservations en cours. Un utilisateur ne peut accéder à la partie administrative, mais un administrateur a accès à toutes les rubriques du site.
|
||||||
|
|
||||||
## Organisation du groupe et méthodes
|
## Organisation du groupe et méthodes
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ De cette façon, à tout moment, on pouvait voir qui travaillait sur quoi, et su
|
||||||
|
|
||||||
En plus de Git, nous avons utilisé Github, un plateforme pour développer des logiciels collaboratifs et hébergeur du code.
|
En plus de Git, nous avons utilisé Github, un plateforme pour développer des logiciels collaboratifs et hébergeur du code.
|
||||||
|
|
||||||
Afin de communiquer d’une manière fluide, en plus du Trello, nous avons eu à notre disposition un salon discord privé. Le serveur qui en faisait partie était accessible seulement aux personnes qui suivaient la formation, ayant été créé par le formateur de notre alternance. Comme notre salon était accessible seulement par ces personnes là, on a pu avoir l’esprit tranquille concernant nos communications ainsi que la confidentialité, car personne ne pouvait accéder à nos discussions. Nuus avons aussi créé un Google Drive afin de centraliser les informations du projet.
|
Afin de communiquer d’une manière fluide, en plus du Trello, nous avons eu à notre disposition un salon discord privé. Le serveur qui en faisait partie était accessible seulement aux personnes qui suivaient la formation, ayant été créé par le formateur de notre alternance. Comme notre salon était accessible seulement par ces personnes là, on a pu avoir l’esprit tranquille concernant nos communications ainsi que la confidentialité, car personne ne pouvait accéder à nos discussions. Nous avons aussi créé un Google Drive afin de centraliser les informations du projet.
|
||||||
|
|
||||||
Pour tous nos modèles, nous avons travaillé ensemble sur Miro dans un board commun afin que tout le monde puisse discuter et faire des modifications si besoin.
|
Pour tous nos modèles, nous avons travaillé ensemble sur Miro dans un board commun afin que tout le monde puisse discuter et faire des modifications si besoin.
|
||||||
|
|
||||||
|
@ -238,21 +238,25 @@ Pour la conception de notre wireframe ainsi que la maquette de l’application,
|
||||||
|
|
||||||
### Le Back-end :
|
### Le Back-end :
|
||||||
|
|
||||||
* NodeJs : Un environnement bas niveau qui permet l’exécution du code JavaScript côté serveur. Comme il a un fonctionnement non bloquant, il permet de concevoir des applications en réseau performantes, comme un serveur ou une API.
|
* NodeJs : Un environnement bas niveau qui permet l’exécution du code JavaScript côté serveur. Comme il a un fonctionnement non bloquant, il permet de concevoir des applications en réseau performantes.
|
||||||
* ExpressJs : Un framework pour construire des applications web basées sur NodeJs. C’est pratique pour le développpement dur serveur. Il permet une création d’API simple er robuste.
|
* ExpressJs : Un framework pour construire des applications web basées sur NodeJs. C’est pratique pour le développpement dur serveur. Il permet une création d’API simple er robuste.
|
||||||
* TypeORM : Une bibliothèque ORM (Object-Relational Mapping) pour TypeScript/JavaScript qui permet d’interagir avec des bases de données relationnelles en utilisant des principes OOP (object-oriented programming). Elle permet une façon de gérer les connexions de base de données et définir les modèles (entités) qui représente les tables de base de données, facilitant le travail de records de base de données comme objet.
|
* TypeORM : Une bibliothèque ORM (Object-Relational Mapping) pour TypeScript/JavaScript qui permet d’interagir avec des bases de données relationnelles en utilisant des principes OOP (object-oriented programming). Elle permet une façon de gérer les connexions de base de données et définir les modèles (entités) qui représente les tables de base de données, facilitant le travail de records de base de données comme objet.
|
||||||
* TypeGraphQL : Une bibliothèque qui construire des APIs graphQL en utilisant TypeScript. Elle utilise les decorators et les classes pour définir les schémas GraphQL, en le rendant plus facile à créer des APIs type-safe sans être obligé d’écrire beaucoup de code de boilerplate.
|
* TypeGraphQL : Une bibliothèque qui construit des APIs graphQL en utilisant TypeScript. Elle utilise les decorators et les classes pour définir les schémas GraphQL. Par exemple, on utilise le décorateur @ObjectType pour définir une classe comme un type GraphQL, et @Field pour déclarer les propriétés de cette classe qui seront mappées aux champs GraphQL.
|
||||||
|
Type-GraphQL rend la création de requêtes, de mutations et de champs plus simple en les définissant comme des méthodes de classe classiques, à l’image des contrôleurs REST. Cela permet une distinction nette entre la logique métier et la couche de transport, tout en facilitant les tests unitaires des résolveurs, qui peuvent être considérés comme de simples services.
|
||||||
* PostgreSQL : Un système de gestion de base de données relationnelle orienté objet, qui dispose également d’une clé étrangère pour rélier les données entre plusieurs tables.
|
* PostgreSQL : Un système de gestion de base de données relationnelle orienté objet, qui dispose également d’une clé étrangère pour rélier les données entre plusieurs tables.
|
||||||
|
|
||||||
### Le Front-end :
|
### Le Front-end :
|
||||||
|
|
||||||
* NextJs : Un framework qui utilise la bibliothèque React et s’appuie sur la technologie NodeJs. Il est utilisé surtout pour réduire la charge sur les navigateurs web et fournir une sécurité accrue.
|
* NextJs : Un framework qui utilise la bibliothèque React et s’appuie sur la technologie NodeJs. Il est utilisé surtout pour réduire la charge sur les navigateurs web et fournir une sécurité accrue.
|
||||||
* Material UI : Une bibliothèque de composants React qui est basée sur du CSS. Elle permet une personnalisation profonde grâce à une large gamme de paramètres de styles et d’options de personnalisation.
|
* Material UI : Une bibliothèque de composants React qui est basée sur du CSS. Elle permet une personnalisation profonde grâce à une large gamme de paramètres de styles et d’options de personnalisation.
|
||||||
|
* CSS : Un langage de style utilisé pour décrire la présentation du HTML.
|
||||||
|
|
||||||
### L'Environnement Général :
|
### L'Environnement Général :
|
||||||
|
|
||||||
* Typescript : Un langage qui ajoute un typage statique à Javascript, ce qui permet la définition des types de données pour les variables, les fonctions, et les objets.
|
* Typescript : Un langage qui ajoute un typage statique à Javascript, ce qui permet la définition des types de données pour les variables, les fonctions, et les objets.
|
||||||
* GraphQL : Un langage de requête de données pour API ainsi qu’un environnement d’exécution.
|
* GraphQL : Un langage de requête de données pour API ainsi qu’un environnement d’exécution. Il permet aux clients de demander uniquement les données dont ils ont besoin.
|
||||||
|
Contrairement aux APIs REST, GraphQL organise les requêtes de manière à obtenir des données provenant de plusieurs sources en un seul appel API. Côté serveur, il s’appuie sur un schéma qui décrit les données accessibles et utilise des résolveurs pour générer les valeurs correspondant aux requêtes.
|
||||||
|
Côté cleint, il s'appuie sur deux opérations principales : les Queries et les Mutations. Les Queries permettent de lire les données (l'équivalent de "Read" dans le modèle CRUD), alors que les Mutations permettent de créer, modifier, ou supprimer des données, qui sont quant à elles équivalentes aux opérations "Create", "Update", et "Delete" dans le modèle CRUD.
|
||||||
* Apollo Studio : Un plateforme de Apollo GraphQL qui aide les développeurs à build, gérer, et optimiser leurs APIs GraphQL. Pour le front-end, il y a Apollo Client, et le back, Apollo Server. Ayant des outils de la même équipe rend les chose plus lisibles.
|
* Apollo Studio : Un plateforme de Apollo GraphQL qui aide les développeurs à build, gérer, et optimiser leurs APIs GraphQL. Pour le front-end, il y a Apollo Client, et le back, Apollo Server. Ayant des outils de la même équipe rend les chose plus lisibles.
|
||||||
* Jest : Un framework de test Javascript qui permet de créer des tests unitaires.
|
* Jest : Un framework de test Javascript qui permet de créer des tests unitaires.
|
||||||
* Cypress : Un framework d’automatisation de tests basé sur Javascript, pour les tests end-to-end.
|
* Cypress : Un framework d’automatisation de tests basé sur Javascript, pour les tests end-to-end.
|
||||||
|
@ -294,7 +298,7 @@ On a aussi fait les maximalités (chiffres en bleu dans notre diagramme). Cela n
|
||||||
|
|
||||||
### Modèle Logique de Données
|
### Modèle Logique de Données
|
||||||
|
|
||||||
Nous progressons vers le Modèle Logique de Données, une étape plus concrète dans la définition de la base de données. Comme mentionné ci-dessus, on peut voir qu'il est nécessaire de créer un tableau intermédiaire entre Réservation et Material, qu’on a décidé d’appeler reserved_material, afin d’éviter une relation Many to Many. À cause de cela, on voit qu’on a deux attributs qui sont des foreign keys dans ce tableau, alors que dans d’autres comme Reservation, on a une clé étrangère pour lier les deux entités.
|
Nous progressons vers le Modèle Logique de Données, une étape plus concrète dans la définition de la base de données. Comme mentionné ci-dessus, on peut voir qu'il est nécessaire de créer un tableau intermédiaire entre Réservation et Material, qu’on a décidé d’appeler reserved_material, afin d’éviter une relation Many to Many. À cause de cela, on voit qu’on a deux attributs qui sont des foreign keys dans ce tableau, alors que dans d’autres comme Reservation, on n'a qu'une seule clé étrangère pour lier les deux entités.
|
||||||
|
|
||||||
![Le Modèle Logique de Données](images/MLD.jpg){width=70%}
|
![Le Modèle Logique de Données](images/MLD.jpg){width=70%}
|
||||||
|
|
||||||
|
@ -318,6 +322,8 @@ Après la création de notre merise, nous avons continué à travailler avec la
|
||||||
|
|
||||||
### Diagramme de classe
|
### Diagramme de classe
|
||||||
|
|
||||||
|
Le diagramme de classe reflète la définition des classes transformées en entités via GraphQL, les services, et les méthodes intégrés avec TypeORM, ainsi que les inputs pour les interactions avec le front-end.
|
||||||
|
|
||||||
Pour le diagramme de classe, nous nous reposons sur ces idées :
|
Pour le diagramme de classe, nous nous reposons sur ces idées :
|
||||||
|
|
||||||
* Nous cherchons à développer un système qui gère la location des matériaux à notre magasin.
|
* Nous cherchons à développer un système qui gère la location des matériaux à notre magasin.
|
||||||
|
@ -395,7 +401,7 @@ Un admin, cependant, a des droits qu’un client n’a pas. Il peut créer, mett
|
||||||
|
|
||||||
### Diagramme de séquence
|
### Diagramme de séquence
|
||||||
|
|
||||||
Le dernier diagramme UML que nous avons créé, c’était un diagramme de séquence. Nous avons choisi de se concentrer sur comment réagit un client loggé sur le site. On peut voir quand le client fait une requête de l’inventaire du site, il devrait recevoir une réponse avec les résultats de cette recherche. Il pourrait ensuite regarder la description du matériel ainsi que de l’ajouter dans son panier, et puis enfin procéder avec le paiement si désiré.
|
Le dernier diagramme UML que nous avons créé était un diagramme de séquence. Nous avons choisi de se concentrer sur comment réagit un client loggé sur le site. On peut voir quand le client fait une requête de l’inventaire du site, il devrait recevoir une réponse avec les résultats de cette recherche. Il pourrait ensuite regarder la description du matériel ainsi que de l’ajouter dans son panier, et puis enfin procéder avec le paiement si désiré.
|
||||||
|
|
||||||
![Le Diagramme de séquence](images/sequence diagram.jpg){width=60%}
|
![Le Diagramme de séquence](images/sequence diagram.jpg){width=60%}
|
||||||
|
|
||||||
|
@ -403,16 +409,18 @@ Le dernier diagramme UML que nous avons créé, c’était un diagramme de séqu
|
||||||
|
|
||||||
## Maquettes et enchaînement des maquettes
|
## Maquettes et enchaînement des maquettes
|
||||||
|
|
||||||
Nous avons créé une maquette sur Figma, un membre de notre groupe s’est dédié à la création des wireframes et puis nous avons discuté ensemble pour être sûrs que l’enchaînement du site semblait logique pour tout le monde, et s’assurer qu’on était sur la même page pour les prochaines étapes du développement, ainsi que nous aider à voir les interactions entre chaque page et les composants de l’application.
|
Suite à une séance de brainstorming sur notre vision de l'application, en tenant compte des éléments fournis par le client dans le cahier des charges, et après avoir travaillé la Merise et nos diagrammes, nous avons commencé à créer un wireframe avec l'outil Figma. Ce wireframe nous offre une représentation schématique et simplifiée de l'application, nous permettant de visualiser rapidement sa structure, son arborescence ainsi que l'organisation des composants et des interactions. Cela constitue une base solide pour débuter l'élaboration de la maquette.
|
||||||
|
|
||||||
![Wireframe du page d'accueil et de la recherche](images/accueil et recherche.png)
|
![Wireframe du page d'accueil et de la recherche](images/accueil et recherche.png)
|
||||||
|
|
||||||
![Wireframe du modal connexion](images/connexion.png)
|
![Wireframe du modal connexion](images/connexion.png)
|
||||||
|
|
||||||
Après, nous avons créé des mockups afin de peaufiner nos idées, choisir les couleurs en regardant des moodboards, et discuter des choses comme la typographie afin de s’assurer en termes d’UI que cela reste lisible et agréable pour l’utilisateur.
|
Ensuite, nous avons créé un moodboard pour définir l'orientation design du site. Nous avons réfléchi à l'expérience utilisateur (UX) et à l'interface utilisateur (UI) afin d'assurer une expérience à la fois efficace, agréable et fonctionnelle, tout en répondant aux attentes du client.
|
||||||
|
Les maquettes intègrent des éléments visuels tels que les couleurs, les typographies, les icônes et les images, afin de représenter le style et l'identité visuelle de l'application. Nous avons également inclus des éléments d'interaction comme des boutons et des formulaires pour illustrer les fonctionnalités de l'application.
|
||||||
La création des wireframes et mockups nous avaient beaucoup aidé dans le design dans le front ainsi qu’aider la communication entre nous, car nous étions tous sur la même page vers ce que comment le site final devrait ressembler.
|
Les maquettes finales ont servi de référence visuelle tout au long du processus de développement, facilitant ainsi la communication entre les différentes parties prenantes du projet. Le travail de maquettage sur Figma a été crucial pour la planification et la réalisation du projet, car il a permis de visualiser et de valider le concept avant sa mise en œuvre.
|
||||||
|
|
||||||
![Mockup de l'espace admin](images/mockup admin.png)
|
![Mockup de l'espace admin](images/mockup admin.png)
|
||||||
|
|
||||||
![Mockup de l'accueil](images/mockup accueil.png)
|
![Mockup de l'accueil](images/mockup accueil.png)
|
||||||
|
|
||||||
\newpage
|
\newpage
|
||||||
|
@ -421,7 +429,7 @@ La création des wireframes et mockups nous avaient beaucoup aidé dans le desig
|
||||||
|
|
||||||
## Création et connexion à la base de données et accès aux données
|
## Création et connexion à la base de données et accès aux données
|
||||||
|
|
||||||
Une fois la modélisation et la maquettes ont été faites, nous avons créé notre base de données avec PostgreSQL, que je vais vous décrire dans les prochaines étapes :
|
Une fois la modélisation et les maquettes ont été faites, nous avons créé notre base de données avec PostgreSQL, que je vais vous décrire dans les prochaines étapes :
|
||||||
|
|
||||||
### Configuration de TypeORM
|
### Configuration de TypeORM
|
||||||
|
|
||||||
|
@ -519,7 +527,7 @@ export default Category;
|
||||||
|
|
||||||
Les opérations de création, lecture, mise à jour et suppression des données s'effectuent dans les services grâce aux méthodes fournies par TypeORM. Cela permet une gestion efficace des données dans PostgreSQL tout en maintenant la logique métier distincte de l’implémentation des requêtes.
|
Les opérations de création, lecture, mise à jour et suppression des données s'effectuent dans les services grâce aux méthodes fournies par TypeORM. Cela permet une gestion efficace des données dans PostgreSQL tout en maintenant la logique métier distincte de l’implémentation des requêtes.
|
||||||
|
|
||||||
Prenons le service CategoryServices, qui s'occupe des entités Category. Ce service utilise TypeORM pour communiquer avec la base de données PostgreSQL, en offrant des méthodes telles que la création (create), la lecture (findById et list), la mise à jour (update) et la suppression (delete) d'une catégorie. De plus, le service est conçu pour gérer les relations entre les catégories et les matériaux associés, en utilisant des décorateurs TypeORM comme @OneToMany.
|
Prenons le service CategoryServices, qui s'occupe de l'entité Category. Ce service utilise TypeORM pour communiquer avec la base de données PostgreSQL, en offrant des méthodes telles que la création (create), la lecture (findById et list), la mise à jour (update) et la suppression (delete) d'une catégorie. De plus, le service est conçu pour gérer les relations entre les catégories et les matériaux associés, en utilisant des décorateurs TypeORM comme @OneToMany.
|
||||||
|
|
||||||
Ci-dessous un exemple des méthodes CRUD pour l’Entity Category, dans CategoryService :
|
Ci-dessous un exemple des méthodes CRUD pour l’Entity Category, dans CategoryService :
|
||||||
|
|
||||||
|
@ -596,7 +604,7 @@ Dans ce contexte, TypeORM facilite la gestion des données de manière efficace
|
||||||
|
|
||||||
Pour finir, TypeORM est associé à GraphQL et Apollo Server, ce qui simplifie les opérations de requêtes et de mutations sur la base de données. Les resolvers GraphQL interviennent sur le serveur pour gérer ces actions. Ils permettent d’accéder aux entités définies par TypeORM pour les requêtes (queries) et de manipuler les données lors des mutations (création, mise à jour et suppression). Chaque resolver correspond à une opération particulière de l'API GraphQL, garantissant ainsi une communication fluide entre le front-end et le back-end.
|
Pour finir, TypeORM est associé à GraphQL et Apollo Server, ce qui simplifie les opérations de requêtes et de mutations sur la base de données. Les resolvers GraphQL interviennent sur le serveur pour gérer ces actions. Ils permettent d’accéder aux entités définies par TypeORM pour les requêtes (queries) et de manipuler les données lors des mutations (création, mise à jour et suppression). Chaque resolver correspond à une opération particulière de l'API GraphQL, garantissant ainsi une communication fluide entre le front-end et le back-end.
|
||||||
|
|
||||||
Par exemple, le resolver dédié à la gestion des catégories inclut des requêtes telles que createCategory pour créer une catégorie et updateCategory pour en mettre à jour en fonction de son identifiant. On peut voir avec la capture ci-dessous que ce code suit les besoins d’une méthode CRUD, selon les méthodes fournies par CategoryServices.
|
Par exemple, le resolver dédié à la gestion des catégories inclut des requêtes telles que createCategory pour créer une catégorie et updateCategory pour en mettre à jour en fonction de son identifiant. On peut voir avec le code ci-dessous qu'il suit les besoins d’une méthode CRUD, selon les méthodes fournies par CategoryServices.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// category.resolver.ts
|
// category.resolver.ts
|
||||||
|
@ -677,15 +685,15 @@ export default class User {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Voir la section sécurité pour plus d'informations concernant argon2.
|
Plus d'information concernant argon2 est fournie dans la section Sécurité.
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
### JWT et gestion des cookies
|
### JWT et gestion des cookies
|
||||||
|
|
||||||
Les JSON Web Tokens (JWT) servent à authentifier les utilisateurs lors de leur connexion et tout au long de leur session. Après que le mot de passe a été validé par argon2, un JWT est créé avec la bibliothèque jose. Ce token inclut des informations non sensibles, comme l’adresse email et le rôle de l’utilisateur, qui sont intégrées dans le payload du JWT. Cela permet d’éviter de vérifier le rôle en base de données à chaque requête, ce qui allège la charge du serveur.
|
Les JSON Web Tokens (JWT) servent à authentifier les utilisateurs lors de leur connexion et tout au long de leur session. Après la validation du mot de passe par argon2, un JWT est créé avec la bibliothèque jose. Ce token inclut des informations non sensibles, comme l’adresse email et le rôle de l’utilisateur, qui sont intégrées dans le payload du JWT. Cela permet d’éviter de vérifier le rôle en base de données à chaque requête, ce qui allège la charge du serveur.
|
||||||
|
|
||||||
Un JWT est composé de trois éléments : le header, le payload et la signature. Le header, fourni automatiquement par la bibliothèque jose, indique l’algorithme de chiffrement utilisé (ici HS256). La signature est générée en combinant le header, le payload et une clé secrète (process.env.SECRET_KEY). Cette clé est cruciale pour la sécurité du JWT, car elle assure que le token n’a pas été modifié. Elle doit rester strictement confidentielle et ne jamais être divulguée.
|
Un JWT est composé de trois éléments : le header, le payload et la signature. Le header, fourni automatiquement par la bibliothèque jose, indique l’algorithme de chiffrement utilisé (dans notre cas, HS256). La signature est générée en combinant le header, le payload et une clé secrète (process.env.SECRET_KEY). Cette clé est cruciale pour la sécurité du JWT, car elle assure que le token n’a pas été modifié. Elle doit rester strictement confidentielle et ne jamais être divulguée.
|
||||||
|
|
||||||
Le JWT est ensuite enregistré dans un cookie HTTP-only, ce qui empêche l'accès par des scripts JavaScript malveillants, protégeant ainsi l'application contre les attaques XSS (Cross-Site Scripting). Grâce à ce cookie, l'utilisateur demeure authentifié pour chaque requête HTTP, et si un token valide est détecté, les informations qu'il renferme (comme l'email et le rôle) sont extraites pour établir le contexte de l'utilisateur.
|
Le JWT est ensuite enregistré dans un cookie HTTP-only, ce qui empêche l'accès par des scripts JavaScript malveillants, protégeant ainsi l'application contre les attaques XSS (Cross-Site Scripting). Grâce à ce cookie, l'utilisateur demeure authentifié pour chaque requête HTTP, et si un token valide est détecté, les informations qu'il renferme (comme l'email et le rôle) sont extraites pour établir le contexte de l'utilisateur.
|
||||||
|
|
||||||
|
@ -708,7 +716,7 @@ export interface Payload {
|
||||||
async login(@Arg('infos') infos: InputLogin, @Ctx() ctx: MyContext) {
|
async login(@Arg('infos') infos: InputLogin, @Ctx() ctx: MyContext) {
|
||||||
const user = await new UserServices().findUserByEmail(infos.email);
|
const user = await new UserServices().findUserByEmail(infos.email);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Error('Vérify your login information');
|
throw new Error('Vérifiez votre information de login');
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPasswordValid = await argon2.verify(user.password, infos.password);
|
const isPasswordValid = await argon2.verify(user.password, infos.password);
|
||||||
|
@ -729,7 +737,7 @@ export interface Payload {
|
||||||
m.message = 'Welcome!';
|
m.message = 'Welcome!';
|
||||||
m.success = true;
|
m.success = true;
|
||||||
} else {
|
} else {
|
||||||
m.message = 'Verify your login information';
|
m.message = 'Vérifiez votre information de login';
|
||||||
m.success = false;
|
m.success = false;
|
||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
|
@ -804,7 +812,7 @@ expressMiddleware(server, {
|
||||||
|
|
||||||
De plus, pour renforcer la sécurité, nous avons mis en place un système d'authentification et de gestion des autorisations utilisant le décorateur @Authorized de TypeGraphQL et un customAuthChecker. Ce dispositif permet de protéger les résolveurs et de contrôler l'accès aux différentes fonctionnalités de l'application selon les rôles des utilisateurs.
|
De plus, pour renforcer la sécurité, nous avons mis en place un système d'authentification et de gestion des autorisations utilisant le décorateur @Authorized de TypeGraphQL et un customAuthChecker. Ce dispositif permet de protéger les résolveurs et de contrôler l'accès aux différentes fonctionnalités de l'application selon les rôles des utilisateurs.
|
||||||
|
|
||||||
Le décorateur @Authorized est employé pour limiter l'accès à certains résolveurs ou champs. Par exemple, nous avons appliqué @Authorized("ADMIN") sur des résolveurs sensibles, tels que la création, la modification ou la suppression de catégorie (voir page 25), afin que seuls les administrateurs puissent y accéder. Pour d'autres cas, nous avons opté pour @Authorized("USER", "ADMIN"), permettant ainsi l'accès aux utilisateurs et aux administrateurs. Lorsque qu'un résolveur n'exige pas de restrictions particulières, mais qu'il doit vérifier que l'utilisateur est authentifié, nous utilisons @Authorized() pour s'assurer que l'accès est réservé aux utilisateurs connectés.
|
Le décorateur @Authorized est employé pour limiter l'accès à certains résolveurs ou champs. Par exemple, nous avons appliqué @Authorized("ADMIN") sur des résolveurs sensibles, tels que la création, la modification ou la suppression de catégorie (voir pages 25 et 26), afin que seuls les administrateurs puissent y accéder. Pour d'autres cas, nous avons opté pour @Authorized("USER", "ADMIN"), permettant ainsi l'accès aux utilisateurs et aux administrateurs. Lorsque qu'un résolveur n'exige pas de restrictions particulières, mais qu'il doit vérifier que l'utilisateur est authentifié, nous utilisons @Authorized() pour s'assurer que l'accès est réservé aux utilisateurs connectés.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// authChecker.ts
|
// authChecker.ts
|
||||||
|
@ -916,7 +924,7 @@ export default function Home() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* Le fichier index.tsx est intégré dans le composant App, défini dans _app.tsx, qui s'occupe de la configuration générale de l'application. Dans _app.tsx, nous avons mis en place un client Apollo pour gérer les requêtes GraphQL et utilisé AppCacheProvider afin d'optimiser les performances des composants Material-UI. Nous avons également désactivé le rendu côté serveur (SSR) pour cette application. De plus, nous avons ajouté le composant MainNav dans _app.tsx pour garantir que la navigation principale soit accessible sur toutes les pages, tout en permettant de déterminer si l'on se trouve sur la page d'accueil, ce qui nous aidera à adapter la navigation selon les besoins.
|
* Le fichier index.tsx est intégré dans le composant App, défini dans _app.tsx, qui s'occupe de la configuration générale de l'application. Dans _app.tsx, nous avons mis en place un client Apollo pour gérer les requêtes GraphQL et avons utilisé AppCacheProvider afin d'optimiser les performances des composants Material-UI. Nous avons également désactivé le rendu côté serveur (SSR) pour cette application. De plus, nous avons ajouté le composant MainNav dans _app.tsx pour garantir que la navigation principale soit accessible sur toutes les pages, tout en permettant de déterminer si l'on se trouve sur la page d'accueil, ce qui nous aidera à adapter la navigation selon les besoins.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/pages/_app.tsx
|
// src/pages/_app.tsx
|
||||||
|
@ -1006,7 +1014,7 @@ Document.getInitialProps = async (ctx: any) => {
|
||||||
Nous avons utilisé Apollo Client et GraphQL Codegen pour simplifier et optimiser la gestion des données entre le front-end et notre API GraphQL.
|
Nous avons utilisé Apollo Client et GraphQL Codegen pour simplifier et optimiser la gestion des données entre le front-end et notre API GraphQL.
|
||||||
|
|
||||||
* Apollo Client est une bibliothèque qui facilite la communication avec le serveur GraphQL. Elle simplifie l'envoi des requêtes et des mutations, tout en intégrant les états de chargement, de réussite et d'erreur directement au sein de nos composants.
|
* Apollo Client est une bibliothèque qui facilite la communication avec le serveur GraphQL. Elle simplifie l'envoi des requêtes et des mutations, tout en intégrant les états de chargement, de réussite et d'erreur directement au sein de nos composants.
|
||||||
* GraphQL Codegen, de son côté, génère automatiquement des types TypeScript et des hooks à partir de nos schémas GraphQL, ce qui assure une fiabilité du typage, diminue les erreurs et augmente la productivité.
|
* GraphQL Codegen, de son côté, génère automatiquement des types TypeScript et des hooks à partir de nos schémas GraphQL, ce qui assure une fiabilité du typage, diminuant ainsi les erreurs et augmentant la productivité.
|
||||||
|
|
||||||
Tout d'abord, nous établissons nos requêtes et nos mutations dans des fichiers spécifiques, qui servent de fondation pour Apollo Client et Codegen, comme illustré ici :
|
Tout d'abord, nous établissons nos requêtes et nos mutations dans des fichiers spécifiques, qui servent de fondation pour Apollo Client et Codegen, comme illustré ici :
|
||||||
|
|
||||||
|
@ -1064,7 +1072,7 @@ export const FIND_CATEGORY = gql`
|
||||||
`;
|
`;
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensuite, nous configurons Codegen pour qu'il utilise à la fois nos schémas disponibles sur le serveur et les requêtes et mutations définies dans nos fichiers `.queries.ts` et `.mutation.ts`.
|
Ensuite, nous configurons Codegen pour qu'il utilise à la fois nos schémas disponibles sur le serveur et les requêtes et mutations définies dans nos fichiers *`.queries.ts`* et *`.mutation.ts`*.
|
||||||
|
|
||||||
Codegen produit alors un fichier qui contient les types et les hooks correspondants.
|
Codegen produit alors un fichier qui contient les types et les hooks correspondants.
|
||||||
|
|
||||||
|
@ -1104,7 +1112,7 @@ const Login: React.FC<LoginProps> = ({ showRegister, closeModals }) => {
|
||||||
|
|
||||||
const [loginUser, { data, loading, error }] = useLazyQuery(LOGIN, {
|
const [loginUser, { data, loading, error }] = useLazyQuery(LOGIN, {
|
||||||
onCompleted: async (data) => {
|
onCompleted: async (data) => {
|
||||||
console.log("Mutation completed:", data);
|
console.log("Mutation complété:", data);
|
||||||
if (data.login.success) {
|
if (data.login.success) {
|
||||||
closeModals();
|
closeModals();
|
||||||
await client.refetchQueries({include:["UserInfos"]});
|
await client.refetchQueries({include:["UserInfos"]});
|
||||||
|
@ -1112,7 +1120,7 @@ const Login: React.FC<LoginProps> = ({ showRegister, closeModals }) => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("Mutation error:", error);
|
console.error("Erreur de mutation:", error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1135,9 +1143,9 @@ const Login: React.FC<LoginProps> = ({ showRegister, closeModals }) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log("Response from server:", response);
|
console.log("Réponse du serveur:", response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Submission error:", e);
|
console.error("Erreur de soumission:", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
@ -1220,7 +1228,7 @@ useEffect(() => {
|
||||||
|
|
||||||
Lorsque l'utilisateur se trouve sur la page d'accueil, ces boutons de catégorie sont visibles, offrant un accès direct aux différentes catégories de matériels. Ces boutons sont créés dynamiquement en parcourant les catégories obtenues grâce à la requête GraphQL. Chaque bouton permet de rediriger l'utilisateur vers une page de liste de matériels correspondant à la catégorie choisie.
|
Lorsque l'utilisateur se trouve sur la page d'accueil, ces boutons de catégorie sont visibles, offrant un accès direct aux différentes catégories de matériels. Ces boutons sont créés dynamiquement en parcourant les catégories obtenues grâce à la requête GraphQL. Chaque bouton permet de rediriger l'utilisateur vers une page de liste de matériels correspondant à la catégorie choisie.
|
||||||
|
|
||||||
Le fichier `categories/[id].tsx` dans Next.js utilise le routage dynamique pour afficher les détails d'une catégorie spécifique. Le segment `[id]` dans l'URL permet de capturer l'identifiant de la catégorie et de le transmettre comme paramètre à la page. Cette dernière effectue une requête GraphQL pour récupérer les matériels liés à la catégorie sélectionnée et les affiche dans une liste. Chaque matériel est rendu à l'aide d'un composant MaterialCard, qui présente les informations détaillées sur le matériel.
|
Le fichier *`categories/[id].tsx`* dans Next.js utilise le routage dynamique pour afficher les détails d'une catégorie spécifique. Le segment *`[id]`* dans l'URL permet de capturer l'identifiant de la catégorie et de le transmettre comme paramètre à la page. Cette dernière effectue une requête GraphQL pour récupérer les matériels liés à la catégorie sélectionnée et les affiche dans une liste. Chaque matériel est rendu à l'aide d'un composant MaterialCard, qui présente les informations détaillées sur le matériel.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// categories/[id].tsx
|
// categories/[id].tsx
|
||||||
|
@ -1560,7 +1568,7 @@ const materialsData: Material[] = [
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
Dans ce test, nous établissons une requête `LIST_MATERIALS` pour interroger notre API et obtenir une liste de matériaux. Nous simulons cette liste avec des données fictives stockées dans un mock store. Ce store est généré à l'aide de `createMockStore`, grâce à la bibliothèque `@graphql-tools/mock`, qui permet de simuler une base de données en mémoire.
|
Dans ce test, nous établissons une requête `LIST_MATERIALS` pour interroger notre API et obtenir une liste de matériaux. Nous simulons cette liste avec des données fictives stockées dans un mock store. Ce store est généré à l'aide de *`createMockStore`*, grâce à la bibliothèque *`@graphql-tools/mock`*, qui permet de simuler une base de données en mémoire.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// __tests__/material.test.ts
|
// __tests__/material.test.ts
|
||||||
|
@ -1637,9 +1645,9 @@ describe('Test sur les matériels', () => {
|
||||||
### Tests front-end
|
### Tests front-end
|
||||||
|
|
||||||
Tests front-end :
|
Tests front-end :
|
||||||
Pour ce qui concerne le front, nous avons mis en place deux tests sur notre Main Nav, une pour tester que la nav bar rend correctement, nous avons fait ça en créant un test qui cherche le titre dans la navbar. S’il trouve le titre, le test passe. Le deuxième test est pour voir si on peut se connecter en tant qu’admin. Si on arrive à correctement trouver un des boutons qui apparaît lorsqu’on est connecté en tant qu’admin, le test passe également. Mais tout d’abord, comme il y a un import d’un fichier css dans notre nav bar (MainNav.tsx), il fallait trouver un moyen de contourner cela, car comme mentionné ci-dessus, jest ne reconnaît que des fichiers ts ou tsx.
|
Pour ce qui concerne le front, nous avons mis en place deux tests sur notre Main Nav, un test d'unité ainsi qu'un test d'intégration. Mais tout d’abord, comme il y a un import d’un fichier css dans notre nav bar (MainNav.tsx), il fallait trouver un moyen de contourner cela, car comme mentionné ci-dessus, jest ne reconnaît que des fichiers ts ou tsx.
|
||||||
|
|
||||||
Pour éviter ce problème, nous avons installé webpack, un module bundler qui permet de traiter différents types de fichier. Dans notre cas, il est particulièrement utile pour transformer la compréhension des fichiers css pour Jest. Nous avons aussi ajouté une ligne de code supplémentaire dans notre jest.config.ts pour rédiriger et rendre compréhensible le css éxistant dans notre MainNav.
|
Pour éviter ce problème, nous avons installé webpack, un module bundler qui permet de traiter différents types de fichiers. Dans notre cas, il est particulièrement utile pour transformer la compréhension des fichiers css pour Jest. Nous avons aussi ajouté une ligne de code supplémentaire dans notre jest.config.ts pour rédiriger et rendre compréhensible le css éxistant dans notre MainNav.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// jest.config.ts
|
// jest.config.ts
|
||||||
|
@ -1659,21 +1667,13 @@ Et par conséquent, nous avons créé ce fichier vide pour la redirection :
|
||||||
module.exports = {};
|
module.exports = {};
|
||||||
```
|
```
|
||||||
|
|
||||||
Avec cette mise en place, on a pu créer les tests :
|
En plus de ce contournement du fichier CSS, nous avons aussi mis en place un MockedProvider, car pour chaque test d'un composant React qui utilise Apollo Client doit l'avoir disponible dans le context de React. Dans le code de l'application, on crée ce context en encapsulant les composants, alors pour imiter au mieux ce schéma, on utilise MockedProvider.
|
||||||
|
|
||||||
|
Pour le premier test (un test d'unité), on teste si notre fichier MainNav.tsx affiche bien notre titre; en utilisant la méthode *`findByText`*, qui peut retourner un élément trouvé dans le DOM ou lancer une erreur si aucun élément correspondant n'est trouvé, ainsi que *`toBeInTheDocument`* comme une manière supplémentaire de s'assurer que l'élement existe dans le DOM au moment du test.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Navbar.test.tsx
|
// Navbar.test.tsx
|
||||||
|
// test sur le titre
|
||||||
import "cross-fetch/polyfill";
|
|
||||||
import "@testing-library/jest-dom";
|
|
||||||
import { render, screen } from "@testing-library/react";
|
|
||||||
import MainNav from "@/components/MainNav/MainNav";
|
|
||||||
import { MockedProvider } from "@apollo/client/testing";
|
|
||||||
import { LIST_MATERIALS } from "@/requests/queries/materials.queries";
|
|
||||||
import { LIST_CATEGORIES } from "@/requests/queries/categories.queries";
|
|
||||||
import { USER_INFOS } from "@/requests/queries/auth.queries";
|
|
||||||
import AuthProvider from "@/context/authProvider";
|
|
||||||
|
|
||||||
|
|
||||||
jest.mock("next/router", () => ({
|
jest.mock("next/router", () => ({
|
||||||
useRouter: jest.fn().mockReturnValue({
|
useRouter: jest.fn().mockReturnValue({
|
||||||
|
@ -1718,63 +1718,68 @@ describe("MainNav", () => {
|
||||||
|
|
||||||
expect(titre).toBeInTheDocument();
|
expect(titre).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
```
|
||||||
|
|
||||||
it("mock d'un faux utilisateur connecté", async () => {
|
Pour le deuxième test (un test d'intégration), on veut s'assurer qu'un des boutons (nous avons choisi le bouton stock) apparaît lorqu'on est connecté en tant qu'admin sur notre application. Comme c'est un bouton qui apparaît seulement lorsqu'on est connecté en tant qu'admin, cela nous assure que l'intéraction entre le login et le MainNav fonctionne correctement. Pour ceci, un crée un mock user concernant les infos nécessaires dans USER_INFOS.
|
||||||
render(
|
|
||||||
<MockedProvider
|
```typescript
|
||||||
mocks={[
|
// Navbar.test.tsx
|
||||||
{
|
// test sur l'apparition du bouton stock lors d'être connecté comme admin
|
||||||
request: {
|
|
||||||
query: LIST_MATERIALS,
|
it("mock d'un faux utilisateur connecté", async () => {
|
||||||
},
|
render(
|
||||||
result: {
|
<MockedProvider
|
||||||
data: {
|
mocks={[
|
||||||
listMaterials: [],
|
{
|
||||||
},
|
request: {
|
||||||
},
|
query: LIST_MATERIALS,
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: {
|
||||||
|
listMaterials: [],
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
request: {
|
},
|
||||||
query: LIST_CATEGORIES,
|
{
|
||||||
},
|
request: {
|
||||||
result: {
|
query: LIST_CATEGORIES,
|
||||||
data: {
|
},
|
||||||
listCategories: [],
|
result: {
|
||||||
},
|
data: {
|
||||||
},
|
listCategories: [],
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
request: {
|
},
|
||||||
query: USER_INFOS,
|
{
|
||||||
},
|
request: {
|
||||||
result: {
|
query: USER_INFOS,
|
||||||
data: {
|
},
|
||||||
userInfos: {
|
result: {
|
||||||
id: "123",
|
data: {
|
||||||
firstname: "admin",
|
userInfos: {
|
||||||
lastname: "adminer",
|
id: "123",
|
||||||
email: "test@myoros.com",
|
firstname: "admin",
|
||||||
role: "ADMIN",
|
lastname: "adminer",
|
||||||
},
|
email: "test@myoros.com",
|
||||||
|
role: "ADMIN",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]}
|
},
|
||||||
>
|
]}
|
||||||
<AuthProvider>
|
>
|
||||||
<MainNav />
|
<AuthProvider>
|
||||||
</AuthProvider>
|
<MainNav />
|
||||||
</MockedProvider>
|
</AuthProvider>
|
||||||
);
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
|
||||||
const adminLink = await screen.findByTestId("admin-link");
|
const adminLink = await screen.findByTestId("admin-link");
|
||||||
|
expect(adminLink).toBeInTheDocument();
|
||||||
expect(adminLink).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Dans notre composant MainNav, nous avons mis ce lien pour le test également :
|
Comme on teste notre bouton en utilisant *`findByTestId`*; ce test id doit également apparaître dans notre MainNav afin que ce test fonctionne correctement :
|
||||||
|
|
||||||
![Lien dans MainNav faisant référence au test](images/test link MainNav.png){width=70%}
|
![Lien dans MainNav faisant référence au test](images/test link MainNav.png){width=70%}
|
||||||
|
|
||||||
|
@ -1802,7 +1807,7 @@ Nous avons donc mis en place des workflows d'intégration continue (CI) afin d'a
|
||||||
Le workflow back-end est divisé en deux étapes principales :
|
Le workflow back-end est divisé en deux étapes principales :
|
||||||
|
|
||||||
1. Tests Jest : Lorsqu'un push est effectué, les tests du back-end sont lancés grâce au script `npm run test-ci`. Ces tests vérifient le bon fonctionnement des différentes sections de l'application, s'assurant que les modifications apportées au code ne causent pas de régressions.
|
1. Tests Jest : Lorsqu'un push est effectué, les tests du back-end sont lancés grâce au script `npm run test-ci`. Ces tests vérifient le bon fonctionnement des différentes sections de l'application, s'assurant que les modifications apportées au code ne causent pas de régressions.
|
||||||
1. Build et Push Docker : Si les tests réussissent sur la branche dev, une image Docker de l'application back-end est créée et envoyée sur DockerHub. Les secrets GitHub sont employés pour protéger les identifiants de DockerHub.
|
1. Build et Push Docker : Si les tests réussissent sur la branche main, une image Docker de l'application back-end est créée et envoyée sur DockerHub. Les secrets GitHub sont employés pour protéger les identifiants de DockerHub.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
// .github/workflows/back.tests.yml
|
// .github/workflows/back.tests.yml
|
||||||
|
@ -1895,7 +1900,10 @@ Ensuite, nous avons mis en place l'environnement serveur requis pour le déploie
|
||||||
* Configuration de Caddy et des webhooks : Nous avons aussi configuré les fichiers de configuration pour Caddy et les webhooks :
|
* Configuration de Caddy et des webhooks : Nous avons aussi configuré les fichiers de configuration pour Caddy et les webhooks :
|
||||||
* Caddyfile : Ce fichier permet de diriger les requêtes entrantes grâce à la directive reverse_proxy vers les ports appropriés. Les requêtes adressées à staging.1123-jaune-1.wns.wilders.dev sont redirigées vers le port 8001 (staging), celles destinées à 1123-jaune-1.wns.wilders.dev vont vers le port 8000 (production), tandis que les requêtes à ops.1123-jaune-1.wns.wilders.dev sont dirigées vers le port 9000 (service Webhook).
|
* Caddyfile : Ce fichier permet de diriger les requêtes entrantes grâce à la directive reverse_proxy vers les ports appropriés. Les requêtes adressées à staging.1123-jaune-1.wns.wilders.dev sont redirigées vers le port 8001 (staging), celles destinées à 1123-jaune-1.wns.wilders.dev vont vers le port 8000 (production), tandis que les requêtes à ops.1123-jaune-1.wns.wilders.dev sont dirigées vers le port 9000 (service Webhook).
|
||||||
* Webhook.conf : Ce fichier de configuration au format JSON spécifie les actions à réaliser lors de la réception d'une notification. Nous avons configuré deux webhooks :
|
* Webhook.conf : Ce fichier de configuration au format JSON spécifie les actions à réaliser lors de la réception d'une notification. Nous avons configuré deux webhooks :
|
||||||
* update-staging : le premier peut être lancé en allant à `https://ops.1123-jaune-1.wns.wilders.dev/hooks/update-staging` lors de la disponibilité d'une nouvelle image sur Dockerhub, afin de vérifier les résultats attendus
|
* update-staging : le premier est déclenché automatiquement par Dockerhub lorqu'une nouvelle image est disponible, comme nous avons fait un lien entre les deux :
|
||||||
|
|
||||||
|
![Webhook referencé sur le dépôt front-end de Dockerhub](images/webhook.png){width=70%}
|
||||||
|
|
||||||
* update-prod : le deuxième peut être lancé en allant à `https://ops.1123-jaune-1.wns.wilders.dev/hooks/update-prod` une fois que les résultats sur `https://staging.1123-jaune-1.wns.wilders.dev` ont été vérifé afin de mettre l'image la plus récente de Dockerhub
|
* update-prod : le deuxième peut être lancé en allant à `https://ops.1123-jaune-1.wns.wilders.dev/hooks/update-prod` une fois que les résultats sur `https://staging.1123-jaune-1.wns.wilders.dev` ont été vérifé afin de mettre l'image la plus récente de Dockerhub
|
||||||
* Fichiers Docker Compose et Nginx : Voici un aperçu des fichiers utilisés pour la configuration de nos services et de notre proxy :
|
* Fichiers Docker Compose et Nginx : Voici un aperçu des fichiers utilisés pour la configuration de nos services et de notre proxy :
|
||||||
* services : backend, frontend, db, nginx : Le fichier docker-compose-staging.yml configure les services du back-end (Node.js), du front-end (Next), de la base de données (Postgres), ainsi que Nginx pour gérer le reverse proxy. Chacun de ces services est surveillé par Docker, avec des vérifications de santé (healthcheck) et des dépendances clairement établies entre eux.
|
* services : backend, frontend, db, nginx : Le fichier docker-compose-staging.yml configure les services du back-end (Node.js), du front-end (Next), de la base de données (Postgres), ainsi que Nginx pour gérer le reverse proxy. Chacun de ces services est surveillé par Docker, avec des vérifications de santé (healthcheck) et des dépendances clairement établies entre eux.
|
||||||
|
@ -1970,7 +1978,7 @@ Ensuite, nous avons mis en place l'environnement serveur requis pour le déploie
|
||||||
|
|
||||||
* nginx.conf : Ce fichier a pour rôle de rediriger les requêtes entrantes. Par exemple, les requêtes adressées à /graphql sont dirigées vers le back-end, tandis que toutes les autres sont envoyées vers le front-end.
|
* nginx.conf : Ce fichier a pour rôle de rediriger les requêtes entrantes. Par exemple, les requêtes adressées à /graphql sont dirigées vers le back-end, tandis que toutes les autres sont envoyées vers le front-end.
|
||||||
|
|
||||||
```
|
```shell
|
||||||
// nginx.conf
|
// nginx.conf
|
||||||
|
|
||||||
events {}
|
events {}
|
||||||
|
@ -2059,32 +2067,31 @@ La securité d’une application est extrêmement importante. On ne peut pas pen
|
||||||
|
|
||||||
1. TypeORM : ORM veut dire Object-Relational Mapping, c’est une bibiliothèque pour TypeScript/JavaScript qui permet les développeurs à interagir avec les bases de données en utilisant une méthode orienté-objet. Voici les raisons pour lesquelles elle peut être utile pour la sécurité :
|
1. TypeORM : ORM veut dire Object-Relational Mapping, c’est une bibiliothèque pour TypeScript/JavaScript qui permet les développeurs à interagir avec les bases de données en utilisant une méthode orienté-objet. Voici les raisons pour lesquelles elle peut être utile pour la sécurité :
|
||||||
* Prévention d’injection SQL : comme elle sépare les données des commandes, cela aide à éviter les attaques par injection SQL
|
* Prévention d’injection SQL : comme elle sépare les données des commandes, cela aide à éviter les attaques par injection SQL
|
||||||
* Validation des données : TypeORM supporte les decorators pour validation ce qui permet de mettre des contraintes sur les propriétés Entity (par exemple des fields obligatoires, des valeurs uniques), ce qui aide à être certain que seulement des données valide est sauvegardé dans la base de données.
|
* Validation des données : TypeORM supporte les decorators pour validation, ce qui permet de mettre des contraintes sur les propriétés Entité (par exemple des champs obligatoires, des valeurs uniques), ce qui aide à se rassurer que seulement des données valides sont sauvegardées dans la base de données.
|
||||||
* Gestion de relation entre Entity : comme elle gère les relations par Entity, cela reduit le risque d’exposer des données sensibles par accident en faisant des queries complexes. On peut contrôler quelles données sont récupérées.
|
* Gestion de relation entre Entités : comme elle gère les relations par Entité, cela reduit le risque d’exposer des données sensibles par accident en faisant des quêtes complexes. On peut contrôler quelles données sont récupérées.
|
||||||
* Options de connexion : TypeORM permet de configurer les options de connexion d’une façon secure, y compris l’utilisation de variables d’environnement pour les données sensibles (comme les identifiants de base de donnée), ce qui permet de les séparer de la source du code.
|
* Options de connexion : TypeORM permet de configurer les options de connexion d’une façon secure, y compris l’utilisation de variables d’environnement pour les données sensibles (comme les identifiants de base de données), ce qui permet de les séparer de la source du code.
|
||||||
* Eager / Lazy Loading : TypeORM support eager loading ainsi que lazy loading, ce qui permet de contrôler quand les données sont récupérées, ce qui peut réduie les chances d’exposer des données pas nécessaires et des attaques de surface.
|
* Eager / Lazy Loading : TypeORM support eager loading ainsi que lazy loading, ce qui permet de contrôler quand les données sont récupérées, ce qui peut réduire les chances d’exposer des données non nécessaires et la surface d'attaques.
|
||||||
1. JSX : Comme nous utilisons Next et React pour notre application, nous avons écrit en JSX. Même si JSX est une extension de syntaxe pour JavaScript avec React pour décrire comment l’UI devrait être, mais il peut également contribuer à la sécurité d’une application :
|
1. JSX : Comme nous utilisons Next et React pour notre application, nous avons écrit en JSX. Même si le JSX est une extension de syntaxe pour JavaScript avec React pour décrire comment l’UI devrait être, mais il peut également contribuer à la sécurité d’une application :
|
||||||
* Échappe automatique : JSX échappe automatiquement des valeurs insérées dans le markup. Cela aide à empêcher des attaques Cross-Site Scripting (XSS) car n’importe quel contenu généré par un utilisateur est traité comme texte simple, et pas du code exécutable.
|
* Échappe automatique : le JSX échappe automatiquement des valeurs insérées dans le markup. Cela aide à empêcher des attaques Cross-Site Scripting (XSS) car n’importe quel contenu généré par un utilisateur est traité comme texte simple, et pas du code exécutable.
|
||||||
* Architecture basé sur les components : Comme il encourage une approche modulaire, JSX encourage une meilleure organisation de code. Cette séparation peut aider en isolant et en gérant les préoccupations de sécurité dans des parties plus petites et gérables.
|
* Architecture basée sur les composants : Comme il encourage une approche modulaire, le JSX encourage une meilleure organisation de code. Cette séparation peut aider en isolant et en gérant les préoccupations de sécurité dans des parties plus petites et gérables.
|
||||||
* Rendering Contrôlé : Reaéct permet de contrôler comment les components se rend basés sur state et props. Cela veut dire qu’on peut rendre du contenu sous condition ce qui peut être utile pour implémenter des mesures de sécurité, par exemple cacher des informations sensibles basés sur les rôles d’utilisateur.
|
* Rendu Contrôlé : React permet de contrôler comment les composants se rendent basés sur state et props. Cela veut dire qu’on peut rendre du contenu sous condition, ce qui peut être utile pour implémenter des mesures de sécurité (par exemple cacher des informations sensibles basés sur les rôles d’utilisateur).
|
||||||
* Validation de Props : Utiliser TypeScript avec JSX peut aider à s’assurer que les components reçoivent les types de données attendus, ce qui réduit le risque de runtime errors ou un fonctionnement inattendu qui peut créer des vulnérabilités de sécurité.
|
* Validation de Props : Utiliser TypeScript avec JSX peut aider à s’assurer que les composants reçoivent les types de données attendus, ce qui réduit le risque d'erreurs d'exécution ou un fonctionnement inattendu qui peut créer des failles de sécurité.
|
||||||
1. Argon2 : C’est un algorithme de hachage que nous avons utilisé pour les mots de passe utilisateurs, et cela rehausse la sécurité de plusieurs manières :
|
1. Argon2 : C’est un algorithme de hachage que nous avons utilisé pour les mots de passe utilisateurs, et cela rehausse la sécurité de plusieurs manières :
|
||||||
* Fonction Memory-Hard : Argon2 est créé pour obliger une grande quantité de mémoire pour calculer les hashes, ce qui le rend résistant à des attaques de brute force utilisant du matériel informatique comme GPUs et ASICs. Cela veut dire que les personne voulant faire une attaque doit investir dans des ressources très coûteuses s’ils veulent faire une attaque.
|
* Fonction Memory-Hard : Argon2 est créé pour obliger une grande quantité de mémoire pour calculer les hashes, ce qui le rend résistant à des attaques de brute force utilisant du matériel informatique comme des GPUs et ASICs. Cela veut dire que les personne voulant faire une attaque doit investir dans des ressources très coûteuses s’ils veulent faire une attaque.
|
||||||
* Résistance à des attaques side channel : Le design d’Argon2 aide à mitiger les risques d’attaques side channel, comme des attaques de timing, en rendant moins prédictible et plus consistant dans sa performance.
|
* Résistance à des attaques par canal auxiliaire : Le design d’Argon2 aide à mitiger les risques d’attaques par canal auxiliaire, comme des attaques temporelles, en rendant moins prévisible et plus consistant dans sa performance.
|
||||||
* Salting intégré : Argon2 incorpore automatiquement un salt dans le processus de hachage, ce qui assure que même si 2 utilisateurs ont le même mot de passe, leur hashes seront différents. Cela évite des attaques pre-calculés de hachage, comme les rainbow tables.
|
* Salting intégré : Argon2 incorpore automatiquement un salt dans le processus de hachage, ce qui assure que même si 2 utilisateurs ont le même mot de passe, leur hashes seront différents. Cela évite des attaques pre-calculés de hachage, comme les rainbow tables.
|
||||||
1. Jose (Javascript Object Signing and Encryption): C’est un module JavaScript qui fournit plusieurs features qui rehausse la sécurité, particulièrement dans la manipulation des JWT (JSON Web Tokens) et autres protocols de messagerie :
|
1. Jose (Javascript Object Signing and Encryption): C’est un module JavaScript qui fournit plusieurs fonctionnalités qui rehaussent la sécurité, particulièrement dans la manipulation des JWT (JSON Web Tokens) et autres protocols de messagerie :
|
||||||
* Création et Vérification des JWTs : Jose permet de créer et vérifier facilement des JWTs pour l’authentification et autorisation.
|
* Création et Vérification des JWTs : Jose permet de créer et vérifier facilement des JWTs pour l’authentification et l'autorisation.
|
||||||
* Signature et Encryption : La bibiliothèque supporte la signature (assurant l’intégrité des données et l’authencitié) et l’encryption (assurant la confidentialité des données).
|
* Signature et Encryption : La bibiliothèque supporte la signature (assurant l’intégrité des données et l’authencitié) et l’encryption (assurant la confidentialité des données).
|
||||||
* Support de Multiples Algorithmes : Les algorithmes cryptographiques comme HMAC, RSA et ECDSA sont disponible, permettant le choix selon ce qui est le plus approprié.
|
* Support de Multiples Algorithmes : Les algorithmes cryptographiques comme HMAC, RSA et ECDSA sont disponibles, permettant le choix selon ce qui est le plus approprié.
|
||||||
* Gestion des clés : La bibiliothèque donne des outils pour la gestion des clés, comme le support pour la rotation des clés et diverses formats de clé.
|
* Gestion des clés : La bibiliothèque donne des outils pour la gestion des clés, comme le support pour la rotation des clés et diverses formats de clé.
|
||||||
* Validation de claims : Jose permet de spécifier et valider les claims comme temps d’expiration et audience.
|
1. Express Middleware : C'est un composant d'ExpressJs, un framework pour NodeJs qui facilite le développement d'applications. Le middleware est une fonction qui a accès à l'objet de requête (req), à l'objet de réponse (res) et à la fonction next dans le cycle de traitement des requêtes :
|
||||||
1. Express Middleware : Des fonctions qui sont executés pendant le cycle requête-réponse dans une application Express. Elle est utile dans la sécurite des application de ces façons :
|
* Validation et Correction des Entrées : Le middleware peut valider et corriger des requêtes pour assurer que les données sont propres et elles sont dans le format attendu, aidant à empêcher des attaques d’injection, comme des injections SQL ou XSS.
|
||||||
* Validation et Sanitization des Input : Middleware peut valider et sanitze des requêtes pour assurer que les données sont propres et elles sont dans le format attendu, aidant à empêcher des attaques d’injection, comme des injections SQL ou XSS.
|
* Authentification et Authorisation : Le middleware peut gérer l’authentification d’utilisateur (par exemple en vérifiant des tokens) et l’authorisation en assurant que les utilisateurs ont la permission d’accéder à certaines ressources, et cela aide à sécuriser les endpoints.
|
||||||
* Authentification et Authorisation : Middleware peut gérer l’authentification d’utilisateur (par exemple en checkant des tokens) et l’authorisation en assurant que les utilisateurs ont la permission d’accéder à certaines ressources, et cela aide à sécuriser les endpoints.
|
* Rate limiting : Le middleware peut limiter le nombre de requêtes un utilisateur peut faire dans un certain intervalle de temps, aidant à éviter l’abus et des attaques DOS.
|
||||||
* Rate limiting : Middleware peut limiter le nombre de requêtes un utilisateur peut faire dans un certain timeframe, aidant à éviter l’abus et des attaques DOS.
|
* Gestion de CORS : Le middleware peut gérer les settings de Cross-Origin Resource Sharing (CORS) pour contrôler quels domaines peuvent accéder les ressources, ce qui empêche des accès non-authorisés.
|
||||||
* CORS management : Middleware peut gérer les settings de Cross-Origin Resource Sharing (CORS) pour contrôler quels domaines peuvent accéder les ressources, ce qui empêche des accès non-authorisés.
|
* Les En-tête de sécurité : Le middleware peut ajouter des en-tête de sécurité HTTP aux réponses, ce qui donne une couche additionnelle de sécurité contre les attaques.
|
||||||
* Security Headers : Middleware peut ajouter des security headers HTTP aux réponse, ce qui donne une couche additionnelle de sécurité contre les attaques.
|
* Gestion des Sessions : Le middleware peut gérer les cookies de session secures et implemente les mesures comme l'expiration des cookies et secure flags pour protéger les données de session.
|
||||||
* Gestion des Sessions : Middleware peut gérer les cookies de session secures et implemente les mesures comme expiration des cookies et secure flags pour protéder les données de session.
|
|
||||||
|
|
||||||
# Annexe (diagramme de classe)
|
# Annexe (diagramme de classe)
|
||||||
|
|
||||||
|
|
|
@ -40,10 +40,12 @@ ReservedMaterial peut avoir plusieurs matériaux, mais au moins 1, alors 1..*
|
||||||
Un matérial peut être disponible en plusieurs exemplaires, mais au moins 1, alors 1..*
|
Un matérial peut être disponible en plusieurs exemplaires, mais au moins 1, alors 1..*
|
||||||
Chaque matérial appartient à 1 catégorie, alors 1
|
Chaque matérial appartient à 1 catégorie, alors 1
|
||||||
|
|
||||||
|
Dans le diagramme de classe, il faut travailler les multiplicités, alors si on prend l’exemple de l’entity User, dans les deux directions, vers Reservation ainsi que Session, on voit qu’il y a une Multiplicité de 1. Pourquoi ? Car un User est relié à une session. Il ne peut en avoir
|
||||||
|
plusieurs. Une Reservation est également relié à seulement un User, alors c’est également 1.
|
||||||
|
|
||||||
Portée
|
Portée
|
||||||
On peut avoir plusieurs types de portées pour nos attributs : public, private, protected, par exemple. On a décidé de garder tout public à part les mots de passe pour un utilisateur, qui sont privés, pour les raisons de sécurité. De cette façon, les autres classes ne pourront pas y accèder, et cela protège ces données.
|
On peut avoir plusieurs types de portées pour nos attributs : public, private, protected, par exemple. On a décidé de garder tout public à part les mots de passe pour un utilisateur, qui sont privés, pour les raisons de sécurité. De cette façon, les autres classes ne pourront pas y accèder, et cela protège ces données.
|
||||||
|
|
||||||
|
|
||||||
En plus de montrer les liens entre chaque Entity, on a aussi profité de notre diagramme de classe pour imaginer des services. Dans le slide, on voit bien comment on est censés créer chacun des services, en utilisant User comme exemple, et le code que nous avons créé pour create user. On a aussi créé un type InputRegister pour user afin de regrouper les infos nécessaires pour un User.
|
En plus de montrer les liens entre chaque Entity, on a aussi profité de notre diagramme de classe pour imaginer des services. Dans le slide, on voit bien comment on est censés créer chacun des services, en utilisant User comme exemple, et le code que nous avons créé pour create user. On a aussi créé un type InputRegister pour user afin de regrouper les infos nécessaires pour un User.
|
||||||
|
|
||||||
Le dernier diagramme sur lequel nous avons travaillé avant de nous mettre à coder, c'était le diagramme de cas d'utilisation. Après beaucoup de discussion, on voulait qu'un simple visiteur puisse regarder la catalogue, mais si la personne veut faire une réservation, il faut créer un compte et se logger. Alors c'est pour cela que le visiteur ainsi que le client loggué ait accès pour voir les matériaux sur le site. L'admin, cependant, a des droits qu'un client n'a pas - par exemple, il peut créer, mettre à jour, et supprimer des matériaux si nécessaire.
|
Le dernier diagramme sur lequel nous avons travaillé avant de nous mettre à coder, c'était le diagramme de cas d'utilisation. Après beaucoup de discussion, on voulait qu'un simple visiteur puisse regarder la catalogue, mais si la personne veut faire une réservation, il faut créer un compte et se logger. Alors c'est pour cela que le visiteur ainsi que le client loggué ait accès pour voir les matériaux sur le site. L'admin, cependant, a des droits qu'un client n'a pas - par exemple, il peut créer, mettre à jour, et supprimer des matériaux si nécessaire.
|
||||||
|
@ -52,10 +54,7 @@ Pour conclure aujourd'hui, je vous présente un schéma de notre application.
|
||||||
Le schéma commence en bas, avec le développeur, qui fait des commits avec git. Une fois commité et vérifié, le dév push sur main, ou fait un pull request. Cela déclenche les actions github, et les tests mis en place check si tout va bien. Si les tests passent, les nouvelles images sont publiées sur dockerhub, et puis on peut déclencher un webhook ou faire un fetch and deploy afin de récupérer tout ce qu'il faut pour le serveur. Côté client, notre projet utilise Next avec React, ainsi que material UI. Sur le serveur, on utilise node et express, notre BDD utilise postgreSQL, et finalement pour faire les requêtes on utilise graphQL. Notre projet est écrit en typescript, qui est transpilé en Javascript.
|
Le schéma commence en bas, avec le développeur, qui fait des commits avec git. Une fois commité et vérifié, le dév push sur main, ou fait un pull request. Cela déclenche les actions github, et les tests mis en place check si tout va bien. Si les tests passent, les nouvelles images sont publiées sur dockerhub, et puis on peut déclencher un webhook ou faire un fetch and deploy afin de récupérer tout ce qu'il faut pour le serveur. Côté client, notre projet utilise Next avec React, ainsi que material UI. Sur le serveur, on utilise node et express, notre BDD utilise postgreSQL, et finalement pour faire les requêtes on utilise graphQL. Notre projet est écrit en typescript, qui est transpilé en Javascript.
|
||||||
|
|
||||||
maximales mcd
|
maximales mcd
|
||||||
mcd - pas de FK - il y en avait pas ?
|
|
||||||
|
|
||||||
mld - enlever des cardinalités - OK
|
|
||||||
category id dans une autre couleur - OK
|
|
||||||
diagramme de séquence
|
diagramme de séquence
|
||||||
montrer un extrait de diagramme de classe
|
montrer un extrait de diagramme de classe
|
||||||
|
|
||||||
|
|