summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--public/js/form.js24
-rw-r--r--public/js/main.js2
-rw-r--r--src/EmailService.php (renamed from src/controller/EmailController.php)44
-rw-r--r--src/FormValidation.php186
-rw-r--r--src/controller/ContactFormController.php40
-rw-r--r--src/controller/UserController.php284
-rw-r--r--src/router.php2
-rw-r--r--src/view/UserEditBuilder.php8
-rw-r--r--src/view/templates/form.php8
-rw-r--r--src/view/templates/login.php1
-rw-r--r--src/view/templates/user_create.php1
-rw-r--r--src/view/templates/user_edit.php15
12 files changed, 358 insertions, 257 deletions
diff --git a/public/js/form.js b/public/js/form.js
index 5c5a164..cf138e6 100644
--- a/public/js/form.js
+++ b/public/js/form.js
@@ -26,6 +26,12 @@ function changeRecipient(id){
26 }); 26 });
27} 27}
28 28
29function checkCase(){
30 if(document.getElementById('email_address').value.match('[A-Z]')){
31 toastNotify("Votre e-mail comporte une lettre majuscule, il s'agit probablement d'une erreur.");
32 }
33}
34
29function sendTestEmail(id){ 35function sendTestEmail(id){
30 const admin_form = document.querySelector('.admin_form'); 36 const admin_form = document.querySelector('.admin_form');
31 const test_email_success = document.querySelector('.test_email_success'); 37 const test_email_success = document.querySelector('.test_email_success');
@@ -61,15 +67,21 @@ function sendTestEmail(id){
61} 67}
62 68
63function sendVisitorEmail(id){ 69function sendVisitorEmail(id){
64 const send_email_success = document.querySelector('.send_email_success');
65 send_email_success.innerHTML = 'Envoi en cours, veuillez patienter';
66 send_email_success.style.backgroundColor = 'yellow';
67
68 const email_name = document.getElementById('email_name').value; 70 const email_name = document.getElementById('email_name').value;
69 const email_address = document.getElementById('email_address').value; 71 const email_address = document.getElementById('email_address').value;
70 const email_message = document.getElementById('email_message').value; 72 const email_message = document.getElementById('email_message').value;
71 const email_captcha = document.getElementById('email_captcha').value; 73 const email_captcha = document.getElementById('email_captcha').value;
72 const email_hidden = document.getElementById('email_hidden').value; 74 const email_hidden = document.getElementById('email_hidden').value;
75 const send_email_success = document.querySelector('.send_email_success');
76
77 if(email_name === '' || email_address === '' || email_message === '' || email_captcha === ''){
78 toastNotify('Veuillez remplir tous les champs.');
79 return;
80 }
81 else{
82 send_email_success.innerHTML = 'Envoi en cours, veuillez patienter';
83 send_email_success.style.backgroundColor = 'yellow';
84 }
73 85
74 fetch('index.php?action=send_email', { 86 fetch('index.php?action=send_email', {
75 method: 'POST', 87 method: 'POST',
@@ -90,7 +102,7 @@ function sendVisitorEmail(id){
90 let message; 102 let message;
91 let color; 103 let color;
92 if(data.success){ 104 if(data.success){
93 message = 'Votre E-mail a été envoyé!'; 105 message = 'Votre e-mail a été envoyé!';
94 color = 'lawngreen'; 106 color = 'lawngreen';
95 } 107 }
96 else{ 108 else{
@@ -98,8 +110,8 @@ function sendVisitorEmail(id){
98 color = "orangered" 110 color = "orangered"
99 } 111 }
100 send_email_success.innerHTML = message; 112 send_email_success.innerHTML = message;
101 toastNotify(message);
102 send_email_success.style.backgroundColor = color; 113 send_email_success.style.backgroundColor = color;
114 toastNotify(message);
103 }) 115 })
104 .catch(error => { 116 .catch(error => {
105 console.error('Erreur:', error); 117 console.error('Erreur:', error);
diff --git a/public/js/main.js b/public/js/main.js
index 2ffd33b..4be7843 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -33,7 +33,7 @@ function toastNotify(message) {
33 var toast = document.getElementById('toast'); 33 var toast = document.getElementById('toast');
34 toast.textContent = message; 34 toast.textContent = message;
35 toast.className = 'toast show'; 35 toast.className = 'toast show';
36 setTimeout(function(){ toast.className = toast.className.replace('show', ''); }, 3000); 36 setTimeout(function(){ toast.className = toast.className.replace('show', ''); }, 5000);
37} 37}
38 38
39 39
diff --git a/src/controller/EmailController.php b/src/EmailService.php
index 1eea257..c1f74d1 100644
--- a/src/controller/EmailController.php
+++ b/src/EmailService.php
@@ -1,16 +1,16 @@
1<?php 1<?php
2// src/controller/EmailController.php 2// src/EmailService.php
3 3
4declare(strict_types=1); 4declare(strict_types=1);
5 5
6use PHPMailer\PHPMailer\PHPMailer; 6use PHPMailer\PHPMailer\PHPMailer;
7use PHPMailer\PHPMailer\Exception; 7//use PHPMailer\PHPMailer\Exception;
8use App\Entity\Email; 8use App\Entity\Email;
9use Doctrine\ORM\EntityManager; 9use Doctrine\ORM\EntityManager;
10 10
11class EmailController 11class EmailService
12{ 12{
13 static public function send(string $recipient, bool $true_email, string $name = '', string $email = '', string $message = ''): bool 13 static public function send(EntityManager $entityManager, string $recipient, bool $true_email, string $name = '', string $email = '', string $message = ''): bool
14 { 14 {
15 $mail = new PHPMailer(true); // true => exceptions 15 $mail = new PHPMailer(true); // true => exceptions
16 $mail->CharSet = 'UTF-8'; 16 $mail->CharSet = 'UTF-8';
@@ -43,7 +43,6 @@ class EmailController
43 $mail->isHTML(true); 43 $mail->isHTML(true);
44 if($true_email){ 44 if($true_email){
45 $mail->Subject = 'Message envoyé par: ' . $name . ' (' . $email . ') depuis le site web'; 45 $mail->Subject = 'Message envoyé par: ' . $name . ' (' . $email . ') depuis le site web';
46
47 } 46 }
48 else{ 47 else{
49 $mail->Subject = "TEST d'un envoi d'e-mail depuis le site web"; 48 $mail->Subject = "TEST d'un envoi d'e-mail depuis le site web";
@@ -52,6 +51,12 @@ class EmailController
52 $mail->AltBody = $message; 51 $mail->AltBody = $message;
53 52
54 $mail->send(); 53 $mail->send();
54
55 // copie en BDD
56 $db_email = new Email($email, Config::$email_dest, $message);
57 $entityManager->persist($db_email);
58 $entityManager->flush();
59
55 return true; 60 return true;
56 } 61 }
57 catch(Exception $e){ 62 catch(Exception $e){
@@ -59,33 +64,4 @@ class EmailController
59 //echo "Le message n'a pas pu être envoyé. Erreur : {$mail->ErrorInfo}"; 64 //echo "Le message n'a pas pu être envoyé. Erreur : {$mail->ErrorInfo}";
60 } 65 }
61 } 66 }
62
63 static public function submit(array $json, EntityManager $entityManager): void
64 {
65 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0;
66 $captcha_try = isset($json['captcha']) ? Captcha::controlInput($json['captcha']) : 0;
67
68 // contrôles des entrées
69 $name = htmlspecialchars(trim($json['name']));
70 $email = strtolower(htmlspecialchars(trim($json['email'])));
71 $message = htmlspecialchars(trim($json['message']));
72
73 // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur
74 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']);
75 $recipient = $form_data->getData()['email'] ?? Config::$email_dest;
76
77 if($captcha_try != 0 && $captcha_solution != 0 && ($captcha_try === $captcha_solution)
78 && filter_var($email, FILTER_VALIDATE_EMAIL) && isset($json['hidden']) && empty($json['hidden'])
79 && self::send($recipient, true, $name, $email, $message))
80 {
81 $db_email = new Email($email, Config::$email_dest, $message);
82 $entityManager->persist($db_email);
83 $entityManager->flush();
84 echo json_encode(['success' => true]);
85 }
86 else{
87 echo json_encode(['success' => false]);
88 }
89 die;
90 }
91} \ No newline at end of file 67} \ No newline at end of file
diff --git a/src/FormValidation.php b/src/FormValidation.php
new file mode 100644
index 0000000..743cd13
--- /dev/null
+++ b/src/FormValidation.php
@@ -0,0 +1,186 @@
1<?php
2// src/FormValidation.php
3
4class FormValidation
5{
6 private array $data; // tableau associatif (probablement $_POST)
7 private string $validation_strategy; // à remplacer plus tard par un objet (pattern stratégie) d'interface ValidationStrategy
8 private array $errors;
9 private bool $validated = false;
10
11 public function __construct(array $data, string $validation_strategy){
12 $this->data = $data;
13 $this->validation_strategy = $validation_strategy;
14 }
15
16 public function validate(): bool
17 {
18 $this->errors = [];
19
20 // pattern stratégie en une seule classe
21 switch($this->validation_strategy){
22 case 'email':
23 $this->emailStrategy();
24 break;
25 case 'create_user':
26 $this->createUserStrategy();
27 break;
28 case 'connection':
29 $this->connectionStrategy();
30 break;
31 case 'username_update':
32 $this->usernameUpdateStrategy();
33 break;
34 case 'password_update':
35 $this->passwordUpdateStrategy();
36 break;
37 default:
38 http_response_code(500); // c'est un peu comme jeter une exception
39 echo json_encode(['success' => false, 'error' => 'server_error']);
40 die;
41 }
42
43 $this->validated = true;
44 return empty($this->errors);
45 }
46
47 public function getErrors(): array
48 {
49 return $this->errors;
50 }
51
52 public function getField(string $field): string
53 {
54 return $this->validated ? $this->data[$field] : '';
55 }
56
57 // méthodes de validation
58 private function captchaValidate(bool $clean_session = true): void
59 {
60 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0;
61 $captcha_try = isset($this->data['captcha']) ? Captcha::controlInput($this->data['captcha']) : 0;
62 if($clean_session){
63 unset($_SESSION['captcha']);
64 }
65
66 if($captcha_try == 0){
67 $error = 'error_non_valid_captcha';
68 }
69 elseif($captcha_solution == 0){ // ne peut pas arriver, si?
70 $error = 'captcha_server_error';
71 }
72 elseif($captcha_try !== $captcha_solution){
73 $this->errors[] = 'bad_solution_captcha';
74 }
75 }
76
77 // erreurs à la création des mots de passe
78 static private function removeSpacesTabsCRLF(string $chaine): string
79 {
80 $cibles = [' ', "\t", "\n", "\r"]; // doubles quotes !!
81 return(str_replace($cibles, '', $chaine));
82 }
83
84
85 // stratégies
86 private function emailStrategy(): void
87 {
88 $this->captchaValidate(false);
89
90 if(!isset($this->data['name']) || empty($this->data['name'])
91 || !isset($this->data['email']) || empty($this->data['email'])
92 || !isset($this->data['message']) || empty($this->data['message'])
93 || !isset($this->data['hidden']) || !empty($this->data['hidden'])){
94 $this->errors[] = 'missing_fields';
95 }
96
97 if(!filter_var(trim($this->data['email']), FILTER_VALIDATE_EMAIL)){
98 $this->errors[] = 'bad_email_address';
99 }
100
101 $this->data['name'] = htmlspecialchars(trim($this->data['name']));
102 $this->data['email'] = htmlspecialchars(trim($this->data['email']));
103 $this->data['message'] = htmlspecialchars($this->data['message']);
104 }
105 private function createUserStrategy(): void
106 {
107 $this->captchaValidate();
108
109 // test mauvais paramètres
110 if(!isset($this->data['login']) || empty($this->data['login'])
111 || !isset($this->data['password']) || empty($this->data['password'])
112 || !isset($this->data['password_confirmation']) || empty($this->data['password_confirmation'])
113 || !isset($this->data['create_user_hidden']) || !empty($this->data['create_user_hidden']))
114 {
115 $this->errors[] = 'bad_login_or_password';
116 }
117
118 if($this->data['password'] !== $this->data['password_confirmation']){
119 $this->errors[] = 'different_passwords';
120 }
121
122 if($this->data['login'] !== self::removeSpacesTabsCRLF(htmlspecialchars($this->data['login']))
123 || $this->data['password'] !== self::removeSpacesTabsCRLF(htmlspecialchars($this->data['password']))){
124 $this->errors[] = 'forbidden_characters';
125 }
126 }
127 private function connectionStrategy(): void
128 {
129 $this->captchaValidate();
130
131 if(!isset($this->data['login']) || empty($this->data['login'])
132 || !isset($this->data['password']) || empty($this->data['password'])
133 || !isset($this->data['connection_hidden']) || !empty($this->data['connection_hidden']))
134 {
135 $this->errors[] = 'bad_login_or_password';
136 }
137 }
138 private function usernameUpdateStrategy(): void
139 {
140 $this->captchaValidate();
141
142 if(!isset($this->data['login']) || empty($this->data['login'])
143 || !isset($this->data['password']) || empty($this->data['password'])
144 || !isset($this->data['new_login']) || empty($this->data['new_login'])
145 || !isset($this->data['modify_username_hidden']) || !empty($this->data['modify_username_hidden']))
146 {
147 $this->errors[] = 'bad_login_or_password';
148 }
149
150 $new_login = self::removeSpacesTabsCRLF(htmlspecialchars($this->data['new_login']));
151 if($new_login !== $this->data['new_login']){
152 $this->errors[] = 'forbidden_characters';
153 }
154
155 if($this->data['login'] !== $_SESSION['user']){
156 $this->errors[] = 'bad_login_or_password';
157 }
158 if($this->data['login'] === $new_login){
159 $this->errors[] = 'same_username_as_before';
160 }
161 }
162 private function passwordUpdateStrategy(): void
163 {
164 $this->captchaValidate();
165
166 if(!isset($this->data['login']) || empty($this->data['login'])
167 || !isset($this->data['password']) || empty($this->data['password'])
168 || !isset($this->data['new_password']) || empty($this->data['new_password'])
169 || !isset($this->data['modify_password_hidden']) || !empty($this->data['modify_password_hidden']))
170 {
171 $this->errors[] = 'bad_login_or_password';
172 }
173
174 $new_password = self::removeSpacesTabsCRLF(htmlspecialchars($this->data['new_password']));
175 if($new_password !== $this->data['new_password']){
176 $this->errors[] = 'forbidden_characters';
177 }
178
179 if($this->data['login'] !== $_SESSION['user']){
180 $this->errors[] = 'bad_login_or_password';
181 }
182 if($this->data['password'] === $new_password){
183 $this->errors[] = 'same_password_as_before';
184 }
185 }
186} \ No newline at end of file
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
27 } 27 }
28 die; 28 die;
29 } 29 }
30 static public function sendVisitorEmail(EntityManager $entityManager, array $json): void
31 {
32 $form = new FormValidation($json, 'email');
33
34 $error = '';
35 if($form->validate()){
36 // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur
37 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']);
38 if($form_data === null){
39 http_response_code(500);
40 echo json_encode(['success' => false, 'error' => 'server_error']);
41 die;
42 }
43 $recipient = $form_data->getData()['email'] ?? Config::$email_dest;
44
45 if(!EmailService::send($entityManager, $recipient, true, $form->getField('name'), $form->getField('email'), $form->getField('message'))){
46 $error = 'email_not_sent';
47 }
48 }
49 else{
50 $error = $form->getErrors()[0]; // la 1ère erreur sera affichée
51 }
52
53 if(empty($error)){
54 echo json_encode(['success' => true]);
55 }
56 else{
57 echo json_encode(['success' => false, 'error' => $error]);
58 }
59 die;
60 }
30 static public function sendTestEmail(EntityManager $entityManager, array $json): void 61 static public function sendTestEmail(EntityManager $entityManager, array $json): void
31 { 62 {
32 // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur 63 // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur
33 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']); 64 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']);
65 if($form_data === null){
66 http_response_code(500);
67 echo json_encode(['success' => false, 'error' => 'server_error']);
68 die;
69 }
34 $recipient = $form_data->getData()['email'] ?? Config::$email_dest; 70 $recipient = $form_data->getData()['email'] ?? Config::$email_dest;
35 71
36 if(EmailController::send($recipient, false, 'nom du visiteur', 'adresse@du_visiteur.fr', "TEST d'un envoi d'e-mail depuis le site web")){ 72 if(EmailService::send($entityManager, $recipient, false, 'nom du visiteur', 'adresse@du_visiteur.fr', "TEST d'un envoi d'e-mail depuis le site web")){
37 echo json_encode(['success' => true]); 73 echo json_encode(['success' => true]);
38 } 74 }
39 else{ 75 else{
40 echo json_encode(['success' => false]); 76 echo json_encode(['success' => false, 'error' => 'email_not_sent']);
41 } 77 }
42 die; 78 die;
43 } 79 }
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 @@
1<?php 1<?php
2// src/controller/PasswordController.php 2// src/controller/UserController.php
3// 3//
4// test mot de passe et captcha 4/* actuellement un fourre-tout de méthodes en rapport avec les utilisateurs
5pour l'améliorer on pourrait appliquer le principe de reponsabilité unique
6=> UserController devrait se limiter à gérer des requêtes et réponses (changement transparent pour le routeur)
7il instancierait un classe correspondant au formulaire (prend POST et SESSION) et une classe "métier" UserService
8=> UserService contiendrait des méthodes utilisant l'objet formulaire pour agir sur la BDD
9=> les formulaires peuvent tenir dans une classe "UserUpdateForm" avec des stratégies ou plusieurs, les données y sont validées
10=> il est aussi possible de découper UserController en contrôleurs par fonctionnalité:
11Auth (connexion, deconnexion), User (infos, choix photo), Account (créer, supprimer compte), Avatar (upload photo...)
12*/
5 13
6declare(strict_types=1); 14declare(strict_types=1);
7 15
@@ -11,6 +19,7 @@ use App\Entity\Log;
11 19
12class UserController 20class UserController
13{ 21{
22 // account
14 static public function existUsers(EntityManager $entityManager): bool 23 static public function existUsers(EntityManager $entityManager): bool
15 { 24 {
16 // optimiser ça si possible, on veut juste savoir si la table est vide ou non 25 // optimiser ça si possible, on veut juste savoir si la table est vide ou non
@@ -28,71 +37,34 @@ class UserController
28 } 37 }
29 } 38 }
30 39
40 // account
31 static public function createUser(EntityManager $entityManager) 41 static public function createUser(EntityManager $entityManager)
32 { 42 {
33 // test mauvais paramètres
34 if(!isset($_POST['login']) || empty($_POST['login'])
35 || !isset($_POST['password']) || empty($_POST['password'])
36 || !isset($_POST['password_confirmation']) || empty($_POST['password_confirmation'])
37 || !isset($_POST['create_user_hidden']) || !empty($_POST['create_user_hidden']))
38 {
39 header('Location: ' . new URL);
40 die;
41 }
42
43 unset($_SESSION['user']); 43 unset($_SESSION['user']);
44 44
45 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; 45 $form = new FormValidation($_POST, 'create_user');
46 $captcha_try = isset($_POST['captcha']) ? Captcha::controlInput($_POST['captcha']) : 0;
47 unset($_SESSION['captcha']);
48 46
47 $url = new URL;
49 $error = ''; 48 $error = '';
50 if($captcha_try == 0) 49 if($form->validate()){
51 { 50 $password = password_hash($_POST['password'], PASSWORD_DEFAULT);
52 $error = 'error_non_valid_captcha'; 51 $user = new App\Entity\User($_POST['login'], $password);
53 } 52 $entityManager->persist($user);
54 elseif($captcha_solution == 0) // ne peut pas arriver, si? 53 $entityManager->flush();
55 {
56 $error = 'captcha_server_error';
57 }
58 elseif($captcha_try != $captcha_solution) // le test!
59 {
60 $error = 'bad_solution_captcha';
61 } 54 }
62 elseif($_POST['password'] !== $_POST['password_confirmation']) 55 else{
63 { 56 $error = $form->getErrors()[0]; // la 1ère erreur sera affichée
64 $error = 'different_passwords';
65 } 57 }
66 else 58
67 { 59 if(!empty($error)){
68 $login = self::removeSpacesTabsCRLF(htmlspecialchars($_POST['login'])); 60 $url->addParams(['error' => $error]);
69 $password = self::removeSpacesTabsCRLF(htmlspecialchars($_POST['password']));
70
71 // self::removeSpacesTabsCRLF prévient la validation par erreur d'une chaine "vide"
72
73 // conformité
74 if(!empty($password) && !empty($login)
75 && $password === $_POST['password'] && $login === $_POST['login'])
76 {
77 // enregistrement et redirection
78 $password = password_hash($password, PASSWORD_DEFAULT);
79 $user = new App\Entity\User($login, $password);
80 $entityManager->persist($user);
81 $entityManager->flush();
82
83 header('Location: ' . new URL);
84 die;
85 }
86 else{
87 $error = 'forbidden_characters';
88 }
89 } 61 }
90 62
91 $url = empty($error) ? new URL : new URL(['error' => $error]);
92 header('Location: ' . $url); 63 header('Location: ' . $url);
93 die; 64 die;
94 } 65 }
95 66
67 // auth
96 static public function connect(EntityManager $entityManager): void 68 static public function connect(EntityManager $entityManager): void
97 { 69 {
98 if($_SESSION['admin']) // déjà connecté? 70 if($_SESSION['admin']) // déjà connecté?
@@ -100,74 +72,52 @@ class UserController
100 header('Location: ' . new URL); 72 header('Location: ' . new URL);
101 die; 73 die;
102 } 74 }
103
104 $_SESSION['user'] = ''; 75 $_SESSION['user'] = '';
105 $_SESSION['admin'] = false; 76 $_SESSION['admin'] = false;
106 77
107 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; 78 $form = new FormValidation($_POST, 'connection');
108 $captcha_try = isset($_POST['captcha']) ? Captcha::controlInput($_POST['captcha']) : 0;
109 unset($_SESSION['captcha']);
110 79
111 $error = ''; 80 $error = '';
112 if($captcha_try == 0) 81 if($form->validate()){
113 { 82 // à mettre dans une classe métier UserService, Authentication, AuthService?
114 $error = 'error_non_valid_captcha'; 83 $user = self::getUser($_POST['login'], $entityManager);
115 } 84 if(!empty($user) && $_POST['login'] === $user->getLogin() && password_verify($_POST['password'], $user->getPassword()))
116 elseif($captcha_solution == 0) // pas censé se produire
117 {
118 $error = 'captcha_server_error';
119 }
120 elseif($captcha_try != $captcha_solution) // le test!
121 {
122 $error = 'bad_solution_captcha';
123 }
124 elseif(!isset($_POST['login']) || empty($_POST['login'])
125 || !isset($_POST['password']) || empty($_POST['password']))
126 {
127 $error = 'bad_login_or_password';
128 }
129 else // c'est OK
130 {
131 $login = trim($_POST['login']);
132 $password = trim($_POST['password']);
133 $user = self::getUser($login, $entityManager);
134
135 // enregistrement et redirection
136 if(!empty($user) && $login === $user->getLogin() && password_verify($password, $user->getPassword()))
137 { 85 {
138 $log = new Log(true); 86 $log = new Log(true);
139 $entityManager->persist($log);
140 $entityManager->flush();
141 87
142 // protection fixation de session, si l'attaquant crée un cookie de session, il est remplacé 88 // protection fixation de session, si l'attaquant crée un cookie de session, il est remplacé
143 session_regenerate_id(true); 89 session_regenerate_id(true);
144 90 $_SESSION['user'] = $_POST['login'];
145 $_SESSION['user'] = $login;
146 $_SESSION['admin'] = true; 91 $_SESSION['admin'] = true;
147 92
148 $url = new URL(isset($_GET['from']) ? ['page' => $_GET['from']] : []); 93 $url = new URL(isset($_GET['from']) ? ['page' => $_GET['from']] : []);
149 isset($_GET['id']) ? $url->addParams(['id' => $_GET['id']]) : ''; 94 isset($_GET['id']) ? $url->addParams(['id' => $_GET['id']]) : '';
150 header('Location: ' . $url);
151 die;
152 } 95 }
153 else 96 else
154 { 97 {
155 $log = new Log(false); 98 $log = new Log(false);
156 $entityManager->persist($log);
157 $entityManager->flush();
158 $error = 'bad_login_or_password'; 99 $error = 'bad_login_or_password';
159 } 100 }
101 $entityManager->persist($log);
102 $entityManager->flush();
103 }
104 else{
105 $error = $form->getErrors()[0]; // la 1ère erreur sera affichée
106 }
107
108 if(!empty($error)){
109 sleep(1); // défense basique à la force brute
110 $url = new URL(['page' => 'connexion']);
111 isset($_GET['from']) ? $url->addParams(['from' => $_GET['from']]) : null;
112 isset($_GET['id']) ? $url->addParams(['id' => $_GET['id']]) : null;
113 $url->addParams(['error' => $error]);
160 } 114 }
161 115
162 // tous les cas sauf connexion réussie
163 sleep(1); // défense basique à la force brute
164 $url = new URL(isset($_GET['from']) ? ['page' => 'connexion', 'from' => $_GET['from']] : []);
165 isset($_GET['id']) ? $url->addParams(['id' => $_GET['id']]) : '';
166 !empty($error) ? $url->addParams(['error' => $error]) : '';
167 header('Location: ' . $url); 116 header('Location: ' . $url);
168 die; 117 die;
169 } 118 }
170 119
120 // auth
171 static public function disconnect(): void 121 static public function disconnect(): void
172 { 122 {
173 // nettoyage complet 123 // nettoyage complet
@@ -183,154 +133,87 @@ class UserController
183 die; 133 die;
184 } 134 }
185 135
136 // user
186 static public function updateUsername(EntityManager $entityManager): void 137 static public function updateUsername(EntityManager $entityManager): void
187 { 138 {
188 if(!$_SESSION['admin']) // superflux, fait dans le routeur 139 if(!$_SESSION['admin']){ // superflux, fait dans le routeur
189 {
190 self::disconnect(); 140 self::disconnect();
191 } 141 }
192 142
193 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0;
194 $captcha_try = isset($_POST['captcha']) ? Captcha::controlInput($_POST['captcha']) : 0;
195 unset($_SESSION['captcha']);
196
197 $url = new URL(['page' => 'user_edit']); 143 $url = new URL(['page' => 'user_edit']);
198 isset($_GET['from']) ? $url->addParams(['from' => $_GET['from']]) : null; 144 isset($_GET['from']) ? $url->addParams(['from' => $_GET['from']]) : null;
199 145
146 $form = new FormValidation($_POST, 'username_update');
147
200 $error = ''; 148 $error = '';
201 if(!isset($_POST['old_login']) || empty($_POST['old_login']) 149 if($form->validate()){
202 || !isset($_POST['password']) || empty($_POST['password']) 150 // à mettre dans une classe métier UserService?
203 || !isset($_POST['new_login']) || empty($_POST['new_login']) 151 $user = self::getUser($_POST['login'], $entityManager);
204 || !isset($_POST['modify_username_hidden']) || !empty($_POST['modify_username_hidden'])) 152 if(password_verify($_POST['password'], $user->getPassword())){
205 { 153 $user->setLogin($_POST['new_login']);
206 $error = 'bad_login_or_password'; 154 $entityManager->flush();
207 } 155 $_SESSION['user'] = $_POST['new_login'];
208 elseif($captcha_try != $captcha_solution) // le test!
209 {
210 $error = 'bad_solution_captcha';
211 }
212 else
213 {
214 // sécurisation de la saisie
215 $old_login = $_POST['old_login'];
216 $password = $_POST['password'];
217 $new_login = self::removeSpacesTabsCRLF(htmlspecialchars($_POST['new_login']));
218 // removeSpacesTabsCRLF pour éviter d'enregistrer une chaîne vide
219 156
220 // tests de conformité 157 $url->addParams(['success_username' => 'new_login']);
221 if($old_login !== $_POST['old_login'] || $password !== $_POST['password'] || $new_login !== $_POST['new_login']) 158 $error = '';
222 {
223 $error = 'forbidden_characters';
224 } 159 }
225 elseif($old_login !== $_SESSION['user']){ 160 else{
226 $error = 'bad_login_or_password'; 161 $error = 'bad_login_or_password';
227 } 162 }
228 elseif($old_login === $new_login){
229 $error = 'same_username_as_before';
230 }
231 else
232 {
233 $user = self::getUser($old_login, $entityManager);
234
235 if(password_verify($password, $user->getPassword()))
236 {
237 $user->setLogin($new_login);
238 $entityManager->flush();
239 $_SESSION['user'] = $new_login;
240
241 $url->addParams(['success_login' => 'new_login']);
242 $error = '';
243 }
244 else
245 {
246 $error = 'bad_login_or_password';
247 }
248 }
249 } 163 }
250 164 else{
165 $error = $form->getErrors()[0]; // la 1ère erreur sera affichée
166 }
167
251 if(!empty($error)){ 168 if(!empty($error)){
252 sleep(1); 169 sleep(1);
253 $url->addParams(['error_login' => $error]); 170 $url->addParams(['error_username' => $error]);
254 } 171 }
255
256 header('Location: ' . $url); 172 header('Location: ' . $url);
257 die; 173 die;
258 } 174 }
259 175
176 // user
260 static public function updatePassword(EntityManager $entityManager): void 177 static public function updatePassword(EntityManager $entityManager): void
261 { 178 {
262 if(!$_SESSION['admin']) // superflux, fait dans le routeur 179 if(!$_SESSION['admin']){ // superflux, fait dans le routeur
263 {
264 self::disconnect(); 180 self::disconnect();
265 } 181 }
266 182
267 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0;
268 $captcha_try = isset($_POST['captcha']) ? Captcha::controlInput($_POST['captcha']) : 0;
269 unset($_SESSION['captcha']);
270
271 $url = new URL(['page' => 'user_edit']); 183 $url = new URL(['page' => 'user_edit']);
272 isset($_GET['from']) ? $url->addParams(['from' => $_GET['from']]) : null; 184 isset($_GET['from']) ? $url->addParams(['from' => $_GET['from']]) : null;
273 185
186 $form = new FormValidation($_POST, 'password_update');
187
274 $error = ''; 188 $error = '';
275 if(!isset($_POST['login']) || empty($_POST['login']) 189 if($form->validate()){
276 || !isset($_POST['old_password']) || empty($_POST['old_password']) 190 // à mettre dans une classe métier UserService?
277 || !isset($_POST['new_password']) || empty($_POST['new_password']) 191 $user = self::getUser($_POST['login'], $entityManager);
278 || !isset($_POST['modify_password_hidden']) || !empty($_POST['modify_password_hidden'])) 192 if(password_verify($_POST['password'], $user->getPassword())){
279 { 193 $new_password = password_hash($_POST['new_password'], PASSWORD_DEFAULT);
280 $error = 'bad_login_or_password'; 194 $user->setPassword($new_password);
281 } 195 $entityManager->flush();
282 elseif($captcha_try != $captcha_solution) // le test!
283 {
284 $error = 'bad_solution_captcha';
285 }
286 else
287 {
288 // sécurisation de la saisie
289 $login = $_POST['login'];
290 $old_password = $_POST['old_password'];
291 $new_password = self::removeSpacesTabsCRLF(htmlspecialchars($_POST['new_password']));
292 // removeSpacesTabsCRLF pour éviter d'enregistrer une chaîne vide
293 196
294 // tests de conformité 197 $url->addParams(['success_password' => 'new_password']);
295 if($login !== $_POST['login'] || $old_password !== $_POST['old_password'] || $new_password !== $_POST['new_password']) 198 $error = '';
296 {
297 $error = 'forbidden_characters';
298 } 199 }
299 elseif($login !== $_SESSION['user']){ 200 else{
300 $error = 'bad_login_or_password'; 201 $error = 'bad_login_or_password';
301 } 202 }
302 elseif($old_password === $new_password){
303 $error = 'same_password_as_before';
304 }
305 else
306 {
307 $user = self::getUser($login, $entityManager);
308
309 if(password_verify($old_password, $user->getPassword()))
310 {
311 $new_password = password_hash($new_password, PASSWORD_DEFAULT);
312 $user->setPassword($new_password);
313 $entityManager->flush();
314
315 $url->addParams(['success_password' => 'new_password']);
316 $error = '';
317 }
318 else
319 {
320 $error = 'bad_login_or_password';
321 }
322 }
323 } 203 }
324 204 else{
205 $error = $form->getErrors()[0]; // la 1ère erreur sera affichée
206 }
207
325 if(!empty($error)){ 208 if(!empty($error)){
326 sleep(1); 209 sleep(1);
327 $url->addParams(['error_password' => $error]); 210 $url->addParams(['error_password' => $error]);
328 } 211 }
329
330 header('Location: ' . $url); 212 header('Location: ' . $url);
331 die; 213 die;
332 } 214 }
333 215
216 // dans une classe mère ou un trait après découpage de UserController?
334 static private function getUser(string $login, EntityManager $entityManager): ?User 217 static private function getUser(string $login, EntityManager $entityManager): ?User
335 { 218 {
336 $users = $entityManager->getRepository('App\Entity\User')->findBy(['login' => $login]); 219 $users = $entityManager->getRepository('App\Entity\User')->findBy(['login' => $login]);
@@ -351,6 +234,7 @@ class UserController
351 return null; 234 return null;
352 } 235 }
353 236
237 // dans une classe Form?
354 // erreurs à la création des mots de passe 238 // erreurs à la création des mots de passe
355 static private function removeSpacesTabsCRLF(string $chaine): string 239 static private function removeSpacesTabsCRLF(string $chaine): string
356 { 240 {
diff --git a/src/router.php b/src/router.php
index 19fe1c1..238cac9 100644
--- a/src/router.php
+++ b/src/router.php
@@ -63,7 +63,7 @@ elseif($_SERVER['REQUEST_METHOD'] === 'POST'){
63 { 63 {
64 // formulaire de contact 64 // formulaire de contact
65 if($_GET['action'] === 'send_email'){ 65 if($_GET['action'] === 'send_email'){
66 EmailController::submit($json, $entityManager); 66 ContactFormController::sendVisitorEmail($entityManager, $json);
67 } 67 }
68 } 68 }
69 } 69 }
diff --git a/src/view/UserEditBuilder.php b/src/view/UserEditBuilder.php
index 63bbfad..3604e91 100644
--- a/src/view/UserEditBuilder.php
+++ b/src/view/UserEditBuilder.php
@@ -31,8 +31,8 @@ class UserEditBuilder extends AbstractBuilder
31 'same_password_as_before' => 'Nouveau mot de passe identique au précédent.' 31 'same_password_as_before' => 'Nouveau mot de passe identique au précédent.'
32 ]; 32 ];
33 33
34 $error_username = isset($_GET['error_login']) ? $error_messages[$_GET['error_login']] : ''; 34 $error_username = isset($_GET['error_username']) ? $error_messages[$_GET['error_username']] : '';
35 $success_username = (isset($_GET['success_login']) && $_GET['success_login']) ? 'Identifiant modifié avec succès.' : ''; 35 $success_username = (isset($_GET['success_username']) && $_GET['success_username']) ? 'Identifiant modifié avec succès.' : '';
36 $error_password = isset($_GET['error_password']) ? $error_messages[$_GET['error_password']] : ''; 36 $error_password = isset($_GET['error_password']) ? $error_messages[$_GET['error_password']] : '';
37 $success_password = (isset($_GET['success_password']) && $_GET['success_password']) ? 'Mot de passe modifié avec succès.' : ''; 37 $success_password = (isset($_GET['success_password']) && $_GET['success_password']) ? 'Mot de passe modifié avec succès.' : '';
38 38
@@ -51,6 +51,10 @@ class UserEditBuilder extends AbstractBuilder
51 isset($_GET['from']) ? $link_exit->addParams(['page' => $_GET['from'] ]) : ''; 51 isset($_GET['from']) ? $link_exit->addParams(['page' => $_GET['from'] ]) : '';
52 isset($_GET['id']) ? $link_exit->addParams(['id' => $_GET['id']]) : ''; 52 isset($_GET['id']) ? $link_exit->addParams(['id' => $_GET['id']]) : '';
53 53
54 $link_logout = new URL(['action' => 'deconnection']);
55 isset($_GET['from']) ? $link_logout->addParams(['from' => $_GET['from'] ]) : '';
56 isset($_GET['id']) ? $link_logout->addParams(['id' => $_GET['id']]) : '';
57
54 ob_start(); 58 ob_start();
55 require $viewFile; 59 require $viewFile;
56 $this->html = ob_get_clean(); // nouveau contenu 60 $this->html = ob_get_clean(); // nouveau contenu
diff --git a/src/view/templates/form.php b/src/view/templates/form.php
index bcde2f4..25446c1 100644
--- a/src/view/templates/form.php
+++ b/src/view/templates/form.php
@@ -4,13 +4,13 @@
4 <h3><?= $title ?></h3> 4 <h3><?= $title ?></h3>
5 <div class="form_inputs"> 5 <div class="form_inputs">
6 <label for="email_name">Votre nom</label> 6 <label for="email_name">Votre nom</label>
7 <input id="email_name" type="text" name="email_name" value="" required> 7 <input id="email_name" type="text" name="email_name" value="">
8 8
9 <label for="email_address">Votre e-mail</label> 9 <label for="email_address">Votre e-mail</label>
10 <input id="email_address" type="email" name="email_address" placeholder="mon-adresse@email.fr" value="" required> 10 <input id="email_address" type="email" name="email_address" placeholder="mon-adresse@email.fr" value="" onchange="checkCase()">
11 11
12 <label for="email_message">Votre message</label> 12 <label for="email_message">Votre message</label>
13 <textarea id="email_message" type="text" name="email_message" rows="4" required></textarea> 13 <textarea id="email_message" type="text" name="email_message" rows="4"></textarea>
14 14
15 <div class="full_width_column"> 15 <div class="full_width_column">
16 <label for="captcha" >Montrez que vous n'êtes pas un robot</label> 16 <label for="captcha" >Montrez que vous n'êtes pas un robot</label>
@@ -18,7 +18,7 @@
18 18
19 <label for="email_captcha" >Combien font <?= $captcha->getA() ?> fois <?= $captcha->getB() ?>?</label> 19 <label for="email_captcha" >Combien font <?= $captcha->getA() ?> fois <?= $captcha->getB() ?>?</label>
20 <div> 20 <div>
21 <input id="email_captcha" type="text" name="email_captcha" size="1" required> 21 <input id="email_captcha" type="text" name="email_captcha" size="1" autocomplete="off">
22 </div> 22 </div>
23 23
24 <input id="form_id_hidden" type="hidden" name="form_id_hidden" value=""> 24 <input id="form_id_hidden" type="hidden" name="form_id_hidden" value="">
diff --git a/src/view/templates/login.php b/src/view/templates/login.php
index 766c114..c40b3a7 100644
--- a/src/view/templates/login.php
+++ b/src/view/templates/login.php
@@ -9,6 +9,7 @@
9 <input id="login" type="text" name="login" autofocus required></p> 9 <input id="login" type="text" name="login" autofocus required></p>
10 <p><label for="password" >Mot de passe:</label> 10 <p><label for="password" >Mot de passe:</label>
11 <input id="password" type="password" name="password" required></p> 11 <input id="password" type="password" name="password" required></p>
12 <input type="hidden" name="connection_hidden">
12 13
13 <p>Montrez que vous n'êtes pas un robot.<br> 14 <p>Montrez que vous n'êtes pas un robot.<br>
14 <label for="captcha" >Combien font <?= $captcha->getA() ?> fois <?= $captcha->getB() ?>?</label> 15 <label for="captcha" >Combien font <?= $captcha->getA() ?> fois <?= $captcha->getB() ?>?</label>
diff --git a/src/view/templates/user_create.php b/src/view/templates/user_create.php
index dd17547..68e115c 100644
--- a/src/view/templates/user_create.php
+++ b/src/view/templates/user_create.php
@@ -5,6 +5,7 @@ $error_messages = [
5 'error_non_valid_captcha' => 'Erreur au test anti-robot, veuillez saisir un nombre entier.', 5 'error_non_valid_captcha' => 'Erreur au test anti-robot, veuillez saisir un nombre entier.',
6 'captcha_server_error' => 'captcha_server_error', 6 'captcha_server_error' => 'captcha_server_error',
7 'bad_solution_captcha' => 'Erreur au test anti-robot, veuillez réessayer.', 7 'bad_solution_captcha' => 'Erreur au test anti-robot, veuillez réessayer.',
8 'bad_login_or_password' => 'Mauvais identifiant ou mot de passe, veuillez réessayer.',
8 'different_passwords' => 'Les deux mots de passe saisis sont différents', 9 'different_passwords' => 'Les deux mots de passe saisis sont différents',
9 'forbidden_characters' => 'Caractères interdits: espaces, tabulations, sauts CR/LF.' 10 'forbidden_characters' => 'Caractères interdits: espaces, tabulations, sauts CR/LF.'
10]; 11];
diff --git a/src/view/templates/user_edit.php b/src/view/templates/user_edit.php
index b4b35ed..77cd9f2 100644
--- a/src/view/templates/user_edit.php
+++ b/src/view/templates/user_edit.php
@@ -14,8 +14,8 @@
14 <p style="color: red; font-style: italic;"><?= $error_username ?></p> 14 <p style="color: red; font-style: italic;"><?= $error_username ?></p>
15 <p style="color: green; font-style: italic;"><?= $success_username ?></p> 15 <p style="color: green; font-style: italic;"><?= $success_username ?></p>
16 <form class="connexionFormulaire" method="post" action="<?= $link_user_form ?>" > 16 <form class="connexionFormulaire" method="post" action="<?= $link_user_form ?>" >
17 <p><label for="old_login" >Ancien nom:</label> 17 <p><label for="login" >Ancien nom:</label>
18 <input id="old_login" type="text" name="old_login" required></p> 18 <input id="login" type="text" name="login" required></p>
19 <p><label for="password" >Mot de passe:</label> 19 <p><label for="password" >Mot de passe:</label>
20 <input id="password" type="password" name="password" required ></p> 20 <input id="password" type="password" name="password" required ></p>
21 <p><label for="new_login" >Nouveau nom:</label> 21 <p><label for="new_login" >Nouveau nom:</label>
@@ -37,8 +37,8 @@
37 <form class="connexionFormulaire" method="post" action="<?= $link_password_form ?>" > 37 <form class="connexionFormulaire" method="post" action="<?= $link_password_form ?>" >
38 <p><label for="login" >Nom:</label> 38 <p><label for="login" >Nom:</label>
39 <input id="login" type="text" name="login" required></p> 39 <input id="login" type="text" name="login" required></p>
40 <p><label for="old_password" >Ancien mot de passe:</label> 40 <p><label for="password" >Ancien mot de passe:</label>
41 <input id="old_password" type="password" name="old_password" required ></p> 41 <input id="password" type="password" name="password" required ></p>
42 <p><label for="new_password" >Nouveau mot de passe:</label> 42 <p><label for="new_password" >Nouveau mot de passe:</label>
43 <input id="new_password" type="password" name="new_password" required autocomplete="off"></p> 43 <input id="new_password" type="password" name="new_password" required autocomplete="off"></p>
44 <input type="hidden" name="modify_password_hidden"> 44 <input type="hidden" name="modify_password_hidden">
@@ -54,9 +54,10 @@
54 </div> 54 </div>
55 <div class="login_form"> 55 <div class="login_form">
56 <p class="connexionP connexionFooter" > 56 <p class="connexionP connexionFooter" >
57 <a href="<?= $link_exit ?>" > 57 <a href="<?= $link_exit ?>">
58 <button>Retour au site</button> 58 <button>Retour au site</button></a>
59 </a> 59 <a href="<?= $link_logout ?>">
60 <button>Déconnexion</button></a>
60 </p> 61 </p>
61 </div> 62 </div>
62 </section> \ No newline at end of file 63 </section> \ No newline at end of file