db = Database::getInstance(); $this->appConfig = AppConfig::getInstance(); } public function getUsers(): void { Session::requireAuth(); // Vérification des droits d'accès (rôle administrateur) // Récupérer le rôle de l'utilisateur depuis la base de données $userId = Session::getUserId(); $stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?'); $stmt->execute([$userId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); $userRole = $result ? $result['fk_role'] : null; if ($userRole != '1' && $userRole != '2') { // Supposons que 1 et 2 sont des rôles admin Response::json([ 'status' => 'error', 'message' => 'Accès non autorisé' ], 403); return; } try { $stmt = $this->db->prepare(' SELECT u.id, u.encrypted_email, u.encrypted_name, u.first_name, u.fk_role as role, u.fk_entite, u.chk_active, u.created_at, u.updated_at, e.encrypted_name as entite_name FROM users u LEFT JOIN entites e ON u.fk_entite = e.id ORDER BY u.created_at DESC '); $stmt->execute(); $users = $stmt->fetchAll(PDO::FETCH_ASSOC); // Déchiffrement des données sensibles pour chaque utilisateur foreach ($users as &$user) { $user['email'] = ApiService::decryptSearchableData($user['encrypted_email']); $user['name'] = ApiService::decryptData($user['encrypted_name']); if (!empty($user['entite_name'])) { $user['entite_name'] = ApiService::decryptData($user['entite_name']); } // Suppression des champs chiffrés unset($user['encrypted_email']); unset($user['encrypted_name']); } Response::json([ 'status' => 'success', 'users' => $users ]); } catch (PDOException $e) { LogService::log('Erreur lors de la récupération des utilisateurs GeoSector', [ 'level' => 'error', 'error' => $e->getMessage() ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function getUserById(string $id): void { Session::requireAuth(); // Vérification des droits d'accès (rôle administrateur ou utilisateur lui-même) $currentUserId = Session::getUserId(); // Récupérer le rôle de l'utilisateur depuis la base de données $stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); $userRole = $result ? $result['fk_role'] : null; if ($userRole != '1' && $userRole != '2' && $currentUserId != $id) { Response::json([ 'status' => 'error', 'message' => 'Accès non autorisé' ], 403); return; } try { $stmt = $this->db->prepare(' SELECT u.id, u.encrypted_email, u.encrypted_name, u.first_name, u.sect_name, u.encrypted_phone, u.encrypted_mobile, u.fk_role as role, u.fk_entite, u.chk_alert_email, u.chk_suivi, u.date_naissance, u.date_embauche, u.chk_active, u.created_at, u.updated_at, e.encrypted_name as entite_name, e.adresse1, e.adresse2, e.cp, e.ville, e.fk_region FROM users u LEFT JOIN entites e ON u.fk_entite = e.id WHERE u.id = ? '); $stmt->execute([$id]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur non trouvé' ], 404); return; } // Déchiffrement des données sensibles $user['email'] = ApiService::decryptSearchableData($user['encrypted_email']); $user['name'] = ApiService::decryptData($user['encrypted_name']); $user['phone'] = ApiService::decryptData($user['encrypted_phone'] ?? ''); $user['mobile'] = ApiService::decryptData($user['encrypted_mobile'] ?? ''); if (!empty($user['entite_name'])) { $user['entite_name'] = ApiService::decryptData($user['entite_name']); } // Suppression des champs chiffrés unset($user['encrypted_email']); unset($user['encrypted_name']); unset($user['encrypted_phone']); unset($user['encrypted_mobile']); Response::json([ 'status' => 'success', 'user' => $user ]); } catch (PDOException $e) { LogService::log('Erreur lors de la récupération de l\'utilisateur GeoSector', [ 'level' => 'error', 'error' => $e->getMessage(), 'userId' => $id ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function createUser(): void { Session::requireAuth(); // Vérification des droits d'accès (rôle administrateur) $currentUserId = Session::getUserId(); // Récupérer le rôle de l'utilisateur depuis la base de données $stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); $userRole = $result ? $result['fk_role'] : null; if ($userRole != '1' && $userRole != '2') { Response::json([ 'status' => 'error', 'message' => 'Accès non autorisé' ], 403); return; } try { $data = Request::getJson(); $currentUserId = Session::getUserId(); // Validation des données requises if (!isset($data['email'], $data['name'])) { Response::json([ 'status' => 'error', 'message' => 'Email et nom requis' ], 400); return; } $email = trim(strtolower($data['email'])); $name = trim($data['name']); $firstName = isset($data['first_name']) ? trim($data['first_name']) : ''; $role = isset($data['role']) ? (int)$data['role'] : 1; $entiteId = isset($data['fk_entite']) ? (int)$data['fk_entite'] : 1; // Vérification des longueurs d'entrée if (strlen($email) > 75 || strlen($name) > 50) { Response::json([ 'status' => 'error', 'message' => 'Email ou nom trop long' ], 400); return; } // Validation de l'email if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { Response::json([ 'status' => 'error', 'message' => 'Format d\'email invalide' ], 400); return; } // Chiffrement des données sensibles $encryptedEmail = ApiService::encryptSearchableData($email); $encryptedName = ApiService::encryptData($name); // Vérification de l'existence de l'email $checkStmt = $this->db->prepare('SELECT id FROM users WHERE encrypted_email = ?'); $checkStmt->execute([$encryptedEmail]); if ($checkStmt->fetch()) { Response::json([ 'status' => 'error', 'message' => 'Cet email est déjà utilisé' ], 409); return; } // Génération du mot de passe $password = ApiService::generateSecurePassword(); $passwordHash = password_hash($password, PASSWORD_DEFAULT); // Préparation des champs optionnels $phone = isset($data['phone']) ? ApiService::encryptData(trim($data['phone'])) : null; $mobile = isset($data['mobile']) ? ApiService::encryptData(trim($data['mobile'])) : null; $sectName = isset($data['sect_name']) ? trim($data['sect_name']) : ''; $alertEmail = isset($data['chk_alert_email']) ? (int)$data['chk_alert_email'] : 1; $suivi = isset($data['chk_suivi']) ? (int)$data['chk_suivi'] : 0; $dateNaissance = isset($data['date_naissance']) ? $data['date_naissance'] : null; $dateEmbauche = isset($data['date_embauche']) ? $data['date_embauche'] : null; // Insertion en base de données $stmt = $this->db->prepare(' INSERT INTO users ( encrypted_email, user_pass_hash, encrypted_name, first_name, sect_name, encrypted_phone, encrypted_mobile, fk_role, fk_entite, chk_alert_email, chk_suivi, date_naissance, date_embauche, created_at, fk_user_creat, chk_active ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?, 1 ) '); $stmt->execute([ $encryptedEmail, $passwordHash, $encryptedName, $firstName, $sectName, $phone, $mobile, $role, $entiteId, $alertEmail, $suivi, $dateNaissance, $dateEmbauche, $currentUserId ]); $userId = $this->db->lastInsertId(); // Envoi de l'email avec les identifiants ApiService::sendEmail($email, $name, 'welcome', ['password' => $password]); LogService::log('Utilisateur GeoSector créé', [ 'level' => 'info', 'createdBy' => $currentUserId, 'newUserId' => $userId, 'email' => $email ]); Response::json([ 'status' => 'success', 'message' => 'Utilisateur créé avec succès', 'id' => $userId ], 201); } catch (PDOException $e) { LogService::log('Erreur lors de la création d\'un utilisateur GeoSector', [ 'level' => 'error', 'error' => $e->getMessage(), 'code' => $e->getCode() ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function updateUser(string $id): void { Session::requireAuth(); // Vérification des droits d'accès (rôle administrateur ou utilisateur lui-même) $currentUserId = Session::getUserId(); // Récupérer le rôle de l'utilisateur depuis la base de données $stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); $userRole = $result ? $result['fk_role'] : null; if ($userRole != '1' && $userRole != '2' && $currentUserId != $id) { Response::json([ 'status' => 'error', 'message' => 'Accès non autorisé' ], 403); return; } try { $data = Request::getJson(); // Vérification qu'il y a des données à mettre à jour if (empty($data)) { Response::json([ 'status' => 'error', 'message' => 'Aucune donnée à mettre à jour' ], 400); return; } // Construction de la requête UPDATE dynamique $updateFields = []; $params = ['id' => $id]; // Traitement des champs à chiffrer if (isset($data['email'])) { // Vérification que l'email n'est pas déjà utilisé par un autre utilisateur $email = trim(strtolower($data['email'])); $encryptedEmail = ApiService::encryptSearchableData($email); $checkStmt = $this->db->prepare('SELECT id FROM users WHERE encrypted_email = ? AND id != ?'); $checkStmt->execute([$encryptedEmail, $id]); if ($checkStmt->fetch()) { Response::json([ 'status' => 'error', 'message' => 'Cet email est déjà utilisé par un autre utilisateur' ], 409); return; } $updateFields[] = "encrypted_email = :encrypted_email"; $params['encrypted_email'] = $encryptedEmail; } if (isset($data['name'])) { $updateFields[] = "encrypted_name = :encrypted_name"; $params['encrypted_name'] = ApiService::encryptData(trim($data['name'])); } if (isset($data['phone'])) { $updateFields[] = "encrypted_phone = :encrypted_phone"; $params['encrypted_phone'] = ApiService::encryptData(trim($data['phone'])); } if (isset($data['mobile'])) { $updateFields[] = "encrypted_mobile = :encrypted_mobile"; $params['encrypted_mobile'] = ApiService::encryptData(trim($data['mobile'])); } // Traitement des champs non chiffrés $nonEncryptedFields = [ 'first_name', 'sect_name', 'fk_role', 'fk_entite', 'chk_alert_email', 'chk_suivi', 'date_naissance', 'date_embauche', 'chk_active' ]; foreach ($nonEncryptedFields as $field) { if (isset($data[$field])) { $updateFields[] = "$field = :$field"; $params[$field] = is_string($data[$field]) ? trim($data[$field]) : $data[$field]; } } // Mise à jour du mot de passe si fourni if (isset($data['password']) && !empty($data['password'])) { if (strlen($data['password']) < 8) { Response::json([ 'status' => 'error', 'message' => 'Le mot de passe doit contenir au moins 8 caractères' ], 400); return; } $updateFields[] = "user_pass_hash = :password"; $params['password'] = password_hash($data['password'], PASSWORD_DEFAULT); } // Ajout des champs de mise à jour $updateFields[] = "updated_at = NOW()"; $updateFields[] = "fk_user_modif = :modifier_id"; $params['modifier_id'] = $currentUserId; if (!empty($updateFields)) { $sql = 'UPDATE users SET ' . implode(', ', $updateFields) . ' WHERE id = :id'; $stmt = $this->db->prepare($sql); $stmt->execute($params); if ($stmt->rowCount() === 0) { Response::json([ 'status' => 'warning', 'message' => 'Aucune modification effectuée' ]); return; } LogService::log('Utilisateur GeoSector mis à jour', [ 'level' => 'info', 'modifiedBy' => $currentUserId, 'userId' => $id, 'fields' => array_keys($data), ]); Response::json([ 'status' => 'success', 'message' => 'Utilisateur mis à jour avec succès' ]); } else { Response::json([ 'status' => 'warning', 'message' => 'Aucune donnée valide à mettre à jour' ]); } } catch (PDOException $e) { LogService::log('Erreur lors de la mise à jour d\'un utilisateur GeoSector', [ 'level' => 'error', 'error' => $e->getMessage(), 'userId' => $id ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function deleteUser(string $id): void { Session::requireAuth(); $currentUserId = Session::getUserId(); // Récupérer les infos de l'utilisateur courant $stmt = $this->db->prepare('SELECT fk_role, fk_entite FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $currentUser = $stmt->fetch(PDO::FETCH_ASSOC); if (!$currentUser) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur courant non trouvé' ], 403); return; } $userRole = (int)$currentUser['fk_role']; $userEntite = $currentUser['fk_entite']; // Empêcher la suppression de son propre compte if ($currentUserId == $id) { Response::json([ 'status' => 'error', 'message' => 'Vous ne pouvez pas supprimer votre propre compte' ], 400); return; } // Récupérer l'utilisateur cible $stmt2 = $this->db->prepare('SELECT fk_entite FROM users WHERE id = ?'); $stmt2->execute([$id]); $userToDelete = $stmt2->fetch(PDO::FETCH_ASSOC); if (!$userToDelete) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur cible non trouvé' ], 404); return; } // Contrôle des droits if ($userRole === 1) { Response::json([ 'status' => 'error', 'message' => "Vous n'avez pas le droit de supprimer un utilisateur" ], 403); return; } elseif ($userRole === 2) { if ($userEntite != $userToDelete['fk_entite']) { Response::json([ 'status' => 'error', 'message' => "Vous n'avez pas le droit de supprimer un utilisateur d'une autre amicale" ], 403); return; } } // fk_role > 2 : tout est permis (hors auto-suppression) // ——— Gestion du transfert éventuel ——— $transferTo = isset($_GET['transfer_to']) ? trim($_GET['transfer_to']) : null; if ($transferTo) { try { // Transférer TOUS les passages de l'utilisateur vers l'utilisateur désigné $stmt3 = $this->db->prepare(' UPDATE ope_pass SET fk_user = :new_user_id WHERE fk_user = :delete_user_id '); $stmt3->execute([ 'new_user_id' => $transferTo, 'delete_user_id' => $id ]); $transferredCount = $stmt3->rowCount(); LogService::log('Passages transférés avant suppression utilisateur', [ 'level' => 'info', 'from_user' => $id, 'to_user' => $transferTo, 'passages_transferred' => $transferredCount ]); } catch (PDOException $e) { Response::json([ 'status' => 'error', 'message' => 'Erreur lors du transfert des passages', 'error' => $e->getMessage() ], 500); return; } } // —— Suppression réelle de l'utilisateur —— try { // Supprimer les enregistrements dépendants dans ope_users $stmtOpeUsers = $this->db->prepare('DELETE FROM ope_users WHERE fk_user = ?'); $stmtOpeUsers->execute([$id]); // Supprimer les enregistrements dépendants dans ope_users_sectors $stmtOpeUsersSectors = $this->db->prepare('DELETE FROM ope_users_sectors WHERE fk_user = ?'); $stmtOpeUsersSectors->execute([$id]); $stmt = $this->db->prepare('DELETE FROM users WHERE id = ?'); $stmt->execute([$id]); if ($stmt->rowCount() === 0) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur non trouvé ou déjà supprimé' ], 404); return; } LogService::log('Utilisateur GeoSector supprimé', [ 'level' => 'info', 'deletedBy' => $currentUserId, 'userId' => $id, 'passage_transfer' => $transferTo ? "Tous les passages transférés vers utilisateur $transferTo" : 'Aucun transfert' ]); Response::json([ 'status' => 'success', 'message' => 'Utilisateur supprimé avec succès' ]); } catch (PDOException $e) { LogService::log('Erreur lors de la suppression d\'un utilisateur GeoSector', [ 'level' => 'error', 'error' => $e->getMessage(), 'userId' => $id ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function resetPassword(string $id): void { Session::requireAuth(); $currentUserId = Session::getUserId(); // Récupérer les infos de l'utilisateur courant $stmt = $this->db->prepare('SELECT fk_role, fk_entite, chk_active FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $currentUser = $stmt->fetch(PDO::FETCH_ASSOC); if (!$currentUser) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur courant non trouvé' ], 403); return; } // Vérifier que l'utilisateur courant est actif if ($currentUser['chk_active'] != 1) { Response::json([ 'status' => 'error', 'message' => 'Votre compte n\'est pas actif' ], 403); return; } $userRole = (int)$currentUser['fk_role']; $userEntite = $currentUser['fk_entite']; // Récupérer l'utilisateur cible $stmt = $this->db->prepare(' SELECT id, encrypted_email, encrypted_name, fk_entite, chk_active FROM users WHERE id = ? '); $stmt->execute([$id]); $targetUser = $stmt->fetch(PDO::FETCH_ASSOC); if (!$targetUser) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur non trouvé' ], 404); return; } // Vérifier que l'utilisateur est actif if ($targetUser['chk_active'] != 1) { Response::json([ 'status' => 'error', 'message' => 'L\'utilisateur n\'est pas actif' ], 400); return; } // Contrôle des droits selon le rôle if ($userRole === 1) { // Role 1 : peut uniquement réinitialiser son propre mot de passe if ($currentUserId != $id) { Response::json([ 'status' => 'error', 'message' => 'Vous ne pouvez réinitialiser que votre propre mot de passe' ], 403); return; } } elseif ($userRole === 2) { // Role 2 : peut réinitialiser les mots de passe de sa propre entité if ($userEntite != $targetUser['fk_entite']) { Response::json([ 'status' => 'error', 'message' => 'Vous ne pouvez réinitialiser que les mots de passe des utilisateurs de votre entité' ], 403); return; } } // Role > 2 : peut tout faire try { // Déchiffrement des données $email = ApiService::decryptSearchableData($targetUser['encrypted_email']); $name = ApiService::decryptData($targetUser['encrypted_name']); // Génération d'un nouveau mot de passe sécurisé $newPassword = ApiService::generateSecurePassword(); $passwordHash = password_hash($newPassword, PASSWORD_DEFAULT); // Mise à jour du mot de passe en base de données $updateStmt = $this->db->prepare(' UPDATE users SET user_pass_hash = :password, updated_at = NOW(), fk_user_modif = :modifier_id WHERE id = :id '); $updateStmt->execute([ 'password' => $passwordHash, 'modifier_id' => $currentUserId, 'id' => $id ]); // Envoi de l'email avec le nouveau mot de passe ApiService::sendEmail($email, $name, 'password_reset', ['password' => $newPassword]); LogService::log('Mot de passe réinitialisé', [ 'level' => 'info', 'resetBy' => $currentUserId, 'userId' => $id, 'email' => $email ]); Response::json([ 'status' => 'success', 'message' => 'Mot de passe réinitialisé avec succès. Un email a été envoyé à l\'utilisateur.' ]); } catch (PDOException $e) { LogService::log('Erreur lors de la réinitialisation du mot de passe', [ 'level' => 'error', 'error' => $e->getMessage(), 'userId' => $id ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } // Méthodes auxiliaires private function validateUpdateData(array $data): ?string { // Validation de l'email if (isset($data['email'])) { if (!filter_var(trim($data['email']), FILTER_VALIDATE_EMAIL)) { return 'Format d\'email invalide'; } } // Validation du nom if (isset($data['name']) && strlen(trim($data['name'])) < 2) { return 'Le nom doit contenir au moins 2 caractères'; } // Validation du téléphone if (isset($data['phone']) && !empty($data['phone'])) { if (!preg_match('/^[0-9+\s()-]{6,20}$/', trim($data['phone']))) { return 'Format de téléphone invalide'; } } // Validation du mobile if (isset($data['mobile']) && !empty($data['mobile'])) { if (!preg_match('/^[0-9+\s()-]{6,20}$/', trim($data['mobile']))) { return 'Format de mobile invalide'; } } return null; } }