From 566a7ba79485bde856c2bf44fbdb39caa693fb93 Mon Sep 17 00:00:00 2001 From: Erwann PHILIPPE Date: Wed, 16 Sep 2026 12:18:28 +0200 Subject: [PATCH] =?UTF-8?q?Authentification=20de=20l'api=20(TODO=20:=20Lie?= =?UTF-8?q?r=20voiture=20=C3=A0=20un=20user)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .htaccess | 6 +- index.php | 321 +++++++++++++++++++++++++++++++++++++++----- voituresapi (1).sql | 28 ++++ 3 files changed, 319 insertions(+), 36 deletions(-) diff --git a/.htaccess b/.htaccess index a6f197c..2223a44 100644 --- a/.htaccess +++ b/.htaccess @@ -3,6 +3,10 @@ RewriteEngine On RewriteBase /voitureAPI/ + # Transmettre l'en-tête HTTP Authorization à PHP sous Apache / WampServer + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + # Ne pas réécrire si le fichier ou dossier existe déjà RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d @@ -15,5 +19,5 @@ Header set Access-Control-Allow-Origin "*" Header set Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" - Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With" + Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With, X-API-KEY" diff --git a/index.php b/index.php index 17a61fe..a9e7f14 100644 --- a/index.php +++ b/index.php @@ -7,7 +7,7 @@ require_once __DIR__ . '/config.php'; header('Content-Type: application/json; charset=UTF-8'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS'); -header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With'); +header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With, X-API-KEY'); // Gestion des requêtes de pré-vérification OPTIONS (CORS) if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { @@ -72,7 +72,96 @@ function getPathSegments(): array } /** - * Recherche d'une voiture par sa plaque d'immatriculation (exacte ou normalisée) + * Récupère le jeton Bearer depuis les en-têtes HTTP ou paramètre d'URL + */ +function getBearerToken(): ?string +{ + $authHeader = null; + + if (!empty($_SERVER['HTTP_AUTHORIZATION'])) { + $authHeader = trim($_SERVER['HTTP_AUTHORIZATION']); + } elseif (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { + $authHeader = trim($_SERVER['REDIRECT_HTTP_AUTHORIZATION']); + } elseif (!empty($_SERVER['HTTP_X_API_KEY'])) { + return trim($_SERVER['HTTP_X_API_KEY']); + } elseif (function_exists('apache_request_headers')) { + $headers = apache_request_headers(); + foreach ($headers as $key => $val) { + if (strcasecmp($key, 'Authorization') === 0) { + $authHeader = trim($val); + break; + } + if (strcasecmp($key, 'X-API-KEY') === 0) { + return trim($val); + } + } + } + + if ($authHeader !== null && preg_match('/Bearer\s+(\S+)/i', $authHeader, $matches)) { + return $matches[1]; + } + + if (!empty($_GET['token'])) { + return trim((string)$_GET['token']); + } + + return null; +} + +/** + * Vérifie l'authentification et les droits de l'utilisateur + */ +function authenticate(PDO $db, bool $required = true, ?string $requiredRole = null): ?array +{ + $token = getBearerToken(); + + if ($token === null) { + if ($required) { + sendJson(401, [ + 'success' => false, + 'error' => "Authentification requise. Veuillez fournir un jeton 'Authorization: Bearer ' ou 'X-API-KEY'." + ]); + } + return null; + } + + $stmt = $db->prepare(' + SELECT id, username, nom, role, api_token, token_expires_at + FROM users + WHERE api_token = ? + LIMIT 1 + '); + $stmt->execute([$token]); + $user = $stmt->fetch(); + + if (!$user) { + sendJson(401, [ + 'success' => false, + 'error' => "Jeton d'authentification invalide. Veuillez vous reconnecter via POST /auth/login." + ]); + } + + // Vérifier l'expiration du jeton + if (!empty($user['token_expires_at']) && strtotime($user['token_expires_at']) < time()) { + sendJson(401, [ + 'success' => false, + 'error' => "Jeton d'authentification expiré. Veuillez vous reconnecter via POST /auth/login." + ]); + } + + // Vérifier le rôle requis (ex: 'admin') + if ($requiredRole !== null && strtolower((string)$user['role']) !== strtolower($requiredRole)) { + sendJson(403, [ + 'success' => false, + 'error' => "Accès refusé. Privilèges insuffisants (rôle '$requiredRole' requis, rôle actuel : '{$user['role']}')." + ]); + } + + return $user; +} + +/** + * Recherche d'une voiture par sa plaque d'immatriculation */ function findCarByPlate(PDO $db, string $plaque): void { @@ -112,34 +201,45 @@ try { if (empty($segments)) { sendJson(200, [ 'success' => true, - 'message' => 'API Voitures opérationnelle', + 'message' => 'API Voitures avec Authentification opérationnelle', 'database' => DB_NAME, + 'comptes_demo' => [ + 'admin' => ['username' => 'admin', 'password' => 'adminpassword', 'token_fixe' => 'admin-token-secret-12345'], + 'user' => ['username' => 'user', 'password' => 'userpassword', 'token_fixe' => 'user-token-secret-67890'] + ], 'endpoints' => [ + 'Authentification' => [ + 'POST /auth/login' => '[PUBLIC] Se connecter et obtenir un jeton (username, password)', + 'POST /auth/register' => '[PUBLIC] Créer un compte utilisateur (username, password, nom)', + 'GET /auth/me' => '[AUTHENTIFIÉ] Obtenir le profil de l\'utilisateur connecté', + 'POST /auth/logout' => '[AUTHENTIFIÉ] Révoquer le jeton de session' + ], 'Voitures' => [ - 'GET /cars' => 'Liste toutes les voitures (filtrable par ?immatriculation=...)', - 'GET /cars/{id}' => 'Détails d\'une voiture par ID', - 'GET /cars/immatriculation/{plaque}' => 'Identifier une voiture par sa plaque d\'immatriculation', - 'GET /cars/{id}/etat' => 'État complet (voiture, dernier km, entretiens, notes)', - 'POST /cars' => 'Créer une voiture (marque, modele, annee, dateAchat, VIN, immatriculation)', - 'PUT /cars/{id}' => 'Modifier les paramètres d\'une voiture (y compris immatriculation)', - 'DELETE /cars/{id}' => 'Supprimer une voiture (et ses données associées)' + 'GET /cars' => '[PUBLIC] Liste des voitures (filtrable par ?immatriculation=...)', + 'GET /cars/{id}' => '[PUBLIC] Détails d\'une voiture', + 'GET /cars/immatriculation/{plaque}' => '[PUBLIC] Identifier une voiture par sa plaque', + 'GET /cars/{id}/etat' => '[AUTHENTIFIÉ] État complet (voiture, dernier km, entretiens, notes)', + 'POST /cars' => '[AUTHENTIFIÉ] Créer une voiture (marque, modele, annee, dateAchat, VIN, immatriculation)', + 'PUT /cars/{id}' => '[AUTHENTIFIÉ] Modifier les paramètres d\'une voiture', + 'DELETE /cars/{id}' => '[ADMIN] Supprimer une voiture et toutes ses données associées' ], 'Kilométrage' => [ - 'GET /cars/{id}/kilometrage' => 'Historique des relevés kilométriques', - 'POST /cars/{id}/kilometrage' => 'Ajouter un relevé kilométrique (valeur, date_releve)', - 'DELETE /kilometrage/{id}' => 'Supprimer un relevé kilométrique' + 'GET /cars/{id}/kilometrage' => '[PUBLIC] Historique des relevés kilométriques', + 'POST /cars/{id}/kilometrage' => '[AUTHENTIFIÉ] Ajouter un relevé kilométrique (valeur, date_releve)', + 'DELETE /kilometrage/{id}' => '[ADMIN] Supprimer un relevé kilométrique' ], 'Entretiens' => [ - 'GET /cars/{id}/maintenance' => 'Historique des entretiens d\'une voiture', - 'POST /cars/{id}/maintenance' => 'Ajouter un entretien (type_entretien, date_evenement, kilometrage, description, prix)', - 'PUT /maintenance/{id}' => 'Modifier un entretien', - 'DELETE /maintenance/{id}' => 'Supprimer un entretien' + 'GET /cars/{id}/maintenance' => '[PUBLIC] Historique des entretiens d\'une voiture', + 'GET /maintenance/{id}' => '[PUBLIC] Détails d\'un entretien', + 'POST /cars/{id}/maintenance' => '[AUTHENTIFIÉ] Ajouter un entretien (type_entretien, date_evenement, kilometrage, ...)', + 'PUT /maintenance/{id}' => '[AUTHENTIFIÉ] Modifier un entretien', + 'DELETE /maintenance/{id}' => '[ADMIN] Supprimer un entretien' ], - 'Notes' => [ - 'GET /cars/{id}/notes' => 'Notes associées à une voiture', - 'POST /cars/{id}/notes' => 'Ajouter une note (titre, contenu)', - 'PUT /notes/{id}' => 'Modifier une note', - 'DELETE /notes/{id}' => 'Supprimer une note' + 'Notes (Contenu interne)' => [ + 'GET /cars/{id}/notes' => '[AUTHENTIFIÉ] Consulter les notes d\'une voiture', + 'POST /cars/{id}/notes' => '[AUTHENTIFIÉ] Ajouter une note (titre, contenu)', + 'PUT /notes/{id}' => '[AUTHENTIFIÉ] Modifier une note', + 'DELETE /notes/{id}' => '[AUTHENTIFIÉ] Supprimer une note' ] ] ]); @@ -147,6 +247,117 @@ try { $resource = strtolower($segments[0]); + // ========================================================================= + // RESSOURCE: AUTHENTIFICATION (/auth ...) + // ========================================================================= + if ($resource === 'auth') { + $action = strtolower($segments[1] ?? ''); + + // POST /auth/login + if ($action === 'login' && $method === 'POST') { + $body = getBody(); + if (empty($body['username']) || empty($body['password'])) { + sendJson(400, ['success' => false, 'error' => 'Champs username et password obligatoires']); + } + + $stmt = $db->prepare('SELECT * FROM users WHERE username = ? LIMIT 1'); + $stmt->execute([trim((string)$body['username'])]); + $user = $stmt->fetch(); + + if (!$user || !password_verify((string)$body['password'], $user['password'])) { + sendJson(401, ['success' => false, 'error' => 'Identifiants invalides (nom d\'utilisateur ou mot de passe incorrect)']); + } + + // Génération d'un nouveau jeton valide 7 jours + $token = bin2hex(random_bytes(32)); + $expiresAt = date('Y-m-d H:i:s', time() + (7 * 86400)); + + $stmtUpdate = $db->prepare('UPDATE users SET api_token = ?, token_expires_at = ? WHERE id = ?'); + $stmtUpdate->execute([$token, $expiresAt, $user['id']]); + + sendJson(200, [ + 'success' => true, + 'message' => 'Connexion réussie', + 'data' => [ + 'user' => [ + 'id' => (int)$user['id'], + 'username' => $user['username'], + 'nom' => $user['nom'], + 'role' => $user['role'] + ], + 'token' => $token, + 'expires_at' => $expiresAt + ] + ]); + } + + // POST /auth/register + if ($action === 'register' && $method === 'POST') { + $body = getBody(); + if (empty($body['username']) || empty($body['password'])) { + sendJson(400, ['success' => false, 'error' => 'Champs username et password obligatoires']); + } + + $username = trim((string)$body['username']); + $nom = !empty($body['nom']) ? trim((string)$body['nom']) : null; + $password = password_hash((string)$body['password'], PASSWORD_DEFAULT); + + // Vérifier existence + $stmtCheck = $db->prepare('SELECT id FROM users WHERE username = ?'); + $stmtCheck->execute([$username]); + if ($stmtCheck->fetch()) { + sendJson(409, ['success' => false, 'error' => "Le nom d'utilisateur '$username' est déjà utilisé"]); + } + + $token = bin2hex(random_bytes(32)); + $expiresAt = date('Y-m-d H:i:s', time() + (7 * 86400)); + + $stmt = $db->prepare('INSERT INTO users (username, password, nom, role, api_token, token_expires_at) VALUES (?, ?, ?, ?, ?, ?)'); + $stmt->execute([$username, $password, $nom, 'user', $token, $expiresAt]); + $newId = (int)$db->lastInsertId(); + + sendJson(201, [ + 'success' => true, + 'message' => 'Compte créé avec succès', + 'data' => [ + 'user' => [ + 'id' => $newId, + 'username' => $username, + 'nom' => $nom, + 'role' => 'user' + ], + 'token' => $token, + 'expires_at' => $expiresAt + ] + ]); + } + + // GET /auth/me (Profil de l'utilisateur connecté) + if ($action === 'me' && $method === 'GET') { + $user = authenticate($db, true); + sendJson(200, [ + 'success' => true, + 'data' => [ + 'id' => (int)$user['id'], + 'username' => $user['username'], + 'nom' => $user['nom'], + 'role' => $user['role'], + 'token_expires_at' => $user['token_expires_at'] + ] + ]); + } + + // POST /auth/logout (Révocation du jeton) + if ($action === 'logout' && $method === 'POST') { + $user = authenticate($db, true); + $stmt = $db->prepare('UPDATE users SET api_token = NULL, token_expires_at = NULL WHERE id = ?'); + $stmt->execute([$user['id']]); + sendJson(200, ['success' => true, 'message' => 'Déconnexion réussie, le jeton a été révoqué']); + } + + sendJson(404, ['success' => false, 'error' => "Action d'authentification '$action' non reconnue"]); + } + // ========================================================================= // ENDPOINT DIRECT: /immatriculation/{plaque} // ========================================================================= @@ -162,7 +373,7 @@ try { // RESSOURCE: CARS (/cars ...) // ========================================================================= if ($resource === 'cars' || $resource === 'voitures') { - // NOUVEAU ENDPOINT: GET /cars/immatriculation/{plaque} + // GET /cars/immatriculation/{plaque} (Public) if (isset($segments[1]) && strtolower($segments[1]) === 'immatriculation' && $method === 'GET') { $plaque = $segments[2] ?? null; if (empty($plaque)) { @@ -174,8 +385,10 @@ try { $carId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; $subResource = $segments[2] ?? null; - // 1. GET /cars/{id}/etat (État complet via la procédure sp_getEtatVoiture) + // 1. GET /cars/{id}/etat [CONTENU RESTREINT - AUTHENTIFICATION REQUISE] if ($carId && $subResource === 'etat' && $method === 'GET') { + $currentUser = authenticate($db, true); + try { $stmt = $db->prepare('CALL sp_getEtatVoiture(:id)'); $stmt->execute([':id' => $carId]); @@ -198,6 +411,7 @@ try { sendJson(200, [ 'success' => true, + 'consulted_by' => $currentUser['username'], 'data' => [ 'voiture' => $car, 'maintenances' => $maintenances, @@ -224,6 +438,7 @@ try { sendJson(200, [ 'success' => true, + 'consulted_by' => $currentUser['username'], 'data' => [ 'voiture' => $car, 'maintenances' => $stmtMaint->fetchAll(), @@ -235,6 +450,7 @@ try { // 2. /cars/{id}/kilometrage if ($carId && $subResource === 'kilometrage') { + // GET: Public if ($method === 'GET') { $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 50; try { @@ -252,7 +468,10 @@ try { sendJson(200, ['success' => true, 'data' => $data]); } + // POST: [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($method === 'POST') { + authenticate($db, true); + $body = getBody(); if (!isset($body['valeur'])) { sendJson(400, ['success' => false, 'error' => 'Le champ valeur (kilométrage) est obligatoire']); @@ -281,13 +500,17 @@ try { // 3. /cars/{id}/maintenance if ($carId && ($subResource === 'maintenance' || $subResource === 'entretiens')) { + // GET: Public if ($method === 'GET') { $stmt = $db->prepare('SELECT * FROM maintenance_logs WHERE car_id = ? ORDER BY date_evenement DESC'); $stmt->execute([$carId]); sendJson(200, ['success' => true, 'data' => $stmt->fetchAll()]); } + // POST: [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($method === 'POST') { + authenticate($db, true); + $body = getBody(); if (empty($body['type_entretien']) || empty($body['date_evenement']) || !isset($body['kilometrage'])) { sendJson(400, [ @@ -323,15 +546,21 @@ try { sendJson(405, ['success' => false, 'error' => 'Méthode non autorisée pour cette sous-ressource']); } - // 4. /cars/{id}/notes + // 4. /cars/{id}/notes [CONTENU RESTREINT - AUTHENTIFICATION REQUISE] if ($carId && $subResource === 'notes') { + // GET: Protégé (Notes de suivi confidentielles) if ($method === 'GET') { + authenticate($db, true); + $stmt = $db->prepare('SELECT * FROM notes WHERE car_id = ? ORDER BY date_creation DESC'); $stmt->execute([$carId]); sendJson(200, ['success' => true, 'data' => $stmt->fetchAll()]); } + // POST: [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($method === 'POST') { + authenticate($db, true); + $body = getBody(); if (empty($body['contenu'])) { sendJson(400, ['success' => false, 'error' => 'Le champ contenu est obligatoire']); @@ -359,7 +588,7 @@ try { // --- ACTIONS PRINCIPALES SUR LES VOITURES --- - // GET /cars : Liste toutes les voitures (ou filtre ?immatriculation=...) + // GET /cars : Liste toutes les voitures (Public) if ($carId === null && $subResource === null && $method === 'GET') { if (!empty($_GET['immatriculation'])) { findCarByPlate($db, (string)$_GET['immatriculation']); @@ -373,7 +602,7 @@ try { sendJson(200, ['success' => true, 'data' => $stmt->fetchAll()]); } - // GET /cars/{id} : Consulter une voiture + // GET /cars/{id} : Consulter une voiture (Public) if ($carId !== null && $subResource === null && $method === 'GET') { $stmt = $db->prepare(' SELECT c.*, @@ -389,10 +618,11 @@ try { sendJson(200, ['success' => true, 'data' => $car]); } - // POST /cars : Créer une voiture + // POST /cars : Créer une voiture [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($carId === null && $method === 'POST') { - $body = getBody(); + authenticate($db, true); + $body = getBody(); $required = ['marque', 'modele', 'annee', 'dateAchat']; foreach ($required as $field) { if (empty($body[$field])) { @@ -407,7 +637,7 @@ try { $vin = !empty($body['VIN']) ? trim((string)$body['VIN']) : null; $immatriculation = !empty($body['immatriculation']) ? strtoupper(trim((string)$body['immatriculation'])) : null; - // Vérifier unicité de l'immatriculation si renseignée + // Vérifier unicité de l'immatriculation if ($immatriculation !== null) { $normImmat = str_replace(['-', ' ', '.', '_'], '', $immatriculation); $stmtCheck = $db->prepare(' @@ -444,7 +674,6 @@ try { $newId = (int)$db->lastInsertId(); } - // Kilométrage initial optionnel if (!empty($body['kilometrage_initial'])) { $kmStmt = $db->prepare('INSERT INTO kilometrage (car_id, date_releve, valeur) VALUES (?, ?, ?)'); $kmStmt->execute([$newId, $dateAchat, (int)$body['kilometrage_initial']]); @@ -465,8 +694,10 @@ try { ]); } - // PUT / PATCH /cars/{id} : Mettre à jour les paramètres d'une voiture + // PUT / PATCH /cars/{id} : Mettre à jour les paramètres [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($carId !== null && $subResource === null && ($method === 'PUT' || $method === 'PATCH')) { + authenticate($db, true); + $check = $db->prepare('SELECT id FROM cars WHERE id = ?'); $check->execute([$carId]); if (!$check->fetch()) { @@ -478,7 +709,6 @@ try { $updates = []; $params = []; - // Si immatriculation modifiée, vérifier unicité if (!empty($body['immatriculation'])) { $immat = strtoupper(trim((string)$body['immatriculation'])); $body['immatriculation'] = $immat; @@ -529,8 +759,10 @@ try { ]); } - // DELETE /cars/{id} : Supprimer une voiture + // DELETE /cars/{id} : Supprimer une voiture [ACTION PROTÉGÉE - RÔLE ADMIN REQUIS] if ($carId !== null && $subResource === null && $method === 'DELETE') { + authenticate($db, true, 'admin'); + $check = $db->prepare('SELECT id FROM cars WHERE id = ?'); $check->execute([$carId]); if (!$check->fetch()) { @@ -561,7 +793,10 @@ try { if ($resource === 'kilometrage') { $kmId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; + // DELETE: [ACTION PROTÉGÉE - RÔLE ADMIN REQUIS] if ($kmId && $method === 'DELETE') { + authenticate($db, true, 'admin'); + $stmt = $db->prepare('DELETE FROM kilometrage WHERE id = ?'); $stmt->execute([$kmId]); if ($stmt->rowCount() === 0) { @@ -583,6 +818,7 @@ try { sendJson(400, ['success' => false, 'error' => 'Identifiant d\'entretien requis']); } + // GET: Public if ($method === 'GET') { $stmt = $db->prepare('SELECT * FROM maintenance_logs WHERE id = ?'); $stmt->execute([$maintId]); @@ -593,7 +829,10 @@ try { sendJson(200, ['success' => true, 'data' => $item]); } + // PUT/PATCH: [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($method === 'PUT' || $method === 'PATCH') { + authenticate($db, true); + $body = getBody(); $allowed = ['type_entretien', 'date_evenement', 'kilometrage', 'description', 'prix']; $updates = []; @@ -623,7 +862,10 @@ try { ]); } + // DELETE: [ACTION PROTÉGÉE - RÔLE ADMIN REQUIS] if ($method === 'DELETE') { + authenticate($db, true, 'admin'); + $stmt = $db->prepare('DELETE FROM maintenance_logs WHERE id = ?'); $stmt->execute([$maintId]); if ($stmt->rowCount() === 0) { @@ -636,7 +878,7 @@ try { } // ========================================================================= - // RESSOURCE: NOTES DIRECT (/notes/{id}) + // RESSOURCE: NOTES DIRECT (/notes/{id}) [CONTENU RESTREINT - AUTHENTIFICATION REQUISE] // ========================================================================= if ($resource === 'notes') { $noteId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; @@ -645,7 +887,10 @@ try { sendJson(400, ['success' => false, 'error' => 'Identifiant de note requis']); } + // GET: Authentifié if ($method === 'GET') { + authenticate($db, true); + $stmt = $db->prepare('SELECT * FROM notes WHERE id = ?'); $stmt->execute([$noteId]); $item = $stmt->fetch(); @@ -655,7 +900,10 @@ try { sendJson(200, ['success' => true, 'data' => $item]); } + // PUT/PATCH: Authentifié if ($method === 'PUT' || $method === 'PATCH') { + authenticate($db, true); + $body = getBody(); $allowed = ['titre', 'contenu']; $updates = []; @@ -685,7 +933,10 @@ try { ]); } + // DELETE: Authentifié if ($method === 'DELETE') { + authenticate($db, true); + $stmt = $db->prepare('DELETE FROM notes WHERE id = ?'); $stmt->execute([$noteId]); if ($stmt->rowCount() === 0) { diff --git a/voituresapi (1).sql b/voituresapi (1).sql index caf2572..02fe86a 100644 --- a/voituresapi (1).sql +++ b/voituresapi (1).sql @@ -183,6 +183,34 @@ ALTER TABLE `maintenance_logs` -- ALTER TABLE `notes` ADD CONSTRAINT `fk_notes_car_id` FOREIGN KEY (`car_id`) REFERENCES `cars` (`id`) ON DELETE CASCADE; + +-- +-- Structure de la table `users` +-- + +DROP TABLE IF EXISTS `users`; +CREATE TABLE IF NOT EXISTS `users` ( + `id` int NOT NULL AUTO_INCREMENT, + `username` varchar(50) NOT NULL, + `password` varchar(255) NOT NULL, + `nom` varchar(100) DEFAULT NULL, + `role` varchar(20) NOT NULL DEFAULT 'user', + `api_token` varchar(64) DEFAULT NULL, + `token_expires_at` datetime DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_username` (`username`), + UNIQUE KEY `idx_api_token` (`api_token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- +-- Déchargement des données de la table `users` +-- + +INSERT INTO `users` (`id`, `username`, `password`, `nom`, `role`, `api_token`) VALUES +(1, 'admin', '$2y$10$v0vjF8yL15gA1kXfA9q3hOb00Q0pM0N8dMh2pA7uT1VlqjXlZ9rKq', 'Administrateur', 'admin', 'admin-token-secret-12345'), +(2, 'user', '$2y$10$Q4M1s1Fk6vjF8yL15gA1kXfA9q3hOb00Q0pM0N8dMh2pA7uT1Vlqy', 'Utilisateur Standard', 'user', 'user-token-secret-67890'); + COMMIT; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;