]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Controllers/AuthController.php
Remove 'svn' lines
[galette.git] / galette / lib / Galette / Controllers / AuthController.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Galette authentication controller
7 *
8 * PHP version 5
9 *
10 * Copyright © 2019-2020 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * @category Entity
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2019-2020 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.tuxfamily.org
34 * @since Available since 0.9.4dev - 2019-12-02
35 */
36
37 namespace Galette\Controllers;
38
39 use Slim\Http\Request;
40 use Slim\Http\Response;
41 use Galette\Core\SysInfos;
42 use Galette\Core\Login;
43 use Galette\Core\Password;
44 use Galette\Core\GaletteMail;
45 use Galette\Entity\Adherent;
46 use Galette\Entity\Texts;
47 use Analog\Analog;
48
49 /**
50 * Galette authentication controller
51 *
52 * @category Controllers
53 * @name AuthController
54 * @package Galette
55 * @author Johan Cwiklinski <johan@x-tnd.be>
56 * @copyright 2019-2020 The Galette Team
57 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
58 * @link http://galette.tuxfamily.org
59 * @since Available since 0.9.4dev - 2019-12-02
60 */
61
62 class AuthController extends AbstractController
63 {
64 /**
65 * Log in
66 *
67 * @param Request $request PSR Request
68 * @param Response $response PSR Response
69 * @param array $args Request arguments ['r']
70 *
71 * @return void
72 */
73 public function login(Request $request, Response $response, array $args = [])
74 {
75 //store redirect path if any
76 if (
77 isset($args['r'])
78 && $args['r'] != '/logout'
79 && $args['r'] != '/login'
80 ) {
81 $this->session->urlRedirect = $args['r'];
82 }
83
84 if (!$this->login->isLogged()) {
85 // display page
86 $this->view->render(
87 $response,
88 'index.tpl',
89 array(
90 'page_title' => _T("Login"),
91 )
92 );
93 return $response;
94 } else {
95 return $this->galetteRedirect($request, $response, $args);
96 }
97 }
98
99 /**
100 * Do login
101 *
102 * @param Request $request PSR Request
103 * @param Response $response PSR Response
104 *
105 * @return void
106 */
107 public function doLogin(Request $request, Response $response)
108 {
109 $nick = $request->getParsedBody()['login'];
110 $password = $request->getParsedBody()['password'];
111 $checkpass = new \Galette\Util\Password($this->preferences);
112
113 if (trim($nick) == '' || trim($password) == '') {
114 $this->flash->addMessage(
115 'loginfault',
116 _T("You must provide both login and password.")
117 );
118 return $response
119 ->withStatus(301)
120 ->withHeader('Location', $this->router->pathFor('login'));
121 }
122
123 if ($nick === $this->preferences->pref_admin_login) {
124 $pw_superadmin = password_verify(
125 $password,
126 $this->preferences->pref_admin_pass
127 );
128 if (!$pw_superadmin) {
129 $pw_superadmin = (
130 md5($password) === $this->preferences->pref_admin_pass
131 );
132 }
133 if ($pw_superadmin) {
134 $this->login->logAdmin($nick, $this->preferences);
135 }
136 } else {
137 $this->login->logIn($nick, $password);
138 }
139
140 if ($this->login->isLogged()) {
141 if (!$checkpass->isValid($password)) {
142 //password is no longer valid with current rules, must be changed
143 $this->flash->addMessage(
144 'warning_detected',
145 _T("Your password is too weak! Please consider updating it.") .
146 '<br/> -' . implode('<br/>', $checkpass->getErrors())
147 );
148 }
149 $this->session->login = $this->login;
150 $this->history->add(_T("Login"));
151 return $this->galetteRedirect($request, $response, []);
152 } else {
153 $this->flash->addMessage('error_detected', _T("Login failed."));
154 $this->history->add(_T("Authentication failed"), $nick);
155 return $response->withStatus(301)->withHeader('Location', $this->router->pathFor('login'));
156 }
157 }
158
159 /**
160 * Log out
161 *
162 * @param Request $request PSR Request
163 * @param Response $response PSR Response
164 *
165 * @return void
166 */
167 public function logout(Request $request, Response $response)
168 {
169 $this->login->logOut();
170 $this->history->add(_T("Log off"));
171 \RKA\Session::destroy();
172 return $response
173 ->withStatus(301)
174 ->withHeader('Location', $this->router->pathFor('slash'));
175 }
176
177 /**
178 * Impersonate
179 *
180 * @param Request $request PSR Request
181 * @param Response $response PSR Response
182 * @param array $args Request arguments ['id']
183 *
184 * @return void
185 */
186 public function impersonate(Request $request, Response $response, array $args)
187 {
188 $success = $this->login->impersonate((int)$args['id']);
189
190 if ($success === true) {
191 $this->session->login = $this->login;
192 $msg = str_replace(
193 '%login',
194 $this->login->login,
195 _T("Impersonating as %login")
196 );
197
198 $this->history->add($msg);
199 $this->flash->addMessage(
200 'success_detected',
201 $msg
202 );
203 } else {
204 $msg = str_replace(
205 '%id',
206 $args['id'],
207 _T("Unable to impersonate as %id")
208 );
209 $this->flash->addMessage(
210 'error_detected',
211 $msg
212 );
213 $this->history->add($msg);
214 }
215
216 return $response
217 ->withStatus(301)
218 ->withHeader('Location', $this->router->pathFor('slash'));
219 }
220
221 /**
222 * End impersonate
223 *
224 * @param Request $request PSR Request
225 * @param Response $response PSR Response
226 *
227 * @return void
228 */
229 public function unimpersonate(Request $request, Response $response)
230 {
231 $login = new Login($this->zdb, $this->i18n, $this->session);
232 $login->logAdmin($this->preferences->pref_admin_login, $this->preferences);
233 $this->history->add(_T("Impersonating ended"));
234 $this->session->login = $login;
235 $this->login = $login;
236 $this->flash->addMessage(
237 'success_detected',
238 _T("Impersonating ended")
239 );
240 return $response
241 ->withStatus(301)
242 ->withHeader('Location', $this->router->pathFor('slash'));
243 }
244
245 /**
246 * Lost password page
247 *
248 * @param Request $request PSR Request
249 * @param Response $response PSR Response
250 *
251 * @return Response
252 */
253 public function lostPassword(Request $request, Response $response): Response
254 {
255 if ($this->preferences->pref_mail_method === GaletteMail::METHOD_DISABLED) {
256 throw new \RuntimeException('Mailing disabled.');
257 }
258 // display page
259 $this->view->render(
260 $response,
261 'lostpasswd.tpl',
262 array(
263 'page_title' => _T("Password recovery")
264 )
265 );
266 return $response;
267 }
268
269 /**
270 * Retrieve password procedure
271 *
272 * @param Request $request PSR Request
273 * @param Response $response PSR Response
274 * @param array $args Request arguments ['id']
275 *
276 * @return Response
277 */
278 public function retrievePassword(Request $request, Response $response, array $args): Response
279 {
280 $from_admin = false;
281 $redirect_url = $this->router->pathFor('slash');
282 if ((($this->login->isAdmin() || $this->login->isStaff()) && isset($args[Adherent::PK]))) {
283 $from_admin = true;
284 $redirect_url = $this->router->pathFor('member', ['id' => $args[Adherent::PK]]);
285 }
286
287 if (
288 ($this->login->isLogged()
289 || $this->preferences->pref_mail_method == GaletteMail::METHOD_DISABLED)
290 && !$from_admin
291 ) {
292 if ($this->preferences->pref_mail_method == GaletteMail::METHOD_DISABLED) {
293 $this->flash->addMessage(
294 'error_detected',
295 _T("Email sent is disabled in the preferences. Ask galette admin")
296 );
297 }
298 return $response
299 ->withStatus(301)
300 ->withHeader('Location', $redirect_url);
301 }
302
303 $adh = null;
304 $login_adh = null;
305 if (($this->login->isAdmin() || $this->login->isStaff()) && isset($args[Adherent::PK])) {
306 $adh = new Adherent($this->zdb, (int)$args[Adherent::PK]);
307 $login_adh = $adh->login;
308 } else {
309 $post = $request->getParsedBody();
310 $login_adh = $post['login'];
311 $adh = new Adherent($this->zdb, $login_adh);
312 }
313
314 if ($adh->id != '') {
315 //account has been found, proceed
316 if (GaletteMail::isValidEmail($adh->email)) {
317 $password = new Password($this->zdb);
318 $res = $password->generateNewPassword($adh->id);
319 if ($res == true) {
320 $link_validity = new \DateTime();
321 $link_validity->add(new \DateInterval('PT24H'));
322
323 $df = _T("Y-m-d H:i:s");
324
325 $texts = new Texts(
326 $this->preferences,
327 $this->router,
328 array(
329 'change_pass_uri' => $this->preferences->getURL() .
330 $this->router->pathFor(
331 'password-recovery',
332 ['hash' => base64_encode($password->getHash())]
333 ),
334 'link_validity' => $link_validity->format(_T("Y-m-d H:i:s")),
335 'login_adh' => custom_html_entity_decode($adh->login, ENT_QUOTES)
336 )
337 );
338 $mtxt = $texts->getTexts('pwd', $adh->language);
339
340 $mail = new GaletteMail($this->preferences);
341 $mail->setSubject($texts->getSubject());
342 $mail->setRecipients(
343 array(
344 $adh->email => $adh->sname
345 )
346 );
347
348 $mail->setMessage($texts->getBody());
349 $sent = $mail->send();
350
351 if ($sent == GaletteMail::MAIL_SENT) {
352 $this->history->add(
353 str_replace(
354 '%s',
355 $login_adh,
356 _T("Email sent to '%s' for password recovery.")
357 )
358 );
359 if ($from_admin === false) {
360 $message = _T("An email has been sent to your address.<br/>Please check your inbox and follow the instructions.");
361 $done = true;
362 } else {
363 $message = _T("An email has been sent to the member.");
364 }
365
366 $this->flash->addMessage(
367 'success_detected',
368 $message
369 );
370 } else {
371 $str = str_replace(
372 '%s',
373 $login_adh,
374 _T("A problem happened while sending password for account '%s'")
375 );
376 $this->history->add($str);
377 $this->flash->addMessage(
378 'error_detected',
379 $str
380 );
381
382 $error_detected[] = $str;
383 }
384 } else {
385 $str = str_replace(
386 '%s',
387 $login_adh,
388 _T("An error occurred storing temporary password for %s. Please inform an admin.")
389 );
390 $this->history->add($str);
391 $this->flash->addMessage(
392 'error_detected',
393 $str
394 );
395 }
396 } else {
397 $str = str_replace(
398 '%s',
399 $login_adh,
400 _T("Your account (%s) do not contain any valid email address")
401 );
402 $this->history->add($str);
403 $this->flash->addMessage(
404 'error_detected',
405 $str
406 );
407 }
408 } else {
409 //account has not been found
410 if (GaletteMail::isValidEmail($login_adh)) {
411 $str = str_replace(
412 '%s',
413 $login_adh,
414 _T("Mails address %s does not exist")
415 );
416 } else {
417 $str = str_replace(
418 '%s',
419 $login_adh,
420 _T("Login %s does not exist")
421 );
422 }
423
424 $this->history->add($str);
425 $this->flash->addMessage(
426 'error_detected',
427 $str
428 );
429 }
430
431 return $response
432 ->withStatus(301)
433 ->withHeader('Location', $redirect_url);
434 }
435
436 /**
437 * Password recovery page
438 *
439 * @param Request $request PSR Request
440 * @param Response $response PSR Response
441 * @param array $args Request arguments
442 *
443 * @return Response
444 */
445 public function recoverPassword(Request $request, Response $response, array $args): Response
446 {
447 $password = new Password($this->zdb);
448 if (!$id_adh = $password->isHashValid(base64_decode($args['hash']))) {
449 $this->flash->addMessage(
450 'warning_detected',
451 _T("This link is no longer valid. You should ask to retrieve your password again.")
452 );
453 return $response
454 ->withStatus(301)
455 ->withHeader(
456 'Location',
457 $this->router->pathFor('password-lost')
458 );
459 }
460
461 // display page
462 $this->view->render(
463 $response,
464 'change_passwd.tpl',
465 array(
466 'hash' => $args['hash'],
467 'page_title' => _T("Password recovery")
468 )
469 );
470 return $response;
471 }
472
473 /**
474 * Password recovery
475 *
476 * @param Request $request PSR Request
477 * @param Response $response PSR Response
478 *
479 * @return Response
480 */
481 public function doRecoverPassword(Request $request, Response $response): Response
482 {
483 $post = $request->getParsedBody();
484 $password = new Password($this->zdb);
485 $hash_ok = true;
486
487 if (!$id_adh = $password->isHashValid(base64_decode($post['hash']))) {
488 return $response
489 ->withStatus(301)
490 ->withHeader(
491 'Location',
492 $this->router->pathFor('password-recovery', ['hash' => $post['hash']])
493 );
494 }
495
496 $error = null;
497 if ($post['mdp_adh'] == '') {
498 $error = _T("No password");
499 } elseif (isset($post['mdp_adh2'])) {
500 if (strcmp($post['mdp_adh'], $post['mdp_adh2'])) {
501 $error = _T("- The passwords don't match!");
502 } else {
503 $checkpass = new \Galette\Util\Password($this->preferences);
504
505 if (!$checkpass->isValid($post['mdp_adh'])) {
506 //password is not valid with current rules
507 $error = _T("Your password is too weak!") .
508 '<br/> -' . implode('<br/>', $checkpass->getErrors());
509 } else {
510 $res = Adherent::updatePassword(
511 $this->zdb,
512 $id_adh,
513 $post['mdp_adh']
514 );
515 if ($res !== true) {
516 $error = _T("An error occurred while updating your password.");
517 } else {
518 $this->history->add(
519 str_replace(
520 '%s',
521 $id_adh,
522 _T("Password changed for member '%s'.")
523 )
524 );
525 //once password has been changed, we can remove the
526 //temporary password entry
527 $password->removeHash(base64_decode($post['hash']));
528 $password_updated = true;
529 $this->flash->addMessage(
530 'success_detected',
531 _T("Your password has been changed!")
532 );
533 return $response
534 ->withStatus(301)
535 ->withHeader(
536 'Location',
537 $this->router->pathFor('slash')
538 );
539 }
540 }
541 }
542 }
543
544 if ($error !== null) {
545 $this->flash->addMessage(
546 'error_detected',
547 $error
548 );
549 }
550
551 return $response
552 ->withStatus(301)
553 ->withHeader(
554 'Location',
555 $this->router->pathFor('password-recovery', ['hash' => $post['hash']])
556 );
557 }
558 }