summaryrefslogtreecommitdiff
path: root/src/controller/UserController.php
diff options
context:
space:
mode:
Diffstat (limited to 'src/controller/UserController.php')
-rw-r--r--src/controller/UserController.php284
1 files changed, 84 insertions, 200 deletions
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 {