diff options
author | polo <ordipolo@gmx.fr> | 2025-08-17 19:46:20 +0200 |
---|---|---|
committer | polo <ordipolo@gmx.fr> | 2025-08-17 19:46:20 +0200 |
commit | dba24b8c18aed84a71c3169b2df5598d62deab06 (patch) | |
tree | cf913cd986f01894bc32a71997014434bf3ec7ad /src | |
parent | d7468fc363b5d028db84373d4abfa6d7d19bacb9 (diff) | |
download | cms-dba24b8c18aed84a71c3169b2df5598d62deab06.zip |
classe FormValidation et amélioration des envois d'e-mail
Diffstat (limited to 'src')
-rw-r--r-- | src/EmailService.php (renamed from src/controller/EmailController.php) | 44 | ||||
-rw-r--r-- | src/FormValidation.php | 186 | ||||
-rw-r--r-- | src/controller/ContactFormController.php | 40 | ||||
-rw-r--r-- | src/controller/UserController.php | 284 | ||||
-rw-r--r-- | src/router.php | 2 | ||||
-rw-r--r-- | src/view/UserEditBuilder.php | 8 | ||||
-rw-r--r-- | src/view/templates/form.php | 8 | ||||
-rw-r--r-- | src/view/templates/login.php | 1 | ||||
-rw-r--r-- | src/view/templates/user_create.php | 1 | ||||
-rw-r--r-- | src/view/templates/user_edit.php | 15 |
10 files changed, 339 insertions, 250 deletions
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 | ||
4 | declare(strict_types=1); | 4 | declare(strict_types=1); |
5 | 5 | ||
6 | use PHPMailer\PHPMailer\PHPMailer; | 6 | use PHPMailer\PHPMailer\PHPMailer; |
7 | use PHPMailer\PHPMailer\Exception; | 7 | //use PHPMailer\PHPMailer\Exception; |
8 | use App\Entity\Email; | 8 | use App\Entity\Email; |
9 | use Doctrine\ORM\EntityManager; | 9 | use Doctrine\ORM\EntityManager; |
10 | 10 | ||
11 | class EmailController | 11 | class 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 | |||
4 | class 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 |
5 | pour 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) | ||
7 | il 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é: | ||
11 | Auth (connexion, deconnexion), User (infos, choix photo), Account (créer, supprimer compte), Avatar (upload photo...) | ||
12 | */ | ||
5 | 13 | ||
6 | declare(strict_types=1); | 14 | declare(strict_types=1); |
7 | 15 | ||
@@ -11,6 +19,7 @@ use App\Entity\Log; | |||
11 | 19 | ||
12 | class UserController | 20 | class 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 |