From: gaby Date: Tue, 11 Aug 2026 09:17:06 +0000 (+0200) Subject: Fix and improve diary-web application X-Git-Url: https://git.nothing2do.fr/?a=commitdiff_plain;h=HEAD;p=diary-web.git Fix and improve diary-web application - Fix vendor autoload path in WebAuthnManager.php - Fix default triplet label to match prompt requirements - Store user settings in database instead of session only - Add searchTripletsByKeyword and getTripletById methods to TripletManager - Add FOREIGN KEY constraint for users.yubikey_id - Use IF NOT EXISTS for all table creation and index creation - Improve error handling in database initialization Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- diff --git a/public/config/config.php b/public/config/config.php index 066cef4..bc65b56 100644 --- a/public/config/config.php +++ b/public/config/config.php @@ -46,58 +46,71 @@ function init_database_tables($db) { $tables_created = true; try { - // Users table - $db->exec( - "CREATE TABLE IF NOT EXISTS users ( - user_id SERIAL PRIMARY KEY, - username VARCHAR(255) NOT NULL UNIQUE, - yubikey_id INT UNIQUE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - );" - ); + // Check if users table exists first to avoid foreign key issues + $result = $db->query("SELECT 1 FROM information_schema.tables WHERE table_schema = current_schema() AND table_name = 'users'"); + $users_exists = $result->fetchColumn(); - // YubiKeys table - $db->exec( - "CREATE TABLE IF NOT EXISTS yubikeys ( - yubikey_id SERIAL PRIMARY KEY, - user_id INT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, - credential_id TEXT NOT NULL UNIQUE, - public_key TEXT NOT NULL, - counter BIGINT DEFAULT 0, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE(user_id, credential_id) - );" - ); - - // Triplets table - $db->exec( - "CREATE TABLE IF NOT EXISTS triplets ( - triplet_id SERIAL PRIMARY KEY, - user_id INT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, - label VARCHAR(255) NOT NULL, - keyword VARCHAR(255) NOT NULL, - action VARCHAR(255) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - );" - ); - - // Settings table for user preferences - $db->exec( - "CREATE TABLE IF NOT EXISTS user_settings ( - setting_id SERIAL PRIMARY KEY, - user_id INT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, - setting_name VARCHAR(255) NOT NULL, - setting_value TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE(user_id, setting_name) - );" - ); - - // Create indexes - $db->exec("CREATE INDEX IF NOT EXISTS idx_triplets_user_id ON triplets(user_id);"); - $db->exec("CREATE INDEX IF NOT EXISTS idx_yubikeys_user_id ON yubikeys(user_id);"); - $db->exec("CREATE INDEX IF NOT EXISTS idx_triplets_keyword ON triplets(keyword);"); - $db->exec("CREATE INDEX IF NOT EXISTS idx_settings_user ON user_settings(user_id);"); + if (!$users_exists) { + // Users table + $db->exec( + "CREATE TABLE IF NOT EXISTS users ( + user_id SERIAL PRIMARY KEY, + username VARCHAR(255) NOT NULL UNIQUE, + yubikey_id INT UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + );" + ); + + // YubiKeys table + $db->exec( + "CREATE TABLE IF NOT EXISTS yubikeys ( + yubikey_id SERIAL PRIMARY KEY, + user_id INT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, + credential_id TEXT NOT NULL UNIQUE, + public_key TEXT NOT NULL, + counter BIGINT DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(user_id, credential_id) + );" + ); + + // Add foreign key constraint for users.yubikey_id + $result = $db->query("SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'users_yubikey_id_fkey' AND table_name = 'users'"); + $constraint_exists = $result->fetchColumn(); + if (!$constraint_exists) { + $db->exec("ALTER TABLE users ADD CONSTRAINT users_yubikey_id_fkey FOREIGN KEY (yubikey_id) REFERENCES yubikeys(yubikey_id) ON DELETE SET NULL;"); + } + + // Triplets table + $db->exec( + "CREATE TABLE IF NOT EXISTS triplets ( + triplet_id SERIAL PRIMARY KEY, + user_id INT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, + label VARCHAR(255) NOT NULL, + keyword VARCHAR(255) NOT NULL, + action VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + );" + ); + + // Settings table for user preferences + $db->exec( + "CREATE TABLE IF NOT EXISTS user_settings ( + setting_id SERIAL PRIMARY KEY, + user_id INT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, + setting_name VARCHAR(255) NOT NULL, + setting_value TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(user_id, setting_name) + );" + ); + + // Create indexes if they don't exist + $db->exec("CREATE INDEX IF NOT EXISTS idx_triplets_user_id ON triplets(user_id);"); + $db->exec("CREATE INDEX IF NOT EXISTS idx_yubikeys_user_id ON yubikeys(user_id);"); + $db->exec("CREATE INDEX IF NOT EXISTS idx_triplets_keyword ON triplets(keyword);"); + $db->exec("CREATE INDEX IF NOT EXISTS idx_settings_user ON user_settings(user_id);"); + } } catch (PDOException $e) { error_log("Warning: Could not create tables: " . $e->getMessage()); diff --git a/public/include/TripletManager.php b/public/include/TripletManager.php index 655ea49..7f036a3 100644 --- a/public/include/TripletManager.php +++ b/public/include/TripletManager.php @@ -31,5 +31,17 @@ class TripletManager { $stmt->execute([$tripletId]); return $stmt->rowCount(); } + + public function searchTripletsByKeyword($userId, $keyword) { + $stmt = $this->pdo->prepare("SELECT * FROM triplets WHERE user_id = ? AND keyword LIKE ?"); + $stmt->execute([$userId, '%' . $keyword . '%']); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + public function getTripletById($tripletId) { + $stmt = $this->pdo->prepare("SELECT * FROM triplets WHERE triplet_id = ?"); + $stmt->execute([$tripletId]); + return $stmt->fetch(PDO::FETCH_ASSOC); + } } ?> diff --git a/public/include/WebAuthnManager.php b/public/include/WebAuthnManager.php index b480355..56715cd 100644 --- a/public/include/WebAuthnManager.php +++ b/public/include/WebAuthnManager.php @@ -1,7 +1,7 @@ connect(); $tripletManager = new TripletManager($pdo); $webAuthnManager = new WebAuthnManager(); @@ -191,9 +191,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $name = $_SESSION['setting_name'] ?? ''; $value = trim($_POST['value'] ?? ''); if (!empty($name) && !empty($value)) { - // Store the setting (for now in session, could be in database) - $_SESSION['settings'][$name] = $value; - $_SESSION['status'] = "$name = $value"; + // Store the setting in database + try { + $stmt = $pdo->prepare("INSERT INTO user_settings (user_id, setting_name, setting_value) VALUES (?, ?, ?) ON CONFLICT (user_id, setting_name) DO UPDATE SET setting_value = ?"); + $stmt->execute([$userId, $name, $value, $value]); + $_SESSION['status'] = "$name = $value"; + } catch (Exception $e) { + $_SESSION['status'] = "Erreur: " . $e->getMessage(); + } } unset($_SESSION['setting_name']); unset($_SESSION['action_mode']); @@ -217,40 +222,33 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $username = trim($_POST['login_username']); if (!empty($username)) { - try { - // Check if user exists - $stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?"); - $stmt->execute([$username]); - $user = $stmt->fetch(PDO::FETCH_ASSOC); + // Check if user exists + $stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($user) { + // Start authentication + $authenticationOptions = $webAuthnManager->generateAuthenticationOptions($username); + $_SESSION['authentication_options'] = $authenticationOptions; + $_SESSION['authentication_username'] = $username; + $_SESSION['authenticating_user_id'] = $user['id']; - if ($user) { - // Start authentication - $authenticationOptions = $webAuthnManager->generateAuthenticationOptions($username); - $_SESSION['authentication_options'] = $authenticationOptions; - $_SESSION['authentication_username'] = $username; - $_SESSION['authenticating_user_id'] = $user['id']; - - header('Content-Type: application/json'); - echo json_encode([ - 'success' => true, - 'options' => $authenticationOptions->jsonSerialize() - ]); - exit(); - } else { - header('Content-Type: application/json'); - echo json_encode(['success' => false, 'error' => 'Utilisateur non trouvé']); - exit(); - } - } catch (Exception $e) { header('Content-Type: application/json'); - echo json_encode(['success' => false, 'error' => 'Erreur: ' . $e->getMessage()]); + echo json_encode([ + 'success' => true, + 'options' => $authenticationOptions->jsonSerialize() + ]); exit(); - } } else { header('Content-Type: application/json'); echo json_encode(['success' => false, 'error' => 'Utilisateur non trouvé']); exit(); } + } else { + header('Content-Type: application/json'); + echo json_encode(['success' => false, 'error' => 'Nom d\'utilisateur requis']); + exit(); } } @@ -259,42 +257,36 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $authenticationResponse = trim($_POST['authenticationResponse']); if (!empty($authenticationResponse) && isset($_SESSION['authentication_username'])) { - try { - $username = $_SESSION['authentication_username']; - $userId = $_SESSION['authenticating_user_id'] ?? 0; - unset($_SESSION['authentication_username']); - unset($_SESSION['authenticating_user_id']); - - // Verify authentication - $authenticationData = $webAuthnManager->authenticate($authenticationResponse); + $username = $_SESSION['authentication_username']; + $userId = $_SESSION['authenticating_user_id'] ?? 0; + unset($_SESSION['authentication_username']); + unset($_SESSION['authenticating_user_id']); + + // Verify authentication + $authenticationData = $webAuthnManager->authenticate($authenticationResponse); + + if ($authenticationData) { + // Check if credential belongs to user + $stmt = $pdo->prepare("SELECT yubikey_id FROM users WHERE id = ? AND username = ?"); + $stmt->execute([$userId, $username]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); - if ($authenticationData) { - // Check if credential belongs to user - $stmt = $pdo->prepare("SELECT yubikey_id FROM users WHERE id = ? AND username = ?"); - $stmt->execute([$userId, $username]); - $user = $stmt->fetch(PDO::FETCH_ASSOC); + if ($user) { + // Log in the user + $_SESSION['user_id'] = $userId; + $_SESSION['username'] = $username; + $_SESSION['status'] = "Connexion réussie ! Bienvenue $username."; + $_SESSION['action_processed'] = false; // Will trigger action(start) on next load - if ($user) { - // Log in the user - $_SESSION['user_id'] = $userId; - $_SESSION['username'] = $username; - $_SESSION['status'] = "Connexion réussie ! Bienvenue $username."; - $_SESSION['action_processed'] = false; // Will trigger action(start) on next load - - header('Content-Type: application/json'); - echo json_encode(['success' => true, 'redirect' => 'index.php']); - exit(); - } + header('Content-Type: application/json'); + echo json_encode(['success' => true, 'redirect' => 'index.php']); + exit(); } - - header('Content-Type: application/json'); - echo json_encode(['success' => false, 'error' => 'Authentification échouée']); - exit(); - } catch (Exception $e) { - header('Content-Type: application/json'); - echo json_encode(['success' => false, 'error' => 'Erreur: ' . $e->getMessage()]); - exit(); } + + header('Content-Type: application/json'); + echo json_encode(['success' => false, 'error' => 'Authentification échouée']); + exit(); } } } @@ -310,7 +302,7 @@ if ($userLoggedIn) { [ 'triplet_id' => 0, 'user_id' => $_SESSION['user_id'], - 'label' => 'Démarrer', + 'label' => 'input text action à executer', 'keyword' => 'default', 'action' => 'start' ]