From 3f73421fd3f672192defc8ac9c8fac6c9c919a44 Mon Sep 17 00:00:00 2001 From: Jules Date: Wed, 6 May 2026 00:01:39 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20r=C3=B4les=20admin/user,=20page=20Utili?= =?UTF-8?q?sateurs,=20lien=20Vision=20dans=20navbar,=20filtre=20courriers?= =?UTF-8?q?=20par=20compte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- sql/schema.sql | 10 +- web/admin/accounts.php | 2 +- web/admin/domain.php | 2 +- web/admin/index.php | 2 +- web/admin/users.php | 232 ++++++++++++++++++++++++++++++++++++++++ web/api/mails.php | 33 ++++-- web/includes/auth.php | 23 +++- web/includes/layout.php | 29 +++-- 8 files changed, 305 insertions(+), 28 deletions(-) create mode 100644 web/admin/users.php diff --git a/sql/schema.sql b/sql/schema.sql index 557532e..9cb88aa 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -51,10 +51,12 @@ CREATE TABLE IF NOT EXISTS files ( ); CREATE TABLE IF NOT EXISTS admin_users ( - id INT AUTO_INCREMENT PRIMARY KEY, - username VARCHAR(64) NOT NULL UNIQUE, - password VARCHAR(255) NOT NULL, - created_at DATETIME DEFAULT NOW() + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(64) NOT NULL UNIQUE, + role ENUM('admin','user') NOT NULL DEFAULT 'admin', + account_id INT NULL, + password VARCHAR(255) NOT NULL, + created_at DATETIME DEFAULT NOW() ); -- Mot de passe par défaut: admin / copymail2024 diff --git a/web/admin/accounts.php b/web/admin/accounts.php index cfc6ff0..4ea48cd 100644 --- a/web/admin/accounts.php +++ b/web/admin/accounts.php @@ -3,7 +3,7 @@ require_once __DIR__ . '/../includes/db.php'; require_once __DIR__ . '/../includes/auth.php'; require_once __DIR__ . '/../includes/layout.php'; -auth_check(); +admin_check(); $error = ''; diff --git a/web/admin/domain.php b/web/admin/domain.php index 6fedc5c..bc89eda 100644 --- a/web/admin/domain.php +++ b/web/admin/domain.php @@ -3,7 +3,7 @@ require_once __DIR__ . '/../includes/db.php'; require_once __DIR__ . '/../includes/auth.php'; require_once __DIR__ . '/../includes/layout.php'; -auth_check(); +admin_check(); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $fields = ['domain', 'hostname', 'smtp_port', 'submission_port', 'imap_port', 'imaps_port', 'smb_workgroup']; diff --git a/web/admin/index.php b/web/admin/index.php index 96887bd..dc0dc8d 100644 --- a/web/admin/index.php +++ b/web/admin/index.php @@ -3,7 +3,7 @@ require_once __DIR__ . '/../includes/db.php'; require_once __DIR__ . '/../includes/auth.php'; require_once __DIR__ . '/../includes/layout.php'; -auth_check(); +admin_check(); $services = [ 'postfix' => ['Postfix (SMTP)', 'bi-envelope-at'], diff --git a/web/admin/users.php b/web/admin/users.php new file mode 100644 index 0000000..64c1042 --- /dev/null +++ b/web/admin/users.php @@ -0,0 +1,232 @@ + 12]); + try { + db()->prepare('INSERT INTO admin_users (username, role, account_id, password) VALUES (?, ?, ?, ?)') + ->execute([$username, $role, $account_id, $hash]); + flash('success', "Utilisateur {$username} créé."); + } catch (Exception $e) { + $error = "L'utilisateur « {$username} » existe déjà."; + } + } + if (!$error) { header('Location: /admin/users.php'); exit; } + } + + if ($_POST['action'] === 'delete') { + $id = (int)$_POST['user_id']; + if ($id === (int)$_SESSION['admin_id']) { + flash('danger', 'Impossible de supprimer votre propre compte.'); + } else { + db()->prepare('DELETE FROM admin_users WHERE id = ?')->execute([$id]); + flash('success', 'Utilisateur supprimé.'); + } + header('Location: /admin/users.php'); exit; + } + + if ($_POST['action'] === 'edit') { + $id = (int)$_POST['user_id']; + $role = $_POST['role'] === 'admin' ? 'admin' : 'user'; + $account_id = ($role === 'user' && !empty($_POST['account_id'])) ? (int)$_POST['account_id'] : null; + $newPassword = trim($_POST['new_password'] ?? ''); + + db()->prepare('UPDATE admin_users SET role = ?, account_id = ? WHERE id = ?') + ->execute([$role, $account_id, $id]); + + if (strlen($newPassword) >= 6) { + $hash = password_hash($newPassword, PASSWORD_BCRYPT, ['cost' => 12]); + db()->prepare('UPDATE admin_users SET password = ? WHERE id = ?')->execute([$hash, $id]); + } + flash('success', 'Utilisateur mis à jour.'); + header('Location: /admin/users.php'); exit; + } +} + +$users = db()->query('SELECT u.*, a.username as account_name FROM admin_users u LEFT JOIN accounts a ON u.account_id = a.id ORDER BY u.role, u.username')->fetchAll(); +$accounts = db()->query('SELECT id, username, email FROM accounts WHERE active = 1 ORDER BY username')->fetchAll(); + +html_head('Utilisateurs'); +html_navbar('users'); +?> +
+ +
+ + +
+
+
Nouvel utilisateur
+
+ +
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
Si défini, l'utilisateur ne voit que les courriers de ce compte.
+
+ +
+
+
+
+ + +
+
+
+ Utilisateurs + +
+
+ + + + + + + + + + + + + + + + + + + +
UtilisateurRôleCompte associéActions
+ + admin + + user + + + tous' ?> + +
+ + +
+ + + +
+ +
+
+
+
+
+
+
+ + + + + + diff --git a/web/api/mails.php b/web/api/mails.php index 7aee669..874c5f5 100644 --- a/web/api/mails.php +++ b/web/api/mails.php @@ -5,15 +5,30 @@ require_once __DIR__ . '/../includes/auth.php'; auth_check(); header('Content-Type: application/json'); -$rows = db()->query( - 'SELECT sf.id, sf.title, sf.sender, sf.date_processing, a.username, - GROUP_CONCAT(f.id, ":", f.file_name, ":", f.path ORDER BY f.id SEPARATOR "|") as files - FROM source_file sf - LEFT JOIN accounts a ON sf.account_id = a.id - LEFT JOIN files f ON sf.id = f.source_file_id - GROUP BY sf.id - ORDER BY sf.date_processing DESC' -)->fetchAll(); +// Filtre par compte si user non-admin avec compte associé +$accountFilter = current_account_id(); +if ($accountFilter) { + $st = db()->prepare( + 'SELECT sf.id, sf.title, sf.sender, sf.date_processing, a.username, + GROUP_CONCAT(f.id, ":", f.file_name, ":", f.path ORDER BY f.id SEPARATOR "|") as files + FROM source_file sf + LEFT JOIN accounts a ON sf.account_id = a.id + LEFT JOIN files f ON sf.id = f.source_file_id + WHERE sf.account_id = ? + GROUP BY sf.id ORDER BY sf.date_processing DESC' + ); + $st->execute([$accountFilter]); + $rows = $st->fetchAll(); +} else { + $rows = db()->query( + 'SELECT sf.id, sf.title, sf.sender, sf.date_processing, a.username, + GROUP_CONCAT(f.id, ":", f.file_name, ":", f.path ORDER BY f.id SEPARATOR "|") as files + FROM source_file sf + LEFT JOIN accounts a ON sf.account_id = a.id + LEFT JOIN files f ON sf.id = f.source_file_id + GROUP BY sf.id ORDER BY sf.date_processing DESC' + )->fetchAll(); +} $toDelete = []; // source_file ids à supprimer diff --git a/web/includes/auth.php b/web/includes/auth.php index 4420795..790533e 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -15,13 +15,23 @@ function auth_check(): void { } } +function admin_check(): void { + auth_check(); + if ($_SESSION['admin_role'] !== 'admin') { + header('Location: /'); + exit; + } +} + function auth_login(string $username, string $password): bool { - $st = db()->prepare('SELECT id, password FROM admin_users WHERE username = ? LIMIT 1'); + $st = db()->prepare('SELECT id, role, account_id, password FROM admin_users WHERE username = ? LIMIT 1'); $st->execute([$username]); $row = $st->fetch(); if ($row && password_verify($password, $row['password'])) { - $_SESSION['admin_id'] = $row['id']; - $_SESSION['admin_user'] = $username; + $_SESSION['admin_id'] = $row['id']; + $_SESSION['admin_user'] = $username; + $_SESSION['admin_role'] = $row['role']; + $_SESSION['admin_account_id'] = $row['account_id']; return true; } return false; @@ -34,6 +44,9 @@ function auth_logout(): void { exit; } -function current_user(): string { - return $_SESSION['admin_user'] ?? ''; +function current_user(): string { return $_SESSION['admin_user'] ?? ''; } +function is_admin(): bool { return ($_SESSION['admin_role'] ?? '') === 'admin'; } +function current_account_id(): ?int { + $id = $_SESSION['admin_account_id'] ?? null; + return $id ? (int)$id : null; } diff --git a/web/includes/layout.php b/web/includes/layout.php index 5e5efc3..b2676e4 100644 --- a/web/includes/layout.php +++ b/web/includes/layout.php @@ -1,5 +1,7 @@ ['/', 'bi-envelope-fill', 'Courriers'], - 'accounts' => ['/admin/accounts.php', 'bi-people-fill', 'Comptes'], - 'domain' => ['/admin/domain.php', 'bi-gear-fill', 'Configuration'], - 'status' => ['/admin/index.php', 'bi-activity', 'Tableau de bord'], + 'mail' => ['/', 'bi-envelope-fill', 'Courriers'], ]; + if ($isAdmin) { + $pages['accounts'] = ['/admin/accounts.php', 'bi-people-fill', 'Comptes']; + $pages['domain'] = ['/admin/domain.php', 'bi-gear-fill', 'Configuration']; + $pages['status'] = ['/admin/index.php', 'bi-activity', 'Tableau de bord']; + $pages['users'] = ['/admin/users.php', 'bi-person-lock', 'Utilisateurs']; + } + + $roleBadge = $isAdmin + ? 'admin' + : 'user'; + echo ''; + echo ''; } function html_foot(): void {