Projet Corpo Padel
Documentation complÚte pour votre projet pédagogique
Test et Sécurité - Qualité Logiciel - Polytech Tours
đ Cahier des charges
Document complet avec toutes les spécifications fonctionnelles, techniques et de sécurité.
- Spécifications fonctionnelles détaillées
- Architecture technique complĂšte
- Exigences de sécurité
- Guide des tests
- CritÚres d'évaluation
đ» Starter Kit
Kit de dĂ©marrage prĂȘt Ă l'emploi avec backend FastAPI et frontend VueJS configurĂ©s.
- Authentification JWT fonctionnelle
- Anti-brute force implémenté
- Structure complĂšte backend/frontend
- Tests unitaires et E2E
- README multi-OS
đŻ Informations pratiques
Durée du projet : 12 heures (développement + tests + rapport)
Stack imposée : VueJS 3 + FastAPI + SQLite
Repository GitHub : https://github.com/AndreyPividori/PolytechTours-PadelStarterKit
Compte de test : admin@padel.com / Admin@2025!
đ Cahier des Charges - Application Corpo Padel
CAHIER DES CHARGES
Application de Gestion de Tournois
Corpo Padel
Table des MatiĂšres
- 1. Introduction
- 1.1 Contexte du projet
- 1.2 Objectifs pédagogiques
- 1.3 Glossaire
- 2. Spécifications Fonctionnelles
- 2.1 Gestion des rĂŽles et authentification
- 2.2 Page d'accueil
- 2.3 Planning
- 2.4 Matchs
- 2.5 Résultats
- 2.6 Administration
- 2.7 Profil
- 3. Spécifications Techniques
- 3.1 Architecture systĂšme
- 3.2 Stack technologique
- 3.3 ModÚle de données
- 3.4 API REST
- 3.5 Formats et contraintes
- 4. Exigences de Sécurité
- 4.1 Authentification et autorisation
- 4.2 Protection contre les attaques
- 4.3 Validation des données
- 4.4 Gestion des mots de passe
- 5. Spécifications des Tests
- 5.1 Tests unitaires
- 5.2 Tests End-to-End
- 5.3 Tests de sécurité OWASP
- 6. Livrables et Ăvaluation
- 6.1 Livrables attendus
- 6.2 CritÚres d'évaluation
- 7. Annexes
1. Introduction
1.1 Contexte du projet
Ce projet pédagogique s'inscrit dans le cadre d'un cours de test et sécurité pour étudiants ingénieurs. L'objectif est de développer une application web complÚte pour gérer les tournois de padel corporatifs d'une entreprise.
Travail : De 4 Ă 6 personnes par groupe
Modalité : Projet noté avec rendu de code et rapport
L'application doit permettre aux administrateurs de gérer l'ensemble des tournois (planification, équipes, résultats) et aux joueurs de suivre leur progression et leurs prochains matchs.
1.2 Objectifs pédagogiques
Ce projet vous permettra de mettre en pratique les compétences suivantes :
- Développement Full-Stack : Créer une application complÚte avec frontend VueJS et backend FastAPI
- Tests automatisés : Implémenter des tests unitaires (Python) et End-to-End (Cypress)
- SĂ©curitĂ© applicative : Mettre en Ćuvre les bonnes pratiques de sĂ©curitĂ© (validation, XSS, authentification)
- Gestion de base de données : Concevoir et manipuler un schéma de données cohérent
- Documentation : Rédiger un rapport technique complet
1.3 Glossaire
| Terme | Définition |
|---|---|
| Padel | Sport de raquette combinant des éléments du tennis et du squash, joué en double sur un court fermé |
| Corpo | Abréviation de "corporatif", désigne un tournoi inter-entreprises |
| Poule | Groupe de 6 équipes qui s'affrontent dans un systÚme de championnat |
| Ăquipe | BinĂŽme de joueurs reprĂ©sentant une entreprise |
| ĂvĂ©nement | CrĂ©neau de jeu regroupant 1 Ă 3 matchs Ă une date et heure donnĂ©es |
| Piste | Terrain de padel identifié par un numéro |
| Saison | Période annuelle d'un tournoi (du 1er septembre au 31 août) |
| Licence | NumĂ©ro d'identification unique d'un joueur (format : LXXXXXX oĂč X est un chiffre) |
2. Spécifications Fonctionnelles
2.1 Gestion des rĂŽles et authentification
2.1.1 Les trois rĂŽles
| RĂŽle | Description | AccĂšs |
|---|---|---|
| Visiteur | Utilisateur non authentifié | Page d'accueil uniquement |
| Joueur | Joueur inscrit dans au moins une équipe | Toutes les pages sauf Administration |
| Administrateur | Bénévole gestionnaire du tournoi | Toutes les pages avec droits d'édition |
2.1.2 Processus d'authentification
Durée de validité du token : 24 heures
Stockage : localStorage cÎté client
Flux d'authentification :
- L'utilisateur saisit son email et mot de passe
- Le backend vérifie les credentials
- Si valides, un JWT est généré contenant : user_id, email, role, exp
- Le token est retourné au client et stocké
- Chaque requĂȘte ultĂ©rieure inclut le token dans le header Authorization
2.1.3 Mécanisme anti-brute force
RĂšgles :
- Maximum 5 tentatives de connexion échouées
- Blocage du compte pendant 30 minutes aprÚs 5 échecs
- Le compteur se réinitialise aprÚs une connexion réussie
- L'utilisateur doit recevoir un message clair indiquant le nombre de tentatives restantes
- AprÚs blocage, afficher le temps restant avant déblocage
Implémentation technique :
Table: login_attempts - email (TEXT) - attempts_count (INTEGER) - last_attempt (DATETIME) - locked_until (DATETIME nullable)
2.2 Page d'accueil
2.2.1 Objectif
Page d'atterrissage simple et accueillante présentant l'application.
2.2.2 Contenu
- Une image ou banniÚre évoquant le padel corporatif
- Un message de bienvenue expliquant briĂšvement l'application
- Un bouton "Se connecter" bien visible
2.2.3 Comportement
| RĂŽle | Affichage |
|---|---|
| Visiteur | Message de bienvenue + Bouton "Se connecter" |
| Joueur / Admin | Message personnalisé "Bienvenue [Prénom]" + AccÚs au menu complet |
2.3 Planning
2.3.1 Objectif
Visualiser tous les événements de la saison sous forme de calendrier.
2.3.2 Vue calendrier
Affichage :
- Vue mensuelle par défaut
- Possibilité de naviguer entre les mois
- Chaque jour contenant un événement est marqué visuellement
- Clic sur un jour affiche les événements de cette date
2.3.3 Structure d'un événement
| Champ | Type | Format | Obligatoire |
|---|---|---|---|
| Date | DATE | YYYY-MM-DD | Oui |
| Heure | TIME | HH:MM (24h) | Oui |
| Matchs | Liste | 1 Ă 3 matchs | Oui (min 1) |
2.3.4 Structure d'un match
| Champ | Type | Format | Obligatoire |
|---|---|---|---|
| Numéro de piste | INTEGER | 1-10 | Oui |
| Ăquipe 1 | RĂ©fĂ©rence | ID Ă©quipe | Oui |
| Ăquipe 2 | RĂ©fĂ©rence | ID Ă©quipe | Oui |
| Statut | ENUM | A_VENIR, TERMINE, ANNULE | Oui |
| Score équipe 1 | TEXT | Ex: "6-4, 6-3" | Non |
| Score équipe 2 | TEXT | Ex: "4-6, 3-6" | Non |
2.3.5 Droits par rĂŽle
Joueur
- Voir le calendrier complet
- Cliquer sur un événement pour voir les détails (date, heure, matchs)
- Voir uniquement les Ă©vĂ©nements oĂč ils sont impliquĂ©s (filtre automatique)
- Option pour voir tous les événements (décocher le filtre)
Administrateur
- Toutes les actions du joueur
- Ajouter un événement (formulaire avec date, heure, sélection des matchs)
- Modifier un événement existant
- Supprimer un événement
2.3.6 Formulaire d'ajout d'événement (Admin)
Champs :
- Date (obligatoire, datepicker, min: aujourd'hui)
- Heure (obligatoire, format HH:MM)
- Nombre de matchs (1, 2 ou 3)
- Pour chaque match :
- Numéro de piste (select 1-10)
- Ăquipe 1 (select parmi les Ă©quipes)
- Ăquipe 2 (select parmi les Ă©quipes)
Validations :
- La date doit ĂȘtre >= date du jour
- L'heure doit ĂȘtre au format HH:MM (00:00 Ă 23:59)
- Deux matchs ne peuvent pas utiliser la mĂȘme piste pour le mĂȘme Ă©vĂ©nement
- Une équipe ne peut jouer qu'un seul match par événement
2.4 Matchs
2.4.1 Objectif
Afficher la liste des matchs Ă venir dans les 30 prochains jours sous forme de liste.
2.4.2 Affichage par défaut
| Information | Description |
|---|---|
| Date et heure | Format : "Lundi 15 janvier 2025 Ă 19:30" |
| Piste | Numéro de piste |
| Ăquipes | Nom entreprise Ă©quipe 1 vs Nom entreprise Ă©quipe 2 |
| Joueurs | Prénom Nom de chaque joueur des 2 équipes |
| Statut | Badge coloré : à venir (bleu), Annulé (rouge) |
2.4.3 Filtre
Pour les joueurs
- Par défaut : Affiche uniquement les matchs de leur(s) équipe(s)
- Option : Case à cocher "Voir tous les matchs" pour désactiver le filtre
Pour les administrateurs
- Par défaut : Affiche tous les matchs
- Options de filtre :
- Par entreprise
- Par poule
- Par statut
2.4.4 Actions administrateur
Ajouter un match
Formulaire avec les champs suivants :
- Date (YYYY-MM-DD, >= aujourd'hui)
- Heure (HH:MM)
- Piste (1-10)
- Ăquipe 1 (select)
- Ăquipe 2 (select)
Modifier un match
- Modifier la date/heure (si statut = A_VENIR)
- Changer la piste
- Modifier le statut (A_VENIR â ANNULE)
- Saisir les scores (si match terminé)
Supprimer un match
- Possible uniquement si statut = A_VENIR
- Confirmation obligatoire
2.5 Résultats
2.5.1 Objectif
Consulter les résultats des matchs terminés et le classement général.
2.5.2 Vue des résultats personnels (Joueur)
Affichage :
- Liste chronologique (du plus récent au plus ancien)
- Uniquement les matchs avec statut = TERMINE
- Filtre : "Depuis le début de la saison" par défaut
Informations par match :
| Champ | Format |
|---|---|
| Date | DD/MM/YYYY |
| Adversaires | Prénom Nom (Entreprise) |
| Score | 6-4, 6-3 (victoire en vert, défaite en rouge) |
| Piste | Numéro |
2.5.3 Classement général des entreprises
- Victoire : 3 points
- Défaite : 0 point
- Match annulé : ne compte pas
RĂšgles de classement :
- Total de points
- En cas d'égalité : nombre de victoires
- En cas d'égalité : différence de sets gagnés/perdus
- En cas d'égalité : ordre alphabétique
| Position | Entreprise | Matchs joués | Victoires | Défaites | Points |
|---|---|---|---|---|---|
| 1 | Entreprise A | 10 | 8 | 2 | 24 |
| 2 | Entreprise B | 10 | 6 | 4 | 18 |
2.5.4 Format des scores
Exemple : "6-4, 3-6, 7-5"
RĂšgles de validation :
- Chaque set doit ĂȘtre au format "X-Y" oĂč X et Y sont des entiers
- Le vainqueur d'un set doit avoir au moins 6 jeux
- Si un set se termine 7-X, X doit ĂȘtre <= 5
- Si un set se termine 7-6, c'est un tie-break
- Un match se joue au meilleur de 3 sets (2 ou 3 sets maximum)
2.6 Administration
2.6.1 Gestion des joueurs
Ajouter un joueur
Formulaire :
| Champ | Type | Validation | Obligatoire |
|---|---|---|---|
| Nom | TEXT | 2-50 caractĂšres, lettres et espaces uniquement | Oui |
| Prénom | TEXT | 2-50 caractÚres, lettres et espaces uniquement | Oui |
| Entreprise | TEXT | 2-100 caractĂšres | Oui |
| N° de licence | TEXT | Format : LXXXXXX (L suivi de 6 chiffres) | Oui |
| Format email valide, unique dans la base | Oui |
Modifier un joueur
- Tous les champs sauf le numéro de licence et l'email
- Validation identique à la création
Supprimer un joueur
- Possible uniquement si le joueur n'est dans aucune équipe active
- Confirmation obligatoire avec message : "Attention, cette action est irréversible"
2.6.2 Gestion des équipes
Structure d'une équipe
| Champ | Type | Description |
|---|---|---|
| Entreprise | TEXT | Nom de l'entreprise représentée |
| Joueur 1 | Référence | ID du premier joueur |
| Joueur 2 | Référence | ID du second joueur |
| Poule | Référence | ID de la poule (nullable) |
RĂšgles de validation
- Les deux joueurs doivent appartenir Ă la mĂȘme entreprise
- Un joueur ne peut ĂȘtre que dans une seule Ă©quipe par saison
- Une Ă©quipe ne peut ĂȘtre dans qu'une seule poule
Actions
- Créer : Sélectionner entreprise + 2 joueurs
- Modifier : Changer les joueurs (si aucun match joué)
- Supprimer : Possible uniquement si aucun match n'a été joué
2.6.3 Gestion des poules
Structure d'une poule
| Champ | Type | Description |
|---|---|---|
| Identifiant | TEXT | Ex: "Poule A", "Poule B" |
| Ăquipes | Liste | Exactement 6 Ă©quipes |
Actions
- Créer : Nommer la poule + sélectionner 6 équipes
- Modifier : Changer les Ă©quipes (toutes les Ă©quipes doivent ĂȘtre sans matchs jouĂ©s)
- Supprimer : Possible uniquement si aucun match n'a été joué dans cette poule
2.6.4 Gestion des comptes
Créer un compte pour un joueur
Prérequis : Le joueur doit déjà exister dans la base
Processus :
- Sélectionner un joueur existant sans compte
- Le systÚme génÚre automatiquement :
- Email : celui du joueur
- Mot de passe temporaire : généré aléatoirement
- RÎle : JOUEUR par défaut
- Afficher le mot de passe temporaire (une seule fois)
- L'utilisateur devra changer son mot de passe Ă la premiĂšre connexion
Réinitialiser un mot de passe
- Génération d'un nouveau mot de passe temporaire
- Affichage une seule fois Ă l'admin
- Changement obligatoire Ă la prochaine connexion
2.7 Profil
2.7.1 Objectif
Permettre Ă chaque utilisateur de consulter et modifier ses informations personnelles.
2.7.2 Informations affichées
| Champ | Modifiable | Supprimable | Format |
|---|---|---|---|
| Nom | Oui | Non | 2-50 caractĂšres |
| Prénom | Oui | Non | 2-50 caractÚres |
| Photo de profil | Oui | Oui | JPG/PNG, max 2MB |
| Date de naissance | Oui | Oui | YYYY-MM-DD |
| Oui | Non | Format email valide | |
| N° de licence | Non | Non | Lecture seule |
2.7.3 RĂšgles de validation
- Nom/Prénom : Lettres, espaces, tirets et apostrophes uniquement
- Date de naissance :
- L'utilisateur doit avoir au moins 16 ans
- Date ne peut pas ĂȘtre dans le futur
- Email :
- Format valide (regex standard)
- Unique dans la base de données
- Photo :
- Formats acceptés : .jpg, .jpeg, .png
- Taille maximale : 2MB
- Dimensions recommandées : 400x400px
2.7.4 Changement de mot de passe
Formulaire :
- Mot de passe actuel (obligatoire)
- Nouveau mot de passe (obligatoire)
- Confirmation du nouveau mot de passe (obligatoire)
RĂšgles de validation :
- Le mot de passe actuel doit ĂȘtre correct
- Le nouveau mot de passe doit respecter la politique de sécurité (voir section 4.4)
- Les deux nouveaux mots de passe doivent correspondre
- Le nouveau mot de passe doit ĂȘtre diffĂ©rent de l'ancien
3. Spécifications Techniques
3.1 Architecture systĂšme
3.1.1 Architecture globale
âââââââââââââââââââââââââââââââââââââââââââââââ
â CLIENT (Browser) â
â â
â ââââââââââââââââââââââââââââââââââââââ â
â â VueJS 3.x â â
â â - Vue Router â â
â â - Axios (HTTP client) â â
â â - TailwindCSS â â
â ââââââââââââââââââââââââââââââââââââââ â
ââââââââââââââââââââŹâââââââââââââââââââââââââââ
â HTTP/HTTPS
â REST API
ââââââââââââââââââââŒâââââââââââââââââââââââââââ
â SERVER â
â â
â ââââââââââââââââââââââââââââââââââââââ â
â â FastAPI (Python 3.11+) â â
â â - JWT Authentication â â
â â - Pydantic Validation â â
â â - SQLAlchemy ORM â â
â â - Password Hashing (bcrypt) â â
â ââââââââââââââââââŹââââââââââââââââââââ â
â â â
â ââââââââââââââââââŒââââââââââââââââââââ â
â â SQLite Database â â
â â - File: padel_corpo.db â â
â ââââââââââââââââââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââââââ
3.1.2 Stack technologique imposée
| Composant | Technologie | Version minimale |
|---|---|---|
| Frontend | VueJS | 3.3.x |
| Routing | Vue Router | 4.x |
| HTTP Client | Axios | 1.x |
| CSS Framework | TailwindCSS | 3.x |
| Backend | FastAPI | 0.104.x |
| Python | Python | 3.11+ |
| ORM | SQLAlchemy | 2.x |
| Database | SQLite | 3.x |
| Tests Backend | Pytest | 7.x |
| Tests E2E | Cypress | 13.x |
3.2 ModÚle de données
3.2.1 Diagramme ERD
ââââââââââââââââââââ ââââââââââââââââââââ
â users â â players â
âââââââââââââââââââ†ââââââââââââââââââââ€
â id (PK) ââââââââââ¶â id (PK) â
â email (UNIQUE) â â first_name â
â password_hash â â last_name â
â role â â company â
â is_active â â license_number â
â created_at â â birth_date â
ââââââââââââââââââââ â photo_url â
â user_id (FK) â
âââââââŹâââââââââââââ
â
ââââââââââââŽâââââââââââ
â â
⌠âŒ
ââââââââââââââââââââ ââââââââââââââââââââ
â teams â â matches â
âââââââââââââââââââ†ââââââââââââââââââââ€
â id (PK) â â id (PK) â
â company âââââ team1_id (FK) â
â player1_id (FK) â â team2_id (FK) â
â player2_id (FK) â â event_id (FK) â
â pool_id (FK) â â court_number â
ââââââââŹââââââââââââ â status â
â â score_team1 â
â â score_team2 â
â ââââââââââââââââââââ
âŒ
ââââââââââââââââââââ ââââââââââââââââââââ
â pools â â events â
âââââââââââââââââââ†ââââââââââââââââââââ€
â id (PK) â â id (PK) â
â name (UNIQUE) â â event_date â
â created_at â â event_time â
ââââââââââââââââââââ â created_at â
ââââââââââââââââââââ
ââââââââââââââââââââââââ
â login_attempts â
ââââââââââââââââââââââââ€
â id (PK) â
â email â
â attempts_count â
â last_attempt â
â locked_until â
ââââââââââââââââââââââââ
3.2.2 Schéma SQL détaillé
-- Table users
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL CHECK(role IN ('JOUEUR', 'ADMINISTRATEUR')),
is_active BOOLEAN DEFAULT TRUE,
must_change_password BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Table players
CREATE TABLE players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
company TEXT NOT NULL,
license_number TEXT NOT NULL UNIQUE,
birth_date DATE,
photo_url TEXT,
user_id INTEGER UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
CONSTRAINT chk_license_format CHECK(license_number GLOB 'L[0-9][0-9][0-9][0-9][0-9][0-9]')
);
-- Table pools
CREATE TABLE pools (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Table teams
CREATE TABLE teams (
id INTEGER PRIMARY KEY AUTOINCREMENT,
company TEXT NOT NULL,
player1_id INTEGER NOT NULL,
player2_id INTEGER NOT NULL,
pool_id INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (player1_id) REFERENCES players(id) ON DELETE CASCADE,
FOREIGN KEY (player2_id) REFERENCES players(id) ON DELETE CASCADE,
FOREIGN KEY (pool_id) REFERENCES pools(id) ON DELETE SET NULL,
CONSTRAINT chk_different_players CHECK(player1_id != player2_id)
);
-- Table events
CREATE TABLE events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_date DATE NOT NULL,
event_time TIME NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Table matches
CREATE TABLE matches (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_id INTEGER NOT NULL,
team1_id INTEGER NOT NULL,
team2_id INTEGER NOT NULL,
court_number INTEGER NOT NULL CHECK(court_number BETWEEN 1 AND 10),
status TEXT NOT NULL DEFAULT 'A_VENIR' CHECK(status IN ('A_VENIR', 'TERMINE', 'ANNULE')),
score_team1 TEXT,
score_team2 TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE,
FOREIGN KEY (team1_id) REFERENCES teams(id) ON DELETE CASCADE,
FOREIGN KEY (team2_id) REFERENCES teams(id) ON DELETE CASCADE,
CONSTRAINT chk_different_teams CHECK(team1_id != team2_id)
);
-- Table login_attempts
CREATE TABLE login_attempts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL,
attempts_count INTEGER DEFAULT 0,
last_attempt TIMESTAMP,
locked_until TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Indexes pour performances
CREATE INDEX idx_players_user_id ON players(user_id);
CREATE INDEX idx_teams_players ON teams(player1_id, player2_id);
CREATE INDEX idx_teams_pool ON teams(pool_id);
CREATE INDEX idx_matches_event ON matches(event_id);
CREATE INDEX idx_matches_teams ON matches(team1_id, team2_id);
CREATE INDEX idx_matches_status ON matches(status);
CREATE INDEX idx_events_date ON events(event_date);
CREATE INDEX idx_login_attempts_email ON login_attempts(email);
3.3 API REST
3.3.1 Convention de nommage
- Base URL :
http://localhost:8000/api/v1 - Format : JSON uniquement
- Authentification : Bearer Token (JWT) dans le header Authorization
3.3.2 Endpoints - Authentification
POST /auth/login
Description : Authentifier un utilisateur
Request Body :
{
"email": "john.doe@example.com",
"password": "SecureP@ssw0rd"
}
Response Success (200) :
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"user": {
"id": 1,
"email": "john.doe@example.com",
"role": "JOUEUR",
"must_change_password": false
}
}
Response Error (401) :
{
"detail": "Email ou mot de passe incorrect",
"attempts_remaining": 3
}
Response Locked (403) :
{
"detail": "Compte bloqué",
"locked_until": "2025-10-18T15:30:00",
"minutes_remaining": 28
}
POST /auth/change-password
Description : Changer son mot de passe
Authentification : Requise
Request Body :
{
"current_password": "OldP@ssw0rd",
"new_password": "NewSecureP@ssw0rd123!",
"confirm_password": "NewSecureP@ssw0rd123!"
}
Response Success (200) :
{
"message": "Mot de passe modifié avec succÚs"
}
POST /auth/logout
Description : Déconnecter l'utilisateur
Authentification : Requise
3.3.3 Endpoints - Joueurs
GET /players
Description : Liste tous les joueurs (Admin uniquement)
Authentification : Admin
Response (200) :
{
"players": [
{
"id": 1,
"first_name": "John",
"last_name": "Doe",
"company": "Tech Corp",
"license_number": "L123456",
"birth_date": "1990-05-15",
"photo_url": null,
"has_account": true
}
],
"total": 1
}
GET /players/{id}
Description : Détails d'un joueur
Authentification : Requise
POST /players
Description : Créer un joueur (Admin uniquement)
Authentification : Admin
Request Body :
{
"first_name": "John",
"last_name": "Doe",
"company": "Tech Corp",
"license_number": "L123456",
"email": "john.doe@example.com"
}
Validation :
first_name: 2-50 caractĂšres, lettres et espaceslast_name: 2-50 caractĂšres, lettres et espacescompany: 2-100 caractĂšreslicense_number: Format LXXXXXX (unique)email: Format email valide (unique)
PUT /players/{id}
Description : Modifier un joueur (Admin uniquement)
DELETE /players/{id}
Description : Supprimer un joueur (Admin uniquement)
Condition : Le joueur ne doit appartenir à aucune équipe active
3.3.4 Endpoints - Ăquipes
GET /teams
Description : Liste toutes les équipes
Query Params :
pool_id(optional): Filtrer par poulecompany(optional): Filtrer par entreprise
Response (200) :
{
"teams": [
{
"id": 1,
"company": "Tech Corp",
"players": [
{
"id": 1,
"first_name": "John",
"last_name": "Doe"
},
{
"id": 2,
"first_name": "Jane",
"last_name": "Smith"
}
],
"pool": {
"id": 1,
"name": "Poule A"
}
}
],
"total": 1
}
POST /teams
Description : Créer une équipe (Admin uniquement)
Request Body :
{
"company": "Tech Corp",
"player1_id": 1,
"player2_id": 2,
"pool_id": 1
}
Validations :
- Les deux joueurs doivent appartenir Ă la mĂȘme entreprise
- Chaque joueur ne peut ĂȘtre que dans une Ă©quipe par saison
player1_idâplayer2_id
PUT /teams/{id}
Description : Modifier une équipe (Admin uniquement)
Condition : Aucun match joué
DELETE /teams/{id}
Description : Supprimer une équipe (Admin uniquement)
Condition : Aucun match joué
3.3.5 Endpoints - Poules
GET /pools
Description : Liste toutes les poules
Response (200) :
{
"pools": [
{
"id": 1,
"name": "Poule A",
"teams_count": 6,
"teams": [...]
}
]
}
POST /pools
Description : Créer une poule (Admin uniquement)
Request Body :
{
"name": "Poule A",
"team_ids": [1, 2, 3, 4, 5, 6]
}
Validation :
- Exactement 6 équipes
- Nom unique
PUT /pools/{id}
Description : Modifier une poule (Admin uniquement)
Condition : Aucun match joué dans la poule
DELETE /pools/{id}
Description : Supprimer une poule (Admin uniquement)
Condition : Aucun match joué dans la poule
3.3.6 Endpoints - ĂvĂ©nements
GET /events
Description : Liste tous les événements
Query Params :
start_date(optional): Date de début (YYYY-MM-DD)end_date(optional): Date de fin (YYYY-MM-DD)month(optional): Mois spécifique (YYYY-MM)
Response (200) :
{
"events": [
{
"id": 1,
"event_date": "2025-11-15",
"event_time": "19:30",
"matches": [
{
"id": 1,
"court_number": 1,
"team1": {...},
"team2": {...},
"status": "A_VENIR"
}
]
}
]
}
POST /events
Description : Créer un événement (Admin uniquement)
Request Body :
{
"event_date": "2025-11-15",
"event_time": "19:30",
"matches": [
{
"court_number": 1,
"team1_id": 1,
"team2_id": 2
},
{
"court_number": 2,
"team1_id": 3,
"team2_id": 4
}
]
}
Validations :
event_date>= aujourd'huievent_timeformat HH:MM- 1 à 3 matchs par événement
- Pas de piste en double pour le mĂȘme Ă©vĂ©nement
- Une équipe ne peut jouer qu'un match par événement
PUT /events/{id}
Description : Modifier un événement (Admin uniquement)
DELETE /events/{id}
Description : Supprimer un événement (Admin uniquement)
Condition : Tous les matchs doivent avoir le statut A_VENIR
3.3.7 Endpoints - Matchs
GET /matches
Description : Liste des matchs
Query Params :
upcoming: true pour les 30 prochains joursteam_id: Filtrer par équipestatus: Filtrer par statutmy_matches: true pour les matchs de l'utilisateur connecté
Response (200) :
{
"matches": [
{
"id": 1,
"event": {
"date": "2025-11-15",
"time": "19:30"
},
"court_number": 1,
"team1": {
"id": 1,
"company": "Tech Corp",
"players": [...]
},
"team2": {
"id": 2,
"company": "Innov Ltd",
"players": [...]
},
"status": "A_VENIR",
"score_team1": null,
"score_team2": null
}
],
"total": 1
}
POST /matches
Description : Créer un match (Admin uniquement)
PUT /matches/{id}
Description : Modifier un match (Admin uniquement)
Request Body (exemple - mise Ă jour du score) :
{
"status": "TERMINE",
"score_team1": "6-4, 6-3",
"score_team2": "4-6, 3-6"
}
Validation du score :
- Format : "X-Y, X-Y" ou "X-Y, X-Y, X-Y"
- Chaque set : vainqueur >= 6 jeux
- Si 7-X : X <= 5
- 7-6 autorisé (tie-break)
DELETE /matches/{id}
Description : Supprimer un match (Admin uniquement)
Condition : Statut = A_VENIR
3.3.8 Endpoints - Résultats
GET /results/my-results
Description : Résultats de l'utilisateur connecté
Authentification : Joueur
Response (200) :
{
"results": [
{
"match_id": 1,
"date": "2025-10-15",
"opponents": {
"company": "Innov Ltd",
"players": ["Alice Martin", "Bob Dupont"]
},
"score": "6-4, 6-3",
"result": "VICTOIRE",
"court_number": 1
}
],
"statistics": {
"total_matches": 10,
"wins": 7,
"losses": 3,
"win_rate": 70.0
}
}
GET /results/rankings
Description : Classement général des entreprises
Response (200) :
{
"rankings": [
{
"position": 1,
"company": "Tech Corp",
"matches_played": 10,
"wins": 8,
"losses": 2,
"points": 24,
"sets_won": 18,
"sets_lost": 6
}
]
}
3.3.9 Endpoints - Profil
GET /profile/me
Description : Profil de l'utilisateur connecté
Authentification : Requise
Response (200) :
{
"user": {
"id": 1,
"email": "john.doe@example.com",
"role": "JOUEUR"
},
"player": {
"id": 1,
"first_name": "John",
"last_name": "Doe",
"company": "Tech Corp",
"license_number": "L123456",
"birth_date": "1990-05-15",
"photo_url": "/uploads/profile_1.jpg"
}
}
PUT /profile/me
Description : Modifier son profil
Request Body :
{
"first_name": "John",
"last_name": "Doe",
"birth_date": "1990-05-15",
"email": "john.doe@newemail.com"
}
POST /profile/me/photo
Description : Upload photo de profil
Content-Type : multipart/form-data
Max size : 2MB
Formats : .jpg, .jpeg, .png
DELETE /profile/me/photo
Description : Supprimer la photo de profil
3.3.10 Endpoints - Administration
POST /admin/accounts/create
Description : Créer un compte pour un joueur
Authentification : Admin
Request Body :
{
"player_id": 1,
"role": "JOUEUR"
}
Response (201) :
{
"message": "Compte créé avec succÚs",
"email": "john.doe@example.com",
"temporary_password": "T3mp@ryP@ss2025!Xy",
"warning": "Ce mot de passe ne sera affiché qu'une seule fois"
}
POST /admin/accounts/{user_id}/reset-password
Description : Réinitialiser le mot de passe d'un utilisateur
Authentification : Admin
Response (200) :
{
"message": "Mot de passe réinitialisé",
"temporary_password": "N3wT3mp@ryP@ss!",
"warning": "Ce mot de passe ne sera affiché qu'une seule fois"
}
3.4 Formats et Contraintes
3.4.1 Formats de dates et heures
| Type | Format | Exemple | Regex |
|---|---|---|---|
| Date | YYYY-MM-DD | 2025-11-15 | ^\d{4}-\d{2}-\d{2}$ |
| Heure | HH:MM | 19:30 | ^([01]\d|2[0-3]):([0-5]\d)$ |
| Timestamp | ISO 8601 | 2025-11-15T19:30:00Z | - |
3.4.2 Formats de validation
| Champ | Regex / RĂšgle | Message d'erreur |
|---|---|---|
| ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ | "Format d'email invalide" | |
| Licence | ^L\d{6}$ | "Le numĂ©ro de licence doit ĂȘtre au format LXXXXXX" |
| Nom/PrĂ©nom | ^[a-zA-ZĂ-Ăż\s'-]{2,50}$ | "2 Ă 50 caractĂšres, lettres uniquement" |
| Score | ^(\d+-\d+)(,\s*\d+-\d+){1,2}$ | "Format de score invalide (ex: 6-4, 6-3)" |
| Court | 1 <= n <= 10 | "Le numĂ©ro de piste doit ĂȘtre entre 1 et 10" |
3.4.3 Codes de statut HTTP
| Code | Signification | Utilisation |
|---|---|---|
| 200 | OK | RequĂȘte rĂ©ussie (GET, PUT) |
| 201 | Created | Ressource créée (POST) |
| 204 | No Content | Suppression réussie (DELETE) |
| 400 | Bad Request | Données invalides |
| 401 | Unauthorized | Non authentifié |
| 403 | Forbidden | AccÚs interdit (rÎle insuffisant ou compte bloqué) |
| 404 | Not Found | Ressource inexistante |
| 409 | Conflict | Conflit (email/licence déjà utilisé) |
| 422 | Unprocessable Entity | Validation Pydantic échouée |
| 500 | Internal Server Error | Erreur serveur |
4. Exigences de Sécurité
4.1 Authentification et Autorisation
4.1.1 JWT (JSON Web Tokens)
Configuration obligatoire :
- Algorithme : HS256
- Secret : Généré aléatoirement, stocké en variable d'environnement
- Durée de validité : 24 heures
- Payload minimum :
sub: user_idemail: email de l'utilisateurrole: rĂŽle de l'utilisateurexp: timestamp d'expiration
Implémentation :
# Exemple Python avec PyJWT
import jwt
from datetime import datetime, timedelta
def create_access_token(user_id: int, email: str, role: str) -> str:
payload = {
"sub": user_id,
"email": email,
"role": role,
"exp": datetime.utcnow() + timedelta(hours=24)
}
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
4.1.2 Protection des routes
Middleware d'authentification :
- Vérifier la présence du token dans le header
Authorization: Bearer {token} - Valider le token (signature, expiration)
- Extraire les informations utilisateur
- Retourner 401 si token invalide ou absent
Middleware d'autorisation :
- Vérifier que le rÎle de l'utilisateur correspond aux permissions requises
- Retourner 403 si permissions insuffisantes
| Route | Authentification | RĂŽle requis |
|---|---|---|
| /api/v1/auth/login | Non | - |
| /api/v1/profile/* | Oui | Tous |
| /api/v1/players (GET) | Oui | Admin |
| /api/v1/admin/* | Oui | Admin |
| /api/v1/matches (POST/PUT/DELETE) | Oui | Admin |
4.2 Protection contre les attaques
4.2.1 Injection SQL
Exemple correct :
# â
CORRECT - Utilisation de l'ORM
user = db.query(User).filter(User.email == email).first()
# â INTERDIT - RequĂȘte SQL brute
user = db.execute(f"SELECT * FROM users WHERE email = '{email}'")
4.2.2 XSS (Cross-Site Scripting)
Protection cÎté backend :
- Sanitization de toutes les entrées textuelles
- Ăchappement des caractĂšres spĂ©ciaux HTML
- Headers de sécurité :
X-Content-Type-Options: nosniffX-Frame-Options: DENYContent-Security-Policy
Protection cÎté frontend :
- Utilisation de
v-textau lieu dev-htmldans VueJS - Validation des données avant affichage
- Pas d'
eval()ou d'exécution de code dynamique
Exemple de sanitization :
import bleach
def sanitize_input(text: str) -> str:
# Supprime tous les tags HTML
return bleach.clean(text, tags=[], strip=True)
4.2.3 CSRF (Cross-Site Request Forgery)
Protection :
- Utilisation de tokens JWT (inclus dans les headers, pas dans les cookies)
- VĂ©rification de l'origin des requĂȘtes
- Header
SameSite=Strictsi utilisation de cookies
4.2.4 Brute Force (Anti-bot)
Algorithme :
CREATE TABLE login_attempts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL,
attempts_count INTEGER DEFAULT 0,
last_attempt TIMESTAMP,
locked_until TIMESTAMP NULL
);
1. Tentative de connexion reçue
2. Vérifier dans login_attempts si l'email est bloqué
- Si locked_until > maintenant : retourner 403 avec temps restant
3. Vérifier les credentials
- Si invalides :
* Incrémenter attempts_count
* Si attempts_count >= 5 :
- Définir locked_until = maintenant + 30 minutes
* Retourner 401 avec tentatives restantes
- Si valides :
* Réinitialiser attempts_count à 0
* Retourner le token
5. Spécifications des Tests
5.1 Tests Unitaires (Backend Python)
5.1.1 Framework et organisation
Framework : Pytest
Structure :
tests/ âââ conftest.py # Fixtures globales âââ test_auth.py # Tests authentification âââ test_players.py # Tests joueurs âââ test_teams.py # Tests Ă©quipes âââ test_matches.py # Tests matchs âââ test_events.py # Tests Ă©vĂ©nements âââ test_security.py # Tests sĂ©curitĂ© âââ test_validation.py # Tests validation
5.1.2 Couverture minimale requise
Commande pour mesurer la couverture :
pytest --cov=app --cov-report=html --cov-report=term
5.1.3 Exemple de test
# tests/test_auth.py
import pytest
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_login_success(test_db, test_user):
"""Test connexion avec credentials valides"""
response = client.post("/api/v1/auth/login", json={
"email": "test@example.com",
"password": "ValidP@ssw0rd123"
})
assert response.status_code == 200
data = response.json()
assert "access_token" in data
assert data["user"]["email"] == "test@example.com"
assert data["user"]["role"] == "JOUEUR"
def test_login_invalid_password(test_db, test_user):
"""Test connexion avec mot de passe invalide"""
response = client.post("/api/v1/auth/login", json={
"email": "test@example.com",
"password": "WrongPassword"
})
assert response.status_code == 401
data = response.json()
assert "attempts_remaining" in data
def test_brute_force_protection(test_db, test_user):
"""Test blocage aprÚs 5 tentatives échouées"""
# Faire 5 tentatives échouées
for i in range(5):
response = client.post("/api/v1/auth/login", json={
"email": "test@example.com",
"password": "WrongPassword"
})
assert response.status_code == 401
# La 6Ăšme tentative doit ĂȘtre bloquĂ©e
response = client.post("/api/v1/auth/login", json={
"email": "test@example.com",
"password": "ValidP@ssw0rd123"
})
assert response.status_code == 403
data = response.json()
assert "locked_until" in data
assert "minutes_remaining" in data
5.2 Tests End-to-End (Cypress ou autre)
5.2.1 Exemple Configuration Cypress
Installation :
npm install --save-dev cypress
Structure :
cypress/
âââ e2e/
â âââ auth.cy.js # Tests authentification
â âââ player.cy.js # Tests joueur
â âââ admin.cy.js # Tests admin
â âââ navigation.cy.js # Tests navigation
âââ fixtures/
â âââ users.json # DonnĂ©es de test
âââ support/
âââ commands.js # Commandes custom
5.2.2 Exemple de test Cypress
// cypress/e2e/auth.cy.js
describe('Authentification', () => {
beforeEach(() => {
cy.visit('http://localhost:5173')
})
it('Affiche la page de login', () => {
cy.contains('Se connecter').should('be.visible')
cy.get('input[type="email"]').should('be.visible')
cy.get('input[type="password"]').should('be.visible')
})
it('Connexion avec credentials valides', () => {
cy.get('input[type="email"]').type('joueur@example.com')
cy.get('input[type="password"]').type('ValidP@ssw0rd123')
cy.get('button[type="submit"]').click()
// Vérifier la redirection
cy.url().should('include', '/dashboard')
cy.contains('Bienvenue').should('be.visible')
})
it('Connexion échoue avec mauvais mot de passe', () => {
cy.get('input[type="email"]').type('joueur@example.com')
cy.get('input[type="password"]').type('WrongPassword')
cy.get('button[type="submit"]').click()
// Vérifier le message d'erreur
cy.contains('Email ou mot de passe incorrect').should('be.visible')
cy.contains('tentatives restantes').should('be.visible')
})
it('Bloque le compte aprÚs 5 tentatives échouées', () => {
// Faire 5 tentatives
for (let i = 0; i < 5; i++) {
cy.get('input[type="email"]').clear().type('joueur@example.com')
cy.get('input[type="password"]').clear().type('WrongPassword')
cy.get('button[type="submit"]').click()
cy.wait(500)
}
// Vérifier le blocage
cy.contains('Compte bloqué').should('be.visible')
cy.contains('minutes').should('be.visible')
})
})
5.3 Tests de Sécurité OWASP (Bonus)
5.3.1 OWASP Top 10
Tests recommandés :
| Vulnérabilité | Test | Outil |
|---|---|---|
| Injection SQL | Tentatives d'injection dans les formulaires | sqlmap, manuel |
| Authentification cassée | Brute force, session hijacking | Burp Suite |
| XSS | Injection de scripts dans les champs texte | XSStrike |
| Exposition de données | Vérifier les réponses API | Manuel |
| ContrĂŽle d'accĂšs | AccĂšs aux ressources sans autorisation | Manuel |
5.3.2 Checklist de sécurité
⥠Les mots de passe sont-ils hashés avec bcrypt? ⥠Les tokens JWT expirent-ils correctement? ⥠Les entrées utilisateur sont-elles sanitizées? ⥠Le mécanisme anti-brute force fonctionne-t-il? ⥠Les rÎles sont-ils correctement vérifiés? ⥠Les messages d'erreur ne divulguent-ils pas d'informations sensibles? ⥠Les fichiers uploadés sont-ils validés? ⥠Les headers de sécurité sont-ils présents? ⥠HTTPS est-il forcé en production? ⥠Les secrets sont-ils dans des variables d'environnement?
6. Livrables et Ăvaluation
6.1 Livrables attendus
6.1.1 Code source
Date limite : 31/12/2025
Contenu obligatoire :
- Code source complet (backend + frontend)
- Fichier README.md détaillé
- Requirements.txt (Python) et package.json (Node)
- Base de données SQLite
- Tests unitaires et E2E (code applicatif + cahier de recette détaillé)
- Rapports d'anomalie (rapport de bug)
6.1.2 Rapport
Format :
Structure du rapport :
- Introduction
- Présentation du projet
- Objectifs
- Architecture
- Schéma d'architecture
- Choix technologiques
- ModÚle de données
- Fonctionnalités implémentées
- Description détaillée
- Screenshots
- Sécurité
- Mesures implémentées
- Tests de sécurité
- Vulnérabilités identifiées et corrigées
- Tests
- Stratégie de test
- Couverture
- Résultats
- Difficultés rencontrées
- Améliorations possibles
- Conclusion
7. Annexes
7.1 Kit de démarrage
Un kit de démarrage est fourni avec ce cahier des charges. Il contient :
- Structure backend FastAPI avec authentification JWT fonctionnelle
- Structure frontend VueJS avec routing configuré
- Base de données SQLite initialisée
- Page d'accueil et page de login
- README complet pour le lancement
7.2 Commandes utiles
7.2.1 Backend
# Installation cd backend python -m venv venv source venv/bin/activate # Linux/Mac venv\Scripts\activate # Windows pip install -r requirements.txt # Lancement uvicorn app.main:app --reload --port 8000 # Tests pytest pytest --cov=app --cov-report=html # Migrations (si Alembic utilisé) alembic revision --autogenerate -m "description" alembic upgrade head
7.2.2 Frontend
# Installation cd frontend npm install # Lancement dev npm run dev # Build production npm run build # Tests E2E npx cypress open npx cypress run
7.3 Variables d'environnement
Fichier backend/.env :
# Database DATABASE_URL=sqlite:///./padel_corpo.db # Security SECRET_KEY=your-secret-key-here-change-in-production ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=1440 # CORS ALLOWED_ORIGINS=http://localhost:5173 # Upload UPLOAD_DIR=./uploads MAX_UPLOAD_SIZE=2097152
Fichier frontend/.env :
VITE_API_BASE_URL=http://localhost:8000/api/v1
7.4 Ressources recommandées
7.4.1 Documentation officielle
- FastAPI : https://fastapi.tiangolo.com/
- VueJS 3 : https://vuejs.org/
- SQLAlchemy : https://docs.sqlalchemy.org/
- Pytest : https://docs.pytest.org/
- Cypress : https://docs.cypress.io/
- OWASP Top 10 : https://owasp.org/www-project-top-ten/
7.4.2 Tutoriels utiles
- JWT Authentication avec FastAPI
- Vue Router pour SPA
- SQLAlchemy ORM best practices
- Writing effective Pytest tests
- Cypress E2E testing guide
7.5 Exemples de wireframes
7.5.1 Page de login
âââââââââââââââââââââââââââââââââââââââââââ â â â đŸ CORPO PADEL â â â â âââââââââââââââââââââââââââââââ â â â Email â â â â ________________________ â â â â â â â â Mot de passe â â â â ________________________ â â â â â â â â [ Se connecter ] â â â â â â â â Tentatives restantes: 5 â â â âââââââââââââââââââââââââââââââ â â â âââââââââââââââââââââââââââââââââââââââââââ
7.5.2 Planning (vue joueur)
âââââââââââââââââââââââââââââââââââââââââââ â đ Accueil | đ Planning | ✠Matchs â â đ RĂ©sultats | đ€ Profil â ââââââââââââââââââââââââââââââââââââââââââ†â â â PLANNING NOVEMBRE 2025 â â â â [<] NOVEMBRE 2025 [>] â â â â L M M J V S D â â 1 2 3 â â 4 5 6 7 8 [9] 10 â â 11 12 13 14 â15 16 17 â â 18 19 20 â22 23 24 â â 25 26 27 28 29 30 â â â â â = ĂvĂ©nements planifiĂ©s â â â â DĂTAILS DU 15 NOVEMBRE â â âââââââââââââââââââââââââââââââââââââ â â â 19:30 - Piste 1 â â â â Tech Corp vs Innov Ltd â â â âââââââââââââââââââââââââââââââââââââ â â â âââââââââââââââââââââââââââââââââââââââââââ
7.5.3 Administration
âââââââââââââââââââââââââââââââââââââââââââ â ADMINISTRATION â ââââââââââââââââââââââââââââââââââââââââââ†â â â [Joueurs] [Ăquipes] [Poules] [Comptes]â â â â GESTION DES JOUEURS â â â â [+ Nouveau joueur] đ Recherche â â â â ââââââââââââââââââââââââââââââââââââââââ â â Nom PrĂ©nom Entreprise đ đ ââ â âââââââââââââââââââââââââââââââââââââââ€â â â Dupont Jean Tech Corp âïž â ââ â â Martin Alice Innov Ltd âïž â ââ â â Durand Bob StartCo âïž â ââ â ââââââââââââââââââââââââââââââââââââââââ â â â Total: 3 joueurs â â â âââââââââââââââââââââââââââââââââââââââââââ
7.6 RÚgles métier récapitulatives
| Entité | RÚgle |
|---|---|
| Joueur | Tous les champs obligatoires doivent ĂȘtre renseignĂ©s |
| Joueur | Numéro de licence unique au format LXXXXXX |
| Joueur | Email unique |
| Joueur | Suppression impossible si dans une équipe |
| Ăquipe | Les 2 joueurs doivent ĂȘtre de la mĂȘme entreprise |
| Ăquipe | Un joueur ne peut ĂȘtre que dans une Ă©quipe par saison |
| Ăquipe | Modification/suppression impossible si matchs jouĂ©s |
| Poule | Exactement 6 équipes obligatoires |
| Poule | Nom unique |
| ĂvĂ©nement | Date >= aujourd'hui |
| ĂvĂ©nement | 1 Ă 3 matchs par Ă©vĂ©nement |
| ĂvĂ©nement | Suppression impossible si matchs terminĂ©s |
| Match | Une piste ne peut accueillir qu'un match par créneau |
| Match | Une équipe ne peut jouer qu'un match par événement |
| Match | Suppression uniquement si statut A_VENIR |
| Score | Format "X-Y, X-Y" ou "X-Y, X-Y, X-Y" |
| Classement | Victoire = 3 points, Défaite = 0 point |
| Login | Blocage 30 min aprÚs 5 tentatives échouées |
| Mot de passe | Min 12 caractÚres, maj, min, chiffre, spécial |
# Application Corpo Padel
## Description
Application de gestion de tournois corporatifs de padel.
## Prérequis
- Python 3.11+
- Node.js 18+
- SQLite 3
## Installation
### Backend
```bash
cd backend
python -m venv venv
# Linux/Mac
source venv/bin/activate
# Windows
venv\Scripts\activate
pip install -r requirements.txt
```
### Frontend
```bash
cd frontend
npm install
```
## Configuration
### Backend
Créer un fichier `.env` dans le dossier backend :
```
DATABASE_URL=sqlite:///./padel_corpo.db
SECRET_KEY=votre-cle-secrete-ici
```
### Frontend
Créer un fichier `.env` dans le dossier frontend :
```
VITE_API_BASE_URL=http://localhost:8000/api/v1
```
## Lancement
### Backend
```bash
cd backend
uvicorn app.main:app --reload --port 8000
```
Le backend sera accessible sur http://localhost:8000
### Frontend
```bash
cd frontend
npm run dev
```
Le frontend sera accessible sur http://localhost:5173
## Comptes de test
### Administrateur
- Email: admin@padel.com
- Mot de passe: Admin@2025!
### Joueur
- Email: joueur@padel.com
- Mot de passe: Joueur@2025!
## Tests
### Tests unitaires backend
```bash
cd backend
pytest
pytest --cov=app --cov-report=html
```
### Tests E2E
```bash
cd frontend
npx cypress open
```
## Structure du projet
```
.
âââ backend/
â âââ app/
â â âââ api/ # Routes API
â â âââ models/ # ModĂšles SQLAlchemy
â â âââ schemas/ # SchĂ©mas Pydantic
â â âââ services/ # Logique mĂ©tier
â â âââ main.py # Point d'entrĂ©e
â âââ tests/
âââ frontend/
â âââ src/
â â âââ components/ # Composants Vue
â â âââ views/ # Pages
â â âââ router/ # Configuration routing
â â âââ App.vue
â âââ cypress/
âââ README.md
```
## Fonctionnalités implémentées
- â
Authentification JWT avec anti-brute force
- â
Gestion des joueurs, équipes, poules
- â
Planning et matchs
- â
Résultats et classements
- â
Administration
- â
Profils utilisateurs
## Sécurité
- Hashing des mots de passe avec bcrypt
- Protection anti-brute force (5 tentatives max)
- Validation des entrées (Pydantic + frontend)
- Protection XSS
- Utilisation exclusive de l'ORM (pas de SQL brut)
- Headers de sécurité
## Auteurs
[Vos noms]
## Licence
Projet pédagogique - Formation Ingénieur
```
7.9 Checklist avant rendu
⥠L'application démarre correctement en suivant le README
⥠Tous les comptes de test fonctionnent
⥠Le mécanisme anti-brute force est opérationnel
⥠Les mots de passe sont hashés dans la base
⥠Toutes les validations sont en place
⥠Les tests unitaires passent (couverture >= 70%)
⥠Les tests E2E passent
⥠Le rapport PDF est complet (10-15 pages)
⥠Le code est commenté et propre
⥠Les fichiers .env ne contiennent pas de vraies valeurs sensibles
⥠Le fichier .gitignore exclut venv/, node_modules/, .env
⥠Les requirements.txt et package.json sont à jour
⥠Aucune erreur console dans le frontend
⥠Aucune erreur dans les logs backend
⥠Les messages d'erreur sont clairs pour l'utilisateur
⥠La navigation entre les pages fonctionne
⥠Les rÎles sont correctement vérifiés
⥠Les routes protégées ne sont pas accessibles sans auth
⥠Le classement se calcule correctement
⥠Les filtres de matchs fonctionnent
⥠Les formulaires ont une validation temps réel
7.10 Calendrier suggéré (12h)
| Heure | TĂąche |
|---|---|
| H0-H1 | Prise en main du kit de démarrage, compréhension du CDC |
| H1-H3 | ModÚle de données + API joueurs/équipes/poules |
| H3-H5 | API événements/matchs + Frontend (composants de base) |
| H5-H7 | Pages Planning, Matchs, Résultats (frontend + backend) |
| H7-H8 | Page Administration + Gestion des comptes |
| H8-H9 | Profil utilisateur + Upload photo |
| H9-H10 | Tests unitaires (backend) + Tests E2E |
| H10-H11 | Corrections bugs, améliorations sécurité, tests complémentaires |
| H11-H12 | Rédaction du rapport technique |
Bon courage !
N'hésitez pas à consulter la documentation officielle des frameworks utilisés.
Version 1.0 - Octobre 2025
Cahier des charges - Projet pédagogique Test & Sécurité
đ» Starter Kit - Corpo Padel
Kit de dĂ©marrage prĂȘt Ă l'emploi pour votre projet pĂ©dagogique
đŠ Contenu du Kit
Backend (FastAPI)
backend/ âââ app/ â âââ api/ â â âââ auth.py â Routes d'authentification â â âââ deps.py â DĂ©pendances (get_current_user) â âââ core/ â â âââ config.py â Configuration â â âââ security.py â JWT + hashing â âââ models/ â â âââ models.py â User + LoginAttempt â âââ schemas/ â â âââ auth.py â SchĂ©mas Pydantic â âââ database.py â Configuration SQLAlchemy â âââ main.py â Application FastAPI âââ tests/ â âââ conftest.py â Fixtures â âââ test_auth.py â Tests authentification â âââ test_security.py â Tests sĂ©curitĂ© â âââ test_validation.py â Tests validation âââ .env.example âââ requirements.txt âââ README.md
Frontend (VueJS)
frontend/ âââ src/ â âââ components/ â â âââ NavBar.vue â Barre de navigation â âââ router/ â â âââ index.js â Routing avec guards â âââ services/ â â âââ api.js â Client Axios + intercepteurs â âââ stores/ â â âââ auth.js â Store Pinia authentification â âââ views/ â â âââ HomePage.vue â Page d'accueil â â âââ LoginPage.vue â Page de connexion â âââ App.vue â âââ main.js âââ cypress/ â âââ e2e/ â â âââ auth.cy.js â Tests E2E auth â â âââ navigation.cy.js â Tests navigation â âââ support/ â âââ commands.js â Commandes custom âââ .env.example âââ package.json âââ README.md
đ Installation Rapide
1. Cloner le repository
git clone https://github.com/AndreyPividori/PolytechTours-PadelStarterKit.git cd PolytechTours-PadelStarterKit
2. Installation Backend
cd backend # Créer l'environnement virtuel python -m venv venv # Activer l'environnement source venv/bin/activate # Linux/Mac venv\Scripts\activate # Windows # Installer les dépendances pip install -r requirements.txt # Configuration cp .env.example .env # Générer une SECRET_KEY python -c "import secrets; print(secrets.token_urlsafe(32))" # Copier la clé générée dans le fichier .env # Initialiser la base de données python -c "from app.database import init_db; init_db()" # Lancer le serveur uvicorn app.main:app --reload --port 8000
3. Installation Frontend (nouveau terminal)
cd frontend # Installer les dépendances npm install # Configuration cp .env.example .env # Lancer le serveur de développement npm run dev
Application : http://localhost:5173
đ Compte de Test
Email : admin@padel.com
Mot de passe : Admin@2025!
Ce compte administrateur est créé automatiquement lors de l'initialisation de la base de données.
â FonctionnalitĂ©s ImplĂ©mentĂ©es
- â Authentification JWT : SystĂšme complet avec gĂ©nĂ©ration et validation de tokens
- â Anti-brute force : Blocage automatique aprĂšs 5 tentatives Ă©chouĂ©es pendant 30 minutes
- â Hashing des mots de passe : Utilisation de bcrypt pour sĂ©curiser les mots de passe
- â Page d'accueil : Interface d'accueil responsive avec message de bienvenue
- â Page de login : Formulaire de connexion avec validation et gestion des erreurs
- â Navigation protĂ©gĂ©e : Vue Router avec guards pour protĂ©ger les routes
- â Store d'authentification : Gestion d'Ă©tat avec Pinia
- â Client API : Axios configurĂ© avec intercepteurs pour les tokens
- â Tests unitaires : Exemples de tests avec Pytest
- â Tests E2E : Exemples de tests avec Cypress
đ Ă DĂ©velopper
Les fonctionnalitĂ©s suivantes doivent ĂȘtre dĂ©veloppĂ©es selon le cahier des charges :
- đČ Gestion des joueurs (CRUD complet)
- đČ Gestion des Ă©quipes (crĂ©ation, modification, suppression)
- đČ Gestion des poules (6 Ă©quipes par poule)
- đČ Planning avec calendrier mensuel
- đČ Gestion des Ă©vĂ©nements (1 Ă 3 matchs par Ă©vĂ©nement)
- đČ Liste des matchs Ă venir (30 prochains jours)
- đČ Saisie et affichage des rĂ©sultats
- đČ Classement gĂ©nĂ©ral des entreprises
- đČ Interface d'administration complĂšte
- đČ Profil utilisateur avec upload de photo
- đČ Tests complets (couverture â„ 70%)
đ§Ș Lancer les Tests
Tests Backend (Pytest)
cd backend source venv/bin/activate # ou venv\Scripts\activate # Lancer tous les tests pytest # Avec couverture de code pytest --cov=app --cov-report=html # Ouvrir le rapport de couverture # Le fichier htmlcov/index.html contient le rapport détaillé
Tests E2E (Cypress)
cd frontend # Mode interactif (recommandé pour le développement) npx cypress open # Mode headless (pour CI/CD) npx cypress run
đ§ DĂ©pannage
Erreur : "Port 8000 already in use"
# Linux/Mac lsof -ti:8000 | xargs kill -9 # Windows netstat -ano | findstr :8000 taskkill /PID <PID> /F
Erreur : "Module not found" (Backend)
# Vérifier que l'environnement virtuel est activé # Vous devriez voir (venv) dans votre terminal pip install -r requirements.txt
Erreur : CORS
Vérifiez que ALLOWED_ORIGINS dans backend/.env correspond à l'URL du frontend :
ALLOWED_ORIGINS=http://localhost:5173
Erreur : "Cannot find module" (Frontend)
cd frontend rm -rf node_modules package-lock.json npm install
đż Workflow Git RecommandĂ©
Créer votre propre repository
# AprÚs avoir cloné le starter kit cd PolytechTours-PadelStarterKit # Supprimer le remote d'origine git remote remove origin # Ajouter votre remote git remote add origin <url-de-votre-nouveau-repo> # Créer une branche develop git checkout -b develop # Pousser sur votre repository git push -u origin main git push -u origin develop
Workflow de développement
# Créer une branche pour chaque fonctionnalité git checkout develop git checkout -b feature/gestion-joueurs # Développer, tester, commiter git add . git commit -m "feat: ajout CRUD joueurs" # Pousser la branche git push origin feature/gestion-joueurs # Merger dans develop aprÚs validation git checkout develop git merge feature/gestion-joueurs
đ Structure des Commits
Utilisez des messages de commit clairs avec préfixes :
feat:nouvelle fonctionnalitéfix:correction de bugtest:ajout/modification de testsdocs:documentationrefactor:refactoringstyle:formatage
git commit -m "feat: ajout de la gestion des équipes" git commit -m "fix: correction bug anti-brute force" git commit -m "test: ajout tests pour les matchs"
đ Conseils pour RĂ©ussir
đĄ Recommandations importantes
- Commencez par le modÚle de données : Créez d'abord toutes les tables nécessaires
- Développez par itération : Implémentez une fonctionnalité complÚte (backend + frontend + tests) avant de passer à la suivante
- Testez continuellement : Ne laissez pas les tests pour la fin, écrivez-les au fur et à mesure
- Commits réguliers : Faites des commits fréquents avec des messages clairs
- Lisez la documentation : FastAPI et Vue ont d'excellentes documentations officielles
- RĂ©utilisez le code existant : Le pattern d'authentification peut ĂȘtre rĂ©utilisĂ© pour d'autres endpoints
- Validez partout : Backend ET frontend pour une meilleure expérience utilisateur
đ Ressources Utiles
- Repository GitHub : PolytechTours-PadelStarterKit
- Documentation FastAPI : https://fastapi.tiangolo.com/
- Documentation VueJS 3 : https://vuejs.org/
- Documentation SQLAlchemy : https://docs.sqlalchemy.org/
- Documentation Pytest : https://docs.pytest.org/
- Documentation Cypress : https://docs.cypress.io/
đ Vous ĂȘtes prĂȘt !
Tout le nĂ©cessaire est en place pour commencer votre projet. Bon dĂ©veloppement ! đŸ