summaryrefslogtreecommitdiff
path: root/src/controller
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2025-08-17 19:46:20 +0200
committerpolo <ordipolo@gmx.fr>2025-08-17 19:46:20 +0200
commitdba24b8c18aed84a71c3169b2df5598d62deab06 (patch)
treecf913cd986f01894bc32a71997014434bf3ec7ad /src/controller
parentd7468fc363b5d028db84373d4abfa6d7d19bacb9 (diff)
downloadcms-dba24b8c18aed84a71c3169b2df5598d62deab06.zip
classe FormValidation et amélioration des envois d'e-mail
Diffstat (limited to 'src/controller')
-rw-r--r--src/controller/ContactFormController.php40
-rw-r--r--src/controller/EmailController.php91
-rw-r--r--src/controller/UserController.php284
3 files changed, 122 insertions, 293 deletions
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/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 @@
1<?php
2// src/controller/EmailController.php
3
4declare(strict_types=1);
5
6use PHPMailer\PHPMailer\PHPMailer;
7use PHPMailer\PHPMailer\Exception;
8use App\Entity\Email;
9use Doctrine\ORM\EntityManager;
10
11class EmailController
12{
13 static public function send(string $recipient, bool $true_email, string $name = '', string $email = '', string $message = ''): bool
14 {
15 $mail = new PHPMailer(true); // true => exceptions
16 $mail->CharSet = 'UTF-8';
17
18 try{
19 // Paramètres du serveur
20 $mail->isSMTP();
21 $mail->Host = Config::$smtp_host;
22 $mail->SMTPAuth = true;
23 $mail->Port = 25;
24
25 if($mail->SMTPAuth){
26 $mail->Username = Config::$smtp_username; // e-mail
27 $mail->Password = Config::$smtp_password;
28 $mail->SMTPSecure = Config::$smtp_secure; // tls (starttls) ou ssl (smtps)
29 if($mail->SMTPSecure === 'tls'){
30 $mail->Port = 587;
31 }
32 elseif($mail->SMTPSecure === 'ssl'){
33 $mail->Port = 465;
34 }
35 }
36 //var_dump($mail->smtpConnect());die; // test de connexion
37
38 // Expéditeur et destinataire
39 $mail->setFrom(strtolower(Config::$email_from), Config::$email_from_name); // expéditeur
40 $mail->addAddress(strtolower($recipient), Config::$email_dest_name); // destinataire
41
42 // Contenu
43 $mail->isHTML(true);
44 if($true_email){
45 $mail->Subject = 'Message envoyé par: ' . $name . ' (' . $email . ') depuis le site web';
46
47 }
48 else{
49 $mail->Subject = "TEST d'un envoi d'e-mail depuis le site web";
50 }
51 $mail->Body = $message;
52 $mail->AltBody = $message;
53
54 $mail->send();
55 return true;
56 }
57 catch(Exception $e){
58 return false;
59 //echo "Le message n'a pas pu être envoyé. Erreur : {$mail->ErrorInfo}";
60 }
61 }
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
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 {