From dba24b8c18aed84a71c3169b2df5598d62deab06 Mon Sep 17 00:00:00 2001 From: polo Date: Sun, 17 Aug 2025 19:46:20 +0200 Subject: =?UTF-8?q?classe=20FormValidation=20et=20am=C3=A9lioration=20des?= =?UTF-8?q?=20envois=20d'e-mail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/ContactFormController.php | 40 ++++- src/controller/EmailController.php | 91 ---------- src/controller/UserController.php | 284 +++++++++---------------------- 3 files changed, 122 insertions(+), 293 deletions(-) delete mode 100644 src/controller/EmailController.php (limited to 'src/controller') diff --git a/src/controller/ContactFormController.php b/src/controller/ContactFormController.php index 9d62a77..dcea868 100644 --- a/src/controller/ContactFormController.php +++ b/src/controller/ContactFormController.php @@ -27,17 +27,53 @@ class ContactFormController } die; } + static public function sendVisitorEmail(EntityManager $entityManager, array $json): void + { + $form = new FormValidation($json, 'email'); + + $error = ''; + if($form->validate()){ + // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur + $form_data = $entityManager->find('App\Entity\NodeData', $json['id']); + if($form_data === null){ + http_response_code(500); + echo json_encode(['success' => false, 'error' => 'server_error']); + die; + } + $recipient = $form_data->getData()['email'] ?? Config::$email_dest; + + if(!EmailService::send($entityManager, $recipient, true, $form->getField('name'), $form->getField('email'), $form->getField('message'))){ + $error = 'email_not_sent'; + } + } + else{ + $error = $form->getErrors()[0]; // la 1ère erreur sera affichée + } + + if(empty($error)){ + echo json_encode(['success' => true]); + } + else{ + echo json_encode(['success' => false, 'error' => $error]); + } + die; + } static public function sendTestEmail(EntityManager $entityManager, array $json): void { // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur $form_data = $entityManager->find('App\Entity\NodeData', $json['id']); + if($form_data === null){ + http_response_code(500); + echo json_encode(['success' => false, 'error' => 'server_error']); + die; + } $recipient = $form_data->getData()['email'] ?? Config::$email_dest; - if(EmailController::send($recipient, false, 'nom du visiteur', 'adresse@du_visiteur.fr', "TEST d'un envoi d'e-mail depuis le site web")){ + if(EmailService::send($entityManager, $recipient, false, 'nom du visiteur', 'adresse@du_visiteur.fr', "TEST d'un envoi d'e-mail depuis le site web")){ echo json_encode(['success' => true]); } else{ - echo json_encode(['success' => false]); + echo json_encode(['success' => false, 'error' => 'email_not_sent']); } die; } diff --git a/src/controller/EmailController.php b/src/controller/EmailController.php deleted file mode 100644 index 1eea257..0000000 --- a/src/controller/EmailController.php +++ /dev/null @@ -1,91 +0,0 @@ - exceptions - $mail->CharSet = 'UTF-8'; - - try{ - // Paramètres du serveur - $mail->isSMTP(); - $mail->Host = Config::$smtp_host; - $mail->SMTPAuth = true; - $mail->Port = 25; - - if($mail->SMTPAuth){ - $mail->Username = Config::$smtp_username; // e-mail - $mail->Password = Config::$smtp_password; - $mail->SMTPSecure = Config::$smtp_secure; // tls (starttls) ou ssl (smtps) - if($mail->SMTPSecure === 'tls'){ - $mail->Port = 587; - } - elseif($mail->SMTPSecure === 'ssl'){ - $mail->Port = 465; - } - } - //var_dump($mail->smtpConnect());die; // test de connexion - - // Expéditeur et destinataire - $mail->setFrom(strtolower(Config::$email_from), Config::$email_from_name); // expéditeur - $mail->addAddress(strtolower($recipient), Config::$email_dest_name); // destinataire - - // Contenu - $mail->isHTML(true); - if($true_email){ - $mail->Subject = 'Message envoyé par: ' . $name . ' (' . $email . ') depuis le site web'; - - } - else{ - $mail->Subject = "TEST d'un envoi d'e-mail depuis le site web"; - } - $mail->Body = $message; - $mail->AltBody = $message; - - $mail->send(); - return true; - } - catch(Exception $e){ - return false; - //echo "Le message n'a pas pu être envoyé. Erreur : {$mail->ErrorInfo}"; - } - } - - static public function submit(array $json, EntityManager $entityManager): void - { - $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; - $captcha_try = isset($json['captcha']) ? Captcha::controlInput($json['captcha']) : 0; - - // contrôles des entrées - $name = htmlspecialchars(trim($json['name'])); - $email = strtolower(htmlspecialchars(trim($json['email']))); - $message = htmlspecialchars(trim($json['message'])); - - // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur - $form_data = $entityManager->find('App\Entity\NodeData', $json['id']); - $recipient = $form_data->getData()['email'] ?? Config::$email_dest; - - if($captcha_try != 0 && $captcha_solution != 0 && ($captcha_try === $captcha_solution) - && filter_var($email, FILTER_VALIDATE_EMAIL) && isset($json['hidden']) && empty($json['hidden']) - && self::send($recipient, true, $name, $email, $message)) - { - $db_email = new Email($email, Config::$email_dest, $message); - $entityManager->persist($db_email); - $entityManager->flush(); - echo json_encode(['success' => true]); - } - else{ - echo json_encode(['success' => false]); - } - die; - } -} \ No newline at end of file diff --git a/src/controller/UserController.php b/src/controller/UserController.php index 50e8bf7..f0880bb 100644 --- a/src/controller/UserController.php +++ b/src/controller/UserController.php @@ -1,7 +1,15 @@ UserController devrait se limiter à gérer des requêtes et réponses (changement transparent pour le routeur) +il instancierait un classe correspondant au formulaire (prend POST et SESSION) et une classe "métier" UserService +=> UserService contiendrait des méthodes utilisant l'objet formulaire pour agir sur la BDD +=> les formulaires peuvent tenir dans une classe "UserUpdateForm" avec des stratégies ou plusieurs, les données y sont validées +=> il est aussi possible de découper UserController en contrôleurs par fonctionnalité: +Auth (connexion, deconnexion), User (infos, choix photo), Account (créer, supprimer compte), Avatar (upload photo...) +*/ declare(strict_types=1); @@ -11,6 +19,7 @@ use App\Entity\Log; class UserController { + // account static public function existUsers(EntityManager $entityManager): bool { // optimiser ça si possible, on veut juste savoir si la table est vide ou non @@ -28,71 +37,34 @@ class UserController } } + // account static public function createUser(EntityManager $entityManager) { - // test mauvais paramètres - if(!isset($_POST['login']) || empty($_POST['login']) - || !isset($_POST['password']) || empty($_POST['password']) - || !isset($_POST['password_confirmation']) || empty($_POST['password_confirmation']) - || !isset($_POST['create_user_hidden']) || !empty($_POST['create_user_hidden'])) - { - header('Location: ' . new URL); - die; - } - unset($_SESSION['user']); - $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; - $captcha_try = isset($_POST['captcha']) ? Captcha::controlInput($_POST['captcha']) : 0; - unset($_SESSION['captcha']); + $form = new FormValidation($_POST, 'create_user'); + $url = new URL; $error = ''; - if($captcha_try == 0) - { - $error = 'error_non_valid_captcha'; - } - elseif($captcha_solution == 0) // ne peut pas arriver, si? - { - $error = 'captcha_server_error'; - } - elseif($captcha_try != $captcha_solution) // le test! - { - $error = 'bad_solution_captcha'; + if($form->validate()){ + $password = password_hash($_POST['password'], PASSWORD_DEFAULT); + $user = new App\Entity\User($_POST['login'], $password); + $entityManager->persist($user); + $entityManager->flush(); } - elseif($_POST['password'] !== $_POST['password_confirmation']) - { - $error = 'different_passwords'; + else{ + $error = $form->getErrors()[0]; // la 1ère erreur sera affichée } - else - { - $login = self::removeSpacesTabsCRLF(htmlspecialchars($_POST['login'])); - $password = self::removeSpacesTabsCRLF(htmlspecialchars($_POST['password'])); - - // self::removeSpacesTabsCRLF prévient la validation par erreur d'une chaine "vide" - - // conformité - if(!empty($password) && !empty($login) - && $password === $_POST['password'] && $login === $_POST['login']) - { - // enregistrement et redirection - $password = password_hash($password, PASSWORD_DEFAULT); - $user = new App\Entity\User($login, $password); - $entityManager->persist($user); - $entityManager->flush(); - - header('Location: ' . new URL); - die; - } - else{ - $error = 'forbidden_characters'; - } + + if(!empty($error)){ + $url->addParams(['error' => $error]); } - $url = empty($error) ? new URL : new URL(['error' => $error]); header('Location: ' . $url); die; } + // auth static public function connect(EntityManager $entityManager): void { if($_SESSION['admin']) // déjà connecté? @@ -100,74 +72,52 @@ class UserController header('Location: ' . new URL); die; } - $_SESSION['user'] = ''; $_SESSION['admin'] = false; - $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; - $captcha_try = isset($_POST['captcha']) ? Captcha::controlInput($_POST['captcha']) : 0; - unset($_SESSION['captcha']); + $form = new FormValidation($_POST, 'connection'); $error = ''; - if($captcha_try == 0) - { - $error = 'error_non_valid_captcha'; - } - elseif($captcha_solution == 0) // pas censé se produire - { - $error = 'captcha_server_error'; - } - elseif($captcha_try != $captcha_solution) // le test! - { - $error = 'bad_solution_captcha'; - } - elseif(!isset($_POST['login']) || empty($_POST['login']) - || !isset($_POST['password']) || empty($_POST['password'])) - { - $error = 'bad_login_or_password'; - } - else // c'est OK - { - $login = trim($_POST['login']); - $password = trim($_POST['password']); - $user = self::getUser($login, $entityManager); - - // enregistrement et redirection - if(!empty($user) && $login === $user->getLogin() && password_verify($password, $user->getPassword())) + if($form->validate()){ + // à mettre dans une classe métier UserService, Authentication, AuthService? + $user = self::getUser($_POST['login'], $entityManager); + if(!empty($user) && $_POST['login'] === $user->getLogin() && password_verify($_POST['password'], $user->getPassword())) { $log = new Log(true); - $entityManager->persist($log); - $entityManager->flush(); // protection fixation de session, si l'attaquant crée un cookie de session, il est remplacé session_regenerate_id(true); - - $_SESSION['user'] = $login; + $_SESSION['user'] = $_POST['login']; $_SESSION['admin'] = true; $url = new URL(isset($_GET['from']) ? ['page' => $_GET['from']] : []); isset($_GET['id']) ? $url->addParams(['id' => $_GET['id']]) : ''; - header('Location: ' . $url); - die; } else { $log = new Log(false); - $entityManager->persist($log); - $entityManager->flush(); $error = 'bad_login_or_password'; } + $entityManager->persist($log); + $entityManager->flush(); + } + else{ + $error = $form->getErrors()[0]; // la 1ère erreur sera affichée + } + + if(!empty($error)){ + sleep(1); // défense basique à la force brute + $url = new URL(['page' => 'connexion']); + isset($_GET['from']) ? $url->addParams(['from' => $_GET['from']]) : null; + isset($_GET['id']) ? $url->addParams(['id' => $_GET['id']]) : null; + $url->addParams(['error' => $error]); } - // tous les cas sauf connexion réussie - sleep(1); // défense basique à la force brute - $url = new URL(isset($_GET['from']) ? ['page' => 'connexion', 'from' => $_GET['from']] : []); - isset($_GET['id']) ? $url->addParams(['id' => $_GET['id']]) : ''; - !empty($error) ? $url->addParams(['error' => $error]) : ''; header('Location: ' . $url); die; } + // auth static public function disconnect(): void { // nettoyage complet @@ -183,154 +133,87 @@ class UserController die; } + // user static public function updateUsername(EntityManager $entityManager): void { - if(!$_SESSION['admin']) // superflux, fait dans le routeur - { + if(!$_SESSION['admin']){ // superflux, fait dans le routeur self::disconnect(); } - $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; - $captcha_try = isset($_POST['captcha']) ? Captcha::controlInput($_POST['captcha']) : 0; - unset($_SESSION['captcha']); - $url = new URL(['page' => 'user_edit']); isset($_GET['from']) ? $url->addParams(['from' => $_GET['from']]) : null; + $form = new FormValidation($_POST, 'username_update'); + $error = ''; - if(!isset($_POST['old_login']) || empty($_POST['old_login']) - || !isset($_POST['password']) || empty($_POST['password']) - || !isset($_POST['new_login']) || empty($_POST['new_login']) - || !isset($_POST['modify_username_hidden']) || !empty($_POST['modify_username_hidden'])) - { - $error = 'bad_login_or_password'; - } - elseif($captcha_try != $captcha_solution) // le test! - { - $error = 'bad_solution_captcha'; - } - else - { - // sécurisation de la saisie - $old_login = $_POST['old_login']; - $password = $_POST['password']; - $new_login = self::removeSpacesTabsCRLF(htmlspecialchars($_POST['new_login'])); - // removeSpacesTabsCRLF pour éviter d'enregistrer une chaîne vide + if($form->validate()){ + // à mettre dans une classe métier UserService? + $user = self::getUser($_POST['login'], $entityManager); + if(password_verify($_POST['password'], $user->getPassword())){ + $user->setLogin($_POST['new_login']); + $entityManager->flush(); + $_SESSION['user'] = $_POST['new_login']; - // tests de conformité - if($old_login !== $_POST['old_login'] || $password !== $_POST['password'] || $new_login !== $_POST['new_login']) - { - $error = 'forbidden_characters'; + $url->addParams(['success_username' => 'new_login']); + $error = ''; } - elseif($old_login !== $_SESSION['user']){ + else{ $error = 'bad_login_or_password'; } - elseif($old_login === $new_login){ - $error = 'same_username_as_before'; - } - else - { - $user = self::getUser($old_login, $entityManager); - - if(password_verify($password, $user->getPassword())) - { - $user->setLogin($new_login); - $entityManager->flush(); - $_SESSION['user'] = $new_login; - - $url->addParams(['success_login' => 'new_login']); - $error = ''; - } - else - { - $error = 'bad_login_or_password'; - } - } } - + else{ + $error = $form->getErrors()[0]; // la 1ère erreur sera affichée + } + if(!empty($error)){ sleep(1); - $url->addParams(['error_login' => $error]); + $url->addParams(['error_username' => $error]); } - header('Location: ' . $url); die; } + // user static public function updatePassword(EntityManager $entityManager): void { - if(!$_SESSION['admin']) // superflux, fait dans le routeur - { + if(!$_SESSION['admin']){ // superflux, fait dans le routeur self::disconnect(); } - $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; - $captcha_try = isset($_POST['captcha']) ? Captcha::controlInput($_POST['captcha']) : 0; - unset($_SESSION['captcha']); - $url = new URL(['page' => 'user_edit']); isset($_GET['from']) ? $url->addParams(['from' => $_GET['from']]) : null; + $form = new FormValidation($_POST, 'password_update'); + $error = ''; - if(!isset($_POST['login']) || empty($_POST['login']) - || !isset($_POST['old_password']) || empty($_POST['old_password']) - || !isset($_POST['new_password']) || empty($_POST['new_password']) - || !isset($_POST['modify_password_hidden']) || !empty($_POST['modify_password_hidden'])) - { - $error = 'bad_login_or_password'; - } - elseif($captcha_try != $captcha_solution) // le test! - { - $error = 'bad_solution_captcha'; - } - else - { - // sécurisation de la saisie - $login = $_POST['login']; - $old_password = $_POST['old_password']; - $new_password = self::removeSpacesTabsCRLF(htmlspecialchars($_POST['new_password'])); - // removeSpacesTabsCRLF pour éviter d'enregistrer une chaîne vide + if($form->validate()){ + // à mettre dans une classe métier UserService? + $user = self::getUser($_POST['login'], $entityManager); + if(password_verify($_POST['password'], $user->getPassword())){ + $new_password = password_hash($_POST['new_password'], PASSWORD_DEFAULT); + $user->setPassword($new_password); + $entityManager->flush(); - // tests de conformité - if($login !== $_POST['login'] || $old_password !== $_POST['old_password'] || $new_password !== $_POST['new_password']) - { - $error = 'forbidden_characters'; + $url->addParams(['success_password' => 'new_password']); + $error = ''; } - elseif($login !== $_SESSION['user']){ + else{ $error = 'bad_login_or_password'; } - elseif($old_password === $new_password){ - $error = 'same_password_as_before'; - } - else - { - $user = self::getUser($login, $entityManager); - - if(password_verify($old_password, $user->getPassword())) - { - $new_password = password_hash($new_password, PASSWORD_DEFAULT); - $user->setPassword($new_password); - $entityManager->flush(); - - $url->addParams(['success_password' => 'new_password']); - $error = ''; - } - else - { - $error = 'bad_login_or_password'; - } - } } - + else{ + $error = $form->getErrors()[0]; // la 1ère erreur sera affichée + } + if(!empty($error)){ sleep(1); $url->addParams(['error_password' => $error]); } - header('Location: ' . $url); die; } + // dans une classe mère ou un trait après découpage de UserController? static private function getUser(string $login, EntityManager $entityManager): ?User { $users = $entityManager->getRepository('App\Entity\User')->findBy(['login' => $login]); @@ -351,6 +234,7 @@ class UserController return null; } + // dans une classe Form? // erreurs à la création des mots de passe static private function removeSpacesTabsCRLF(string $chaine): string { -- cgit v1.2.3