Projet réalisé par Tom et Prince
- Introduction
- Prérequis
- Installation
- Ressources
- Utiliser le service : cas nominal
- Conception
- Sécurité
- Remarques
- Références
- Contributeurs
Ce projet est une API RESTful permettant de gérer un système de réservation de terrains de badminton pour une association. L'API inclut des fonctionnalités de réservation, d'annulation, et de gestion des indisponibilités des terrains. Une ressource GraphQL est également exposée pour faciliter l'intégration future d'une application mobile.
Avant de commencer, assurez-vous que Docker et Docker Compose sont installés sur votre machine. Si vous ne les avez pas encore installés, vous pouvez suivre les instructions officielles :
-
Clonez ce dépôt :
git clone https://github.com/tombihoreau/API-Badminton.git
-
Accédez au répertoire :
cd API-Badminton
-
Le fichier
.env
à la racine doit contenir les informations suivantes :Le fichier
.env
à la racine doit contenir les informations suivantes :# Mode d'environnement ENV=dev # Nom du projet PROJECT_NAME=api-badminton # Ports pour l'accès aux services depuis l'hôte HOST_PORT_API=3001 HOST_PORT_DB=3306 HOST_PORT_ADMINER=8080 # Configuration de la base de données DB_NAME=mydb DB_USER=user DB_PASSWORD=password DB_HOST=db DB_PORT=3306 DB_ROOT_PASSWORD=root
-
Générer la clé privé
# Générer une clé privé node generatekey
-
Construisez et lancez les conteneurs Docker :
docker-compose up --build
#Cette commande construira les services Docker (API, base de données MySQL, Adminer) et démarrera tous les conteneurs.
#=========== Utilitaires Docker ===============
# Si vous avez apporté des modifications ou souhaitez reconstruire les images des service utilisé :
docker-compose build
#Pour démarrer les services (API, base de données MySQL, Adminer) sans reconstruire les images, utilisez
docker-compose up
#Pour arrêter les conteneurs et supprimer les ressources associées, utilisez :
docker-compose down
-
Migration et Seed de la Base de Données
Les migrations ne sont pas automatiquement exécutées avec
docker-compose up
, donc vous devez les exécuter manuellement. Suivez les étapes ci-dessous pour migrer et remplir la base de données avec les données nécessaires.Ouvrez un nouveau terminal et connectez-vous à votre conteneur de l'API avec la commande suivante :
docker exec -it api-badminton-api sh
Cela vous permettra d'exécuter des commandes à l'intérieur du conteneur.
Une fois dans le conteneur, exécutez les migrations avec la commande suivante :
npx sequelize-cli db:migrate #Cela appliquera les migrations de la base de données et mettra à jour la structure des tables.
Ajouter des données de seed Après avoir exécuté les migrations, vous pouvez remplir votre base de données avec des données de test en exécutant la commande suivante :
npx sequelize-cli db:seed:all #Cela ajoutera les données de test définies dans vos fichiers de seed à la base de données. #Utilitaire pour supprimer les données présentes npx sequelize-cli db:seed:undo:all
-
Accès à l'application:
Une fois que les conteneurs sont lancés avec Docker Compose, vous pouvez accéder aux différents services comme suit :
- API : L'API sera disponible à l'adresse http://localhost:3000.
- Adminer : Adminer, une interface web pour gérer la base de données, sera disponible à l'adresse http://localhost:8080
- Connexion à Adminer
Pour vous connecter à la base de données MySQL via Adminer, entrez les informations suivantes dans le formulaire de conn
-
Serveur :
db
(nom du service dans Docker Compose) -
Nom d'utilisateur :
user
-
Mot de passe :
password
-
Base de données :
mydb
Avec ces informations, vous pourrez gérer et interagir avec votre base de données MySQL directement depuis Adminer.
Ressource | URL | Méthodes HTTP | Paramètres d’URL/Variations | Qui peut faire ça | Commentaires |
---|---|---|---|---|---|
Authentification | /login |
POST |
Aucun | Administrateur·ice | Permet à un administrateur·ice de se connecter pour gérer les ressources protégées. |
Création de compte | /register |
POST |
Aucun | Tout utilisateur | |
Liste des créneaux d'un terrain | /fields/:id/slots |
GET |
{id} : Identifiant du |
Tout utilisateur | Récupère les créneaux disponibles pour une date et un terrain spécifiques. |
Réservation | /reservations |
POST |
Aucun | Tout utilisateur connecté ( pour savoir qui a reservé) | Permet de créer une réservation en fournissant un pseudo (en etant connecté) et les détails du créneau. |
Annulation | /reservations/{id} |
DELETE |
{id} : Identifiant unique de la réservation |
Tout utilisateur | Permet d'annuler une réservation existante. |
Rendre terrain indispo ou dispo | /fields/:id | PATCH |
{id} : Identifiant du terrain |
Administrateur·ice | Rend un terrain indisponible pour une période donnée. Protégé par authentification. |
Liste des réservations | /reservations |
GET |
Aucun |
Administrateur·ice | Permet de lister les réservations existantes. |
Liste des réservations d'un utilisateur | /users/:userId/reservations |
GET |
Aucun | Tout utilisateur | Permet de lister les réservations existantes d'un utilisateur. |
- But : Se connecter à l'interface administrateur avec les identifiants réservés.
- Préconditions : L'admin doit connaître son pseudo et mot de passe.
- Étapes :
- L'administrateur envoie une requête
POST
à l'URL/auth/login
avec les identifiants. - L'API renvoie un
JWT_TOKEN
pour authentifier l'administrateur.
- L'administrateur envoie une requête
Exemple de requête :
{
"username": "admybad",
"password": "astrongpassword."
}
Réponse attendue :
{
"access_token": "<JWT_TOKEN>"
}
2. Rendre un terrain indisponible
-
But : Rendre un terrain temporairement indisponible pour les réservations (par exemple, terrain B).
-
Préconditions : L'administrateur doit être connecté avec le token d'authentification.
-
Étapes :
- L'administrateur envoie une requête
POST
à l'URL/terrain/{terrain}/indisponible
pour mettre un terrain en mode indisponible. - Le terrain sélectionné devient indisponible pour toute nouvelle réservation.
- L'administrateur envoie une requête
Exemple de requête :
{
"terrain_id": "exemple id "
}
Réponse attendue :
{
"message": "Le terrain est maintenant indisponible."
}
1. Lister les créneaux disponibles d'un terrain :
Envoyer une requête GET
à l'URL /fields/:id/slots.
#======= présicer l'id du terrain
Réponse attendue :
// ========= Ceci est un exemple
{
"_embedded": {
"slots": [
{
"_links": {
"self": {
"href": "http://localhost:3000/fields/14/slots/3",
"templated": false
}
},
]
}
}
2. Réserver un terrain :
Envoyer une requête POST
à l'URL /reservations
avec les informations nécessaires .
NB: Il faut être connecté pour cela
Exemple :
{
"slotId": 0,
"date": "2024-12-11"
}
Réponse attendue :
//========== Ceci est exemple
{
"message": "Réservation réussie pour le terrain A le 27 novembre 2024 à 10h00."
}
5 . Annuler une réservation :
-
But : Annuler une réservation existante.
-
Préconditions : L'utilisateur doit disposer d'une réservation à annuler et être connecté.
-
Étapes :
- L'utilisateur envoie une requête
DELETE
à l'URL/reservations/:id
avec l'ID de la réservation à annuler. - L'API confirme l'annulation.
- L'utilisateur envoie une requête
Exemple :
{
"reservation_id": 1
}
Réponse attendue
{
"message": "La réservation a été annulée avec succès."
}
Nom de la donnée | Type | Description | Contraintes |
---|---|---|---|
pseudo | string |
Le pseudo de l'utilisateur ou de l'administrateur. | Obligatoire pour s'identifier. Doit être unique |
password | string |
Le mot de passe de l'utilisateur ou administrateur (lors de la connexion). | Obligatoire lors de la connexion, doit être sécurisé. |
role | enum |
Role de l'utilisateur | Doit être user ou admin |
date | string (date) |
La date de la réservation ou de la disponibilité. | Doit être au format YYYY-MM-DD . |
name(terrain) | string |
Le nom du terrain réservé ou à rendre indisponible. | Doit être une des valeurs suivantes :A , B , C , D . |
time | string (time) |
L'heure du créneau réservé. | Doit être au format HH:mm . |
reservationId | integer |
L'identifiant de la réservation. | Doit être unique pour chaque réservation. |
slotId | integer |
L'identifiant du creneau | Doit être unique |
fieldId | integer |
L'identifiant du terrain | Doit être unique |
userId | integer |
L'identifiant de l'utilisateur | Doit être unique |
access_token | string |
Le jeton JWT utilisé pour authentifier un administrateur. | Obligatoire pour les actions administratives, valide pendant un certain temps. |
isAvailable | boolean |
Indique si un créneau est disponible ou non. | true si disponible, false si non. |
available | string |
L'état d'un terrain : disponible, indisponible. | Peut être available ou unavailable . |
reasonUnavailable | string |
Raison de l'indisponibilité | Est un string |
createdAt | datetime |
Date et heure de la création de la réservation ou de la modification d'un terrain. | Automatiquement généré par le système lors de la création. |
updatedAt | datetime |
Date et heure de la dernière modification. | Automatiquement généré par le système lors de la modification. |
- Nom de la donnée : Le nom de la donnée ou du champ dans la base de données ou dans l'API.
- Type : Le type de la donnée, comme
string
,boolean
,integer
,datetime
, etc. - Description : Une brève description du rôle ou de l'objectif de la donnée dans l'API ou le système.
- Contraintes : Les restrictions, règles ou formats que la donnée doit respecter pour être valide. Cela inclut des informations sur la longueur des chaînes de caractères, le format des dates, ou des valeurs acceptées.
L'authentification pour les actions sensibles (comme rendre un terrain indisponible) est protégée par un système de JWT. Un administrateur doit se connecter avec un pseudo et un mot de passe spécifiques. Une fois authentifié, l'administrateur reçoit un jeton JWT qui permet d'accéder aux ressources protégées. Ce jeton est inclus dans les en-têtes des requêtes pour garantir que seul l'administrateur peut effectuer certaines actions.
Une clé secrète forte est utilisée pour signer les JSON Web Tokens (JWT). Cette clé est conservée de manière sécurisée et n'est jamais exposée. Elle est utilisée pour garantir l'intégrité et la validité des jetons JWT émis lors de l'authentification de l'administrateur.
Les routes protégées par des actions administratives (comme rendre un terrain indisponible) sont uniquement accessibles par des utilisateurs authentifiés. Cela empêche les utilisateurs non autorisés de modifier l'état du système.
Pour éviter les attaques par injection SQL ou autres types de manipulation de données, toutes les données envoyées par les utilisateurs sont soigneusement vérifiées et validées avant d'être traitées.Par exemple :
- Le pseudo de l'utilisateur est validé avant d'être utilisé dans une réservation.
- Les dates et heures sont également vérifiées pour s'assurer qu'elles respectent les formats attendus.
Les utilisateurs normaux ne peuvent pas accéder aux actions administratives. Par exemple, la réservation d'un terrain est autorisée pour tous les utilisateurs, mais seule l'administrateur peut rendre un terrain indisponible. Cela garantit que les actions sensibles sont réservées à des personnes spécifiquement autorisées.
Les mots de passe sont stockés de manière sécurisée (hachés et salés) dans la base de données. Même si la base de données est compromise, les mots de passe restent protégés.
Afin de protéger l'application contre les attaques par brute-force , nous avons mis en place un mécanisme de rate-limiting pour limiter le nombre de tentatives de connexion sur une période donnée. Cela empêche les attaquants de tenter de deviner les informations d'identification de l'administrateur en effectuant trop de tentatives consécutives. Par exemple, un utilisateur est limité à 5 tentatives de connexion par adresse IP toutes les 15 minutes. Un message d'erreur spécifique est renvoyé lorsque la limite est dépassée, assurant une meilleure protection contre les attaques par brute-force .
Des garde-fous ont été ajoutés dans les routes sensibles pour assurer que les utilisateurs ne peuvent pas réserver un créneau qui est déjà indisponible ou effectuer des actions sur des terrains invalides . Ce mécanisme de validation vérifie que les données envoyées par les utilisateurs respectent les critères définis (par exemple, vérification de la disponibilité des créneaux, validation des ID de terrain, etc.). Cela permet de garantir l'intégrité des données et de prévenir toute tentative de manipulation des utilisateurs non autorisés ou de mauvaises pratiques.
- La principale difficulté rencontrée lors de la conception du système a été de gérer la logique d'indisponibilité temporaire des terrains. Il est essentiel de s'assurer que la gestion de l'indisponibilité ne bloque pas les utilisateurs qui pourraient vouloir réserver d'autres terrains.
- L'utilisation de JWT pour sécuriser les ressources et contrôler l'accès a été une solution adéquate pour gérer l'authentification et les permissions d'accès aux différentes ressources.
- Documentation officielle de Node.js - Documentation officielle pour Node.js.
- OpenAPI Specification - Spécification standard pour la description d'API RESTful.
- Tutoriels de Sequelize pour l’ORM - Tutoriels pour l'utilisation de Sequelize, un ORM pour Node.js.
- JWT.io : https://jwt.io/ - Documentation sur l'utilisation des JSON Web Tokens.
- Express.js documentation : https://expressjs.com/ - Documentation pour le framework Node.js, utile pour implémenter des API REST sécurisées.
- MDN Web Docs : https://developer.mozilla.org/ - Pour des exemples sur l’utilisation des requêtes HTTP et la gestion des API.
- RESTful API Design : https://www.restapitutorial.com/ - Un tutoriel détaillé sur la conception d'API REST.
- Docker Documentation : https://docs.docker.com/ - Documentation officielle sur Docker, un outil permettant de conteneuriser les applications pour une gestion plus simple des environnements.
- Protection contre les attaques par force brute : Implémentation de stratégies pour sécuriser les logins et prévenir les attaques par force brute. OWASP Brute Force Protection
- Rate Limiting : Mise en œuvre de la limitation du taux de requêtes pour éviter les abus et protéger les ressources des serveurs. Express-rate-limit pour Node.js.
- White-list / Black-list : Implémentation de listes blanches et noires pour contrôler l'accès aux ressources, en autorisant uniquement certaines adresses IP ou utilisateurs.
- Gestion des Secrets / Variables d’Environnement : Pour une gestion sécurisée des variables d'environnement et des secrets dans des applications Node.js. Utilisation d'outils comme dotenv.
- Node.js Process Management : Gestion des processus Node.js via des outils comme PM2, un gestionnaire de processus permettant de maintenir les applications Node en fonctionnement continu, même après un crash.
- Tom BIHOREAU
- Prince de Gloire ONDONGO