API — Catalogue produits¶
Cette page documente ProductController, monté sous /api/products. Tous les endpoints de cette page sont publics (aucune authentification requise).
Contrôleur couvert
ProductController → GET /, GET /{id}, GET /search, GET /categories
Les endpoints d'administration (création, modification, suppression, upload d'image) vivent sur /api/admin/products/* — voir admin.md.
Vue d'ensemble¶
| Méthode | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/products |
Aucune | Liste paginée de tous les produits du catalogue |
GET |
/api/products/{id} |
Aucune | Détail d'un produit par identifiant |
GET |
/api/products/search |
Aucune | Recherche multi-critères (texte, catégorie, fourchette de prix) |
GET |
/api/products/categories |
Aucune | Liste des catégories distinctes présentes en base |
GET /api/products¶
Retourne la liste paginée des produits du catalogue public. Utilisé par la page d'accueil frontend et les pages catégories.
Auth : aucune
Contrôleur : ProductController.getAllProducts()
Service : ProductService.getAllProducts(Pageable)
Paramètres¶
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
page |
integer | 0 |
Numéro de page (0-indexé) |
size |
integer | 20 |
Nombre de produits par page |
sort |
string | id |
Colonne de tri — whitelist : id, name, price, category, createdAt, stock |
Whitelist de tri
Un paramètre sort non listé dans la whitelist est silencieusement remplacé par id. Ce garde-fou évite une PropertyReferenceException qui se propagerait en 500 si un client envoyait par exemple sort=password.
Réponse — 200 OK (comportement nominal)¶
{
"content": [
{
"id": 1,
"name": "Casque audio Bluetooth",
"description": "Casque sans fil avec réduction de bruit active",
"price": 149.99,
"stock": 42,
"category": "Audio",
"imageUrl": "/images/products/1.jpg",
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-03-20T14:30:00Z",
"pedagogique": false
}
],
"totalElements": 147,
"totalPages": 8,
"number": 0,
"size": 20,
"first": true,
"last": false
}
A13 — Devise incorrecte
En Chaos Métier niveau 4 (Master), la réponse est enveloppée différemment : un wrapper ajoute un champ currency: "USD" alors que les prix restent exprimés en euros dans les autres endpoints. La structure devient :
{
"content": [ /* ... */ ],
"totalElements": 147,
"totalPages": 8,
"number": 0,
"size": 20,
"currency": "USD"
}
Aucune erreur HTTP — le symptôme est visible uniquement par inspection de la réponse. Voir A13.
Codes d'erreur¶
Aucun en fonctionnement normal — cet endpoint retourne toujours 200 OK (liste vide si le catalogue est vide).
GET /api/products/{id}¶
Retourne le détail d'un produit par son identifiant.
Auth : aucune
Contrôleur : ProductController.getProductById()
Paramètres¶
| Paramètre | Type | Description |
|---|---|---|
id (path) |
Long | Identifiant du produit |
Réponse — 200 OK¶
Renvoie directement l'entité Product sérialisée :
{
"id": 1,
"name": "Casque audio Bluetooth",
"description": "Casque sans fil avec réduction de bruit active",
"price": 149.99,
"stock": 42,
"category": "Audio",
"imageUrl": "/images/products/1.jpg",
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-03-20T14:30:00Z",
"pedagogique": false
}
Codes d'erreur¶
| Code | Cause |
|---|---|
| 404 | Produit introuvable (corps vide) |
F4 — Corruption silencieuse
En Chaos Fonctionnel niveau 4 (Master), cet endpoint retourne HTTP 200 avec des données volontairement corrompues :
pricemultiplié par 1.5stockforcé à 0descriptiontronquée
Aucune exception n'est levée, Tempo reste vert, Grafana n'alerte pas. Le diagnostic nécessite une inspection manuelle du payload. Cette anomalie est particulièrement vicieuse car elle illustre qu'un système peut être « en panne » sans jamais le signaler. Voir F4.
GET /api/products/search¶
Recherche multi-critères sur le catalogue. Combine un texte libre (q), un filtre catégorie et une fourchette de prix.
Auth : aucune
Contrôleur : ProductController.searchProducts()
Paramètres¶
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
q |
string | null |
Texte recherché (nom, description) |
category |
string | null |
Filtre catégorie (insensible à la casse) |
minPrice |
decimal | null |
Prix minimum (inclus) |
maxPrice |
decimal | null |
Prix maximum (inclus) |
page |
integer | 0 |
Numéro de page |
size |
integer | 20 |
Taille de page |
Comportement nominal (Chaos Sécurité niveau 0)¶
La recherche utilise ProductService.searchProducts() qui exécute une requête JPQL paramétrée — entièrement immunisée contre l'injection SQL. Le paramètre q est passé comme PreparedStatement à Hibernate.
Réponse — 200 OK¶
Structure identique à GET /api/products (Page Spring Data).
Codes d'erreur¶
Aucun en fonctionnement normal — une recherche sans résultat retourne 200 OK avec content: [].
S1 — Injection SQL
En Chaos Sécurité niveau 1 (Junior) et au-delà, le chemin de code bascule vers ProductRepository.slowSearchFullScan(q) qui exécute une requête SQL native non paramétrée utilisant LIKE '%' || q || '%'. Le paramètre q est concaténé directement dans la requête sans passer par un PreparedStatement.
Payloads exploitables :
q=' OR '1'='1→ retourne tous les produits (bypass du filtre)q='; SELECT password FROM users--→ erreur ou fuite de données selon le moteur SQLq=' UNION SELECT 1,email,password,1,1,1,1,1,1 FROM users--→ exfiltration
Toute tentative est loggée dans activityLog de SecurityChaosService. Voir S1.
GET /api/products/categories¶
Retourne la liste de toutes les catégories présentes dans le catalogue.
Auth : aucune
Contrôleur : ProductController.getCategories()
Service : ProductService.getAllCategories() — SELECT DISTINCT category FROM products
Réponse — 200 OK¶
Retourne directement un tableau JSON (pas d'enveloppe). Pas de pagination — le nombre de catégories est toujours petit (< 20).
Exemple curl¶
# Liste complète (page 0, 20 produits, triés par prix)
curl "http://localhost:9080/api/products?sort=price&size=20"
# Détail d'un produit
curl "http://localhost:9080/api/products/42"
# Recherche "casque" en Audio entre 50 et 200 €
curl "http://localhost:9080/api/products/search?q=casque&category=Audio&minPrice=50&maxPrice=200"
# Liste des catégories
curl "http://localhost:9080/api/products/categories"
Liens associés¶
- Gestion admin des produits — CRUD et upload d'image
- Chaos Sécurité S1 — Injection SQL via
q - Chaos Fonctionnel F4 — Corruption silencieuse produit
- Chaos Métier A13 — Devise USD erronée