getSmtpConfig(); $emailConfig = $appConfig->getEmailConfig(); // Configuration du serveur $mail->isSMTP(); $mail->Host = $smtpConfig['host']; $mail->SMTPAuth = $smtpConfig['auth']; $mail->Username = $smtpConfig['user']; $mail->Password = $smtpConfig['pass']; $mail->SMTPSecure = $smtpConfig['secure']; $mail->Port = $smtpConfig['port']; // Configuration de base $mail->setFrom($emailConfig['from'], 'GEOSECTOR'); $mail->addAddress($email, $name); $mail->isHTML(true); $mail->CharSet = 'UTF-8'; // Configuration selon le type d'email switch ($type) { case 'welcome': $mail->Subject = 'Bienvenue sur GEOSECTOR'; $mail->Body = EmailTemplates::getWelcomeTemplate($name, $data['username'] ?? '', $data['password']); break; case 'welcome_username': $mail->Subject = 'GEOSECTOR - Votre identifiant de connexion'; $mail->Body = EmailTemplates::getWelcomeUsernameTemplate($name, $data['username'] ?? ''); break; case 'welcome_password': $mail->Subject = 'GEOSECTOR - Votre mot de passe'; $mail->Body = EmailTemplates::getWelcomePasswordTemplate($name, $data['password'] ?? ''); break; case 'lostpwd': $mail->Subject = 'Réinitialisation de votre mot de passe GEOSECTOR'; $mail->Body = EmailTemplates::getLostPasswordTemplate($name, $data['username'] ?? '', $data['password']); break; case 'alert': $mail->Subject = $data['subject'] ?? 'Alerte GEOSECTOR'; $mail->Body = EmailTemplates::getAlertTemplate($data['subject'] ?? 'Alerte', $data['message'] ?? ''); break; case 'receipt': $mail->Subject = 'Reçu de passage GEOSECTOR'; $mail->Body = EmailTemplates::getReceiptTemplate( $name, $data['date'] ?? date('d/m/Y'), $data['address'] ?? '', $data['amount'] ?? '0', $data['paymentMethod'] ?? 'Espèces' ); break; default: throw new Exception("Type d'email non reconnu"); } $mail->send(); LogService::log("Email '$type' envoyé avec succès", [ 'level' => 'info', 'email' => $email, 'type' => $type ]); return 1; } catch (Exception $e) { LogService::log("Échec d'envoi d'email", [ 'level' => 'error', 'error' => $e->getMessage(), 'email' => $email, 'type' => $type ]); return 0; } } // Pour les données qui servent de clé de recherche (comme l'email) public static function encryptSearchableData(string $data): string { if (empty($data)) { return ''; } // Forcer un padding cohérent en ajoutant un caractère spécial de contrôle $data = $data . "\x01"; // Garantit que même un bloc parfait reçoit du padding $keyBase64 = AppConfig::getInstance()->getEncryptionKey(); $key = base64_decode($keyBase64); // Décoder la clé base64 en binaire $iv = str_repeat("\0", 16); // IV fixe // Expliciter les options de padding $options = 0; // PKCS7 padding par défaut $encrypted = openssl_encrypt($data, 'AES-256-CBC', $key, $options, $iv); return base64_encode($encrypted); } // Pour les données qui ne servent pas de clé de recherche (comme le nom) public static function encryptData(string $data): string { if (empty($data)) { return ''; } $keyBase64 = AppConfig::getInstance()->getEncryptionKey(); $key = base64_decode($keyBase64); // Décoder la clé base64 en binaire $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('AES-256-CBC')); $encrypted = openssl_encrypt($data, 'AES-256-CBC', $key, 0, $iv); return base64_encode($iv . $encrypted); } public static function decryptSearchableData(string $encryptedData): string { if (empty($encryptedData)) { return ''; } // Décoder la chaîne base64 $encrypted = base64_decode($encryptedData); // Vérifier que le décodage base64 a fonctionné if ($encrypted === false) { return ''; // Échec du décodage, retourner une chaîne vide } // Méthode simple et robuste utilisant le même IV fixe que pour le chiffrement $keyBase64 = AppConfig::getInstance()->getEncryptionKey(); $key = base64_decode($keyBase64); // Décoder la clé base64 en binaire $iv = str_repeat("\0", 16); // IV fixe identique à celui utilisé pour le chiffrement // Déchiffrer avec la méthode standard $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $key, 0, $iv); // Si le déchiffrement a échoué, retourner une chaîne vide if ($decrypted === false) { return ''; } // Supprimer uniquement le caractère de contrôle ajouté if (substr($decrypted, -1) === "\x01") { return substr($decrypted, 0, -1); } return $decrypted; // Pour la rétrocompatibilité avec les anciennes données } public static function decryptData(string $encryptedData): string { if (empty($encryptedData)) { return ''; } $keyBase64 = AppConfig::getInstance()->getEncryptionKey(); $key = base64_decode($keyBase64); // Décoder la clé base64 en binaire $data = base64_decode($encryptedData); // Vérifier que le décodage base64 a fonctionné if ($data === false) { return $encryptedData; // Retourner la chaîne d'origine en cas d'échec } $ivLength = openssl_cipher_iv_length('AES-256-CBC'); // Vérifier que les données sont assez longues pour contenir l'IV if (strlen($data) <= $ivLength) { return $encryptedData; // Retourner la chaîne d'origine en cas d'échec } $iv = substr($data, 0, $ivLength); $encrypted = substr($data, $ivLength); $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $key, 0, $iv); // Si le déchiffrement échoue, retourner la chaîne d'origine return $decrypted !== false ? $decrypted : $encryptedData; } /** * Génère un nom d'utilisateur unique à partir du nom, du code postal et de la ville * * @param PDO $db Instance de la base de données * @param string $name Nom de l'utilisateur * @param string $postalCode Code postal * @param string $cityName Nom de la ville * @param int $minLength Longueur minimale du nom d'utilisateur (par défaut 10) * @return string Nom d'utilisateur généré */ public static function generateUserName(PDO $db, string $name, string $postalCode, string $cityName, int $minLength = 10): string { // Nettoyer et préparer les chaînes $name = preg_replace('/[^a-zA-Z0-9]/', '', strtolower($name)); $postalCode = preg_replace('/[^0-9]/', '', $postalCode); $cityName = preg_replace('/[^a-zA-Z0-9]/', '', strtolower($cityName)); // Extraire les premières lettres du nom (2 à 5 caractères) $nameLength = min(5, max(2, rand(2, strlen($name)))); $namePart = substr($name, 0, $nameLength); // Extraire une partie du code postal (2 à 3 chiffres) $postalLength = min(3, max(2, rand(2, strlen($postalCode)))); $postalPart = substr($postalCode, 0, $postalLength); // Extraire une partie du nom de la ville (2 à 4 caractères) $cityLength = min(4, max(2, rand(2, strlen($cityName)))); $cityPart = substr($cityName, 0, $cityLength); // Combiner les parties avec un séparateur aléatoire $separators = ['', '.', '_', '-']; $separator1 = $separators[array_rand($separators)]; $separator2 = $separators[array_rand($separators)]; // Ajouter un nombre aléatoire pour garantir l'unicité $randomNum = rand(10, 999); // Construire le nom d'utilisateur $username = $namePart . $separator1 . $postalPart . $separator2 . $cityPart . $randomNum; // S'assurer que la longueur minimale est respectée while (strlen($username) < $minLength) { $username .= rand(0, 9); } // Vérifier l'unicité du nom d'utilisateur dans la base de données $isUnique = false; $attempts = 0; $originalUsername = $username; while (!$isUnique && $attempts < 10) { // Chiffrer le nom d'utilisateur pour la recherche $encryptedUsername = self::encryptSearchableData($username); // Vérifier si le nom d'utilisateur existe déjà $stmt = $db->prepare('SELECT COUNT(*) as count FROM users WHERE encrypted_user_name = ?'); $stmt->execute([$encryptedUsername]); $result = $stmt->fetch(PDO::FETCH_ASSOC); if ($result && $result['count'] == 0) { $isUnique = true; } else { // Ajouter un nombre aléatoire supplémentaire $username = $originalUsername . rand(100, 999); $attempts++; } } return $username; } /** * Génère un mot de passe sécurisé aléatoire non compromis * Utilise le service PasswordSecurityService pour vérifier contre HIBP * * @param int $minLength Longueur minimale du mot de passe (par défaut 12) * @param int $maxLength Longueur maximale du mot de passe (par défaut 16) * @return string Mot de passe généré */ public static function generateSecurePassword(int $minLength = 12, int $maxLength = 16): string { $length = random_int($minLength, $maxLength); // Utiliser le nouveau service pour générer un mot de passe non compromis $password = PasswordSecurityService::generateSecurePassword($length, 10); // Si le service échoue (très rare), utiliser l'ancienne méthode if ($password === null) { LogService::log('Fallback vers génération de mot de passe classique', [ 'level' => 'warning', 'reason' => 'PasswordSecurityService a échoué' ]); $lowercase = 'abcdefghijklmnopqrstuvwxyz'; $uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $numbers = '0123456789'; $special = '!@#$%^&*()_+-=[]{}|;:,.<>?'; $password = ''; // Au moins un de chaque type $password .= $lowercase[random_int(0, strlen($lowercase) - 1)]; $password .= $uppercase[random_int(0, strlen($uppercase) - 1)]; $password .= $numbers[random_int(0, strlen($numbers) - 1)]; $password .= $special[random_int(0, strlen($special) - 1)]; // Compléter avec des caractères aléatoires $all = $lowercase . $uppercase . $numbers . $special; for ($i = strlen($password); $i < $length; $i++) { $password .= $all[random_int(0, strlen($all) - 1)]; } // Mélanger le mot de passe return str_shuffle($password); } return $password; } }