query('SELECT id, username, role, email, must_change, created_at FROM users ORDER BY role DESC, username')->fetchAll(); $scopes = db()->query('SELECT id, user_id, com, depc FROM user_scopes ORDER BY com')->fetchAll(); $byUser = []; foreach ($scopes as $s) { $byUser[(int) $s['user_id']][] = ['id' => (int) $s['id'], 'com' => $s['com'], 'depc' => $s['depc']]; } foreach ($users as &$u) { $u['id'] = (int) $u['id']; $u['must_change'] = (int) $u['must_change']; $u['scopes'] = $byUser[$u['id']] ?? []; } json_out($users); } // Mise à jour de l'e-mail d'un compte. if ($method === 'POST' && $action === 'email') { $b = body_json(); $id = (int) ($b['id'] ?? 0); $email = trim((string) ($b['email'] ?? '')); if ($id <= 0) { json_error('id requis.'); } if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) { json_error('Adresse e-mail invalide.'); } db()->prepare('UPDATE users SET email = ? WHERE id = ?')->execute([$email !== '' ? $email : null, $id]); json_out(['ok' => true]); } if ($method === 'POST' && $action === 'scope') { $b = body_json(); $uid = (int) ($b['user_id'] ?? 0); $com = trim((string) ($b['com'] ?? '')); $depc = trim((string) ($b['depc'] ?? '')); if ($uid <= 0 || $com === '' || $depc === '') { json_error('user_id, com et depc requis.'); } db()->prepare('INSERT IGNORE INTO user_scopes (user_id, com, depc) VALUES (?, ?, ?)') ->execute([$uid, $com, $depc]); json_out(['ok' => true]); } if ($method === 'POST' && $action === 'password') { $b = body_json(); $id = (int) ($b['id'] ?? 0); $pwd = (string) ($b['password'] ?? ''); if ($id <= 0) { json_error('id requis.'); } if ($err = password_policy_check($pwd)) { json_error($err); } // Mot de passe posé par l'admin → l'utilisateur devra le changer à sa connexion. db()->prepare('UPDATE users SET password_hash = ?, must_change = 1, failed_attempts = 0, locked_until = NULL WHERE id = ?') ->execute([password_hash($pwd, PASSWORD_DEFAULT), $id]); json_out(['ok' => true]); } if ($method === 'POST') { $b = body_json(); $username = trim((string) ($b['username'] ?? '')); $pwd = (string) ($b['password'] ?? ''); $email = trim((string) ($b['email'] ?? '')); $roleIn = (string) ($b['role'] ?? 'perimeter'); $role = in_array($roleIn, ['super', 'perimeter', 'member'], true) ? $roleIn : 'perimeter'; if ($username === '') { json_error('Identifiant requis.'); } if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) { json_error('Adresse e-mail invalide.'); } if ($err = password_policy_check($pwd)) { json_error($err); } $exists = db()->prepare('SELECT 1 FROM users WHERE username = ?'); $exists->execute([$username]); if ($exists->fetchColumn()) { json_error('Cet identifiant existe déjà.'); } // Mot de passe initial posé par l'admin → changement forcé à la 1re connexion. db()->prepare('INSERT INTO users (username, password_hash, role, email, must_change) VALUES (?, ?, ?, ?, 1)') ->execute([$username, password_hash($pwd, PASSWORD_DEFAULT), $role, $email !== '' ? $email : null]); json_out(['id' => (int) db()->lastInsertId(), 'username' => $username, 'role' => $role], 201); } if ($method === 'DELETE' && $action === 'scope') { $id = (int) ($_GET['id'] ?? 0); if ($id <= 0) { json_error('id requis.'); } db()->prepare('DELETE FROM user_scopes WHERE id = ?')->execute([$id]); json_out(['deleted' => $id]); } if ($method === 'DELETE') { $id = (int) ($_GET['id'] ?? 0); if ($id <= 0) { json_error('id requis.'); } if ($id === (int) $me['id']) { json_error('Vous ne pouvez pas supprimer votre propre compte.'); } db()->prepare('DELETE FROM users WHERE id = ?')->execute([$id]); json_out(['deleted' => $id]); } json_error('Méthode non supportée.', 405); } catch (Throwable $e) { json_error('Erreur : ' . $e->getMessage(), 500); }