aboutsummaryrefslogtreecommitdiffhomepage
path: root/application
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2020-05-27 13:35:48 +0200
committerArthurHoaro <arthur@hoa.ro>2020-07-23 21:19:21 +0200
commitef00f9d2033f6de11e71bf3a909399cae6f73a9f (patch)
tree96f47312084bab73be34495eed4280110a8ff258 /application
parentba43064ddb7771fc97df135a32f9b0d5e373dd36 (diff)
downloadShaarli-ef00f9d2033f6de11e71bf3a909399cae6f73a9f.tar.gz
Shaarli-ef00f9d2033f6de11e71bf3a909399cae6f73a9f.tar.zst
Shaarli-ef00f9d2033f6de11e71bf3a909399cae6f73a9f.zip
Process password change controller through Slim
Diffstat (limited to 'application')
-rw-r--r--application/front/controller/admin/PasswordController.php100
-rw-r--r--application/front/controller/admin/ShaarliAdminController.php59
-rw-r--r--application/front/controller/visitor/ShaarliVisitorController.php8
-rw-r--r--application/front/exceptions/OpenShaarliPasswordException.php18
-rw-r--r--application/front/exceptions/ShaarliFrontException.php4
-rw-r--r--application/front/exceptions/WrongTokenException.php18
-rw-r--r--application/render/PageBuilder.php26
-rw-r--r--application/security/SessionManager.php4
8 files changed, 230 insertions, 7 deletions
diff --git a/application/front/controller/admin/PasswordController.php b/application/front/controller/admin/PasswordController.php
new file mode 100644
index 00000000..6e8f0bcb
--- /dev/null
+++ b/application/front/controller/admin/PasswordController.php
@@ -0,0 +1,100 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Container\ShaarliContainer;
8use Shaarli\Front\Exception\OpenShaarliPasswordException;
9use Shaarli\Front\Exception\ShaarliFrontException;
10use Slim\Http\Request;
11use Slim\Http\Response;
12use Throwable;
13
14/**
15 * Class PasswordController
16 *
17 * Slim controller used to handle passwords update.
18 */
19class PasswordController extends ShaarliAdminController
20{
21 public function __construct(ShaarliContainer $container)
22 {
23 parent::__construct($container);
24
25 $this->assignView(
26 'pagetitle',
27 t('Change password') .' - '. $this->container->conf->get('general.title', 'Shaarli')
28 );
29 }
30
31 /**
32 * GET /password - Displays the change password template
33 */
34 public function index(Request $request, Response $response): Response
35 {
36 return $response->write($this->render('changepassword'));
37 }
38
39 /**
40 * POST /password - Change admin password - existing and new passwords need to be provided.
41 */
42 public function change(Request $request, Response $response): Response
43 {
44 $this->checkToken($request);
45
46 if ($this->container->conf->get('security.open_shaarli', false)) {
47 throw new OpenShaarliPasswordException();
48 }
49
50 $oldPassword = $request->getParam('oldpassword');
51 $newPassword = $request->getParam('setpassword');
52
53 if (empty($newPassword) || empty($oldPassword)) {
54 $this->saveErrorMessage(t('You must provide the current and new password to change it.'));
55
56 return $response
57 ->withStatus(400)
58 ->write($this->render('changepassword'))
59 ;
60 }
61
62 // Make sure old password is correct.
63 $oldHash = sha1(
64 $oldPassword .
65 $this->container->conf->get('credentials.login') .
66 $this->container->conf->get('credentials.salt')
67 );
68
69 if ($oldHash !== $this->container->conf->get('credentials.hash')) {
70 $this->saveErrorMessage(t('The old password is not correct.'));
71
72 return $response
73 ->withStatus(400)
74 ->write($this->render('changepassword'))
75 ;
76 }
77
78 // Save new password
79 // Salt renders rainbow-tables attacks useless.
80 $this->container->conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand()));
81 $this->container->conf->set(
82 'credentials.hash',
83 sha1(
84 $newPassword
85 . $this->container->conf->get('credentials.login')
86 . $this->container->conf->get('credentials.salt')
87 )
88 );
89
90 try {
91 $this->container->conf->write($this->container->loginManager->isLoggedIn());
92 } catch (Throwable $e) {
93 throw new ShaarliFrontException($e->getMessage(), 500, $e);
94 }
95
96 $this->saveSuccessMessage(t('Your password has been changed'));
97
98 return $response->write($this->render('changepassword'));
99 }
100}
diff --git a/application/front/controller/admin/ShaarliAdminController.php b/application/front/controller/admin/ShaarliAdminController.php
index ea703f62..3385006c 100644
--- a/application/front/controller/admin/ShaarliAdminController.php
+++ b/application/front/controller/admin/ShaarliAdminController.php
@@ -7,7 +7,19 @@ namespace Shaarli\Front\Controller\Admin;
7use Shaarli\Container\ShaarliContainer; 7use Shaarli\Container\ShaarliContainer;
8use Shaarli\Front\Controller\Visitor\ShaarliVisitorController; 8use Shaarli\Front\Controller\Visitor\ShaarliVisitorController;
9use Shaarli\Front\Exception\UnauthorizedException; 9use Shaarli\Front\Exception\UnauthorizedException;
10use Shaarli\Front\Exception\WrongTokenException;
11use Shaarli\Security\SessionManager;
12use Slim\Http\Request;
10 13
14/**
15 * Class ShaarliAdminController
16 *
17 * All admin controllers (for logged in users) MUST extend this abstract class.
18 * It makes sure that the user is properly logged in, and otherwise throw an exception
19 * which will redirect to the login page.
20 *
21 * @package Shaarli\Front\Controller\Admin
22 */
11abstract class ShaarliAdminController extends ShaarliVisitorController 23abstract class ShaarliAdminController extends ShaarliVisitorController
12{ 24{
13 public function __construct(ShaarliContainer $container) 25 public function __construct(ShaarliContainer $container)
@@ -18,4 +30,51 @@ abstract class ShaarliAdminController extends ShaarliVisitorController
18 throw new UnauthorizedException(); 30 throw new UnauthorizedException();
19 } 31 }
20 } 32 }
33
34 /**
35 * Any persistent action to the config or data store must check the XSRF token validity.
36 */
37 protected function checkToken(Request $request): void
38 {
39 if (!$this->container->sessionManager->checkToken($request->getParam('token'))) {
40 throw new WrongTokenException();
41 }
42 }
43
44 /**
45 * Save a SUCCESS message in user session, which will be displayed on any template page.
46 */
47 protected function saveSuccessMessage(string $message): void
48 {
49 $this->saveMessage(SessionManager::KEY_SUCCESS_MESSAGES, $message);
50 }
51
52 /**
53 * Save a WARNING message in user session, which will be displayed on any template page.
54 */
55 protected function saveWarningMessage(string $message): void
56 {
57 $this->saveMessage(SessionManager::KEY_WARNING_MESSAGES, $message);
58 }
59
60 /**
61 * Save an ERROR message in user session, which will be displayed on any template page.
62 */
63 protected function saveErrorMessage(string $message): void
64 {
65 $this->saveMessage(SessionManager::KEY_ERROR_MESSAGES, $message);
66 }
67
68 /**
69 * Use the sessionManager to save the provided message using the proper type.
70 *
71 * @param string $type successed/warnings/errors
72 */
73 protected function saveMessage(string $type, string $message): void
74 {
75 $messages = $this->container->sessionManager->getSessionParameter($type) ?? [];
76 $messages[] = $message;
77
78 $this->container->sessionManager->setSessionParameter($type, $messages);
79 }
21} 80}
diff --git a/application/front/controller/visitor/ShaarliVisitorController.php b/application/front/controller/visitor/ShaarliVisitorController.php
index 655b3baa..f12915c1 100644
--- a/application/front/controller/visitor/ShaarliVisitorController.php
+++ b/application/front/controller/visitor/ShaarliVisitorController.php
@@ -9,6 +9,14 @@ use Shaarli\Container\ShaarliContainer;
9use Slim\Http\Request; 9use Slim\Http\Request;
10use Slim\Http\Response; 10use Slim\Http\Response;
11 11
12/**
13 * Class ShaarliVisitorController
14 *
15 * All controllers accessible by visitors (non logged in users) should extend this abstract class.
16 * Contains a few helper function for template rendering, plugins, etc.
17 *
18 * @package Shaarli\Front\Controller\Visitor
19 */
12abstract class ShaarliVisitorController 20abstract class ShaarliVisitorController
13{ 21{
14 /** @var ShaarliContainer */ 22 /** @var ShaarliContainer */
diff --git a/application/front/exceptions/OpenShaarliPasswordException.php b/application/front/exceptions/OpenShaarliPasswordException.php
new file mode 100644
index 00000000..a6f0b3ae
--- /dev/null
+++ b/application/front/exceptions/OpenShaarliPasswordException.php
@@ -0,0 +1,18 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Exception;
6
7/**
8 * Class OpenShaarliPasswordException
9 *
10 * Raised if the user tries to change the admin password on an open shaarli instance.
11 */
12class OpenShaarliPasswordException extends ShaarliFrontException
13{
14 public function __construct()
15 {
16 parent::__construct(t('You are not supposed to change a password on an Open Shaarli.'), 403);
17 }
18}
diff --git a/application/front/exceptions/ShaarliFrontException.php b/application/front/exceptions/ShaarliFrontException.php
index fc8eb92b..73847e6d 100644
--- a/application/front/exceptions/ShaarliFrontException.php
+++ b/application/front/exceptions/ShaarliFrontException.php
@@ -9,11 +9,11 @@ use Throwable;
9/** 9/**
10 * Class ShaarliException 10 * Class ShaarliException
11 * 11 *
12 * Abstract exception class used to defined any custom exception thrown during front rendering. 12 * Exception class used to defined any custom exception thrown during front rendering.
13 * 13 *
14 * @package Front\Exception 14 * @package Front\Exception
15 */ 15 */
16abstract class ShaarliFrontException extends \Exception 16class ShaarliFrontException extends \Exception
17{ 17{
18 /** Override parent constructor to force $message and $httpCode parameters to be set. */ 18 /** Override parent constructor to force $message and $httpCode parameters to be set. */
19 public function __construct(string $message, int $httpCode, Throwable $previous = null) 19 public function __construct(string $message, int $httpCode, Throwable $previous = null)
diff --git a/application/front/exceptions/WrongTokenException.php b/application/front/exceptions/WrongTokenException.php
new file mode 100644
index 00000000..42002720
--- /dev/null
+++ b/application/front/exceptions/WrongTokenException.php
@@ -0,0 +1,18 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Exception;
6
7/**
8 * Class OpenShaarliPasswordException
9 *
10 * Raised if the user tries to perform an action with an invalid XSRF token.
11 */
12class WrongTokenException extends ShaarliFrontException
13{
14 public function __construct()
15 {
16 parent::__construct(t('Wrong token.'), 403);
17 }
18}
diff --git a/application/render/PageBuilder.php b/application/render/PageBuilder.php
index f4fefda8..264cd33b 100644
--- a/application/render/PageBuilder.php
+++ b/application/render/PageBuilder.php
@@ -7,6 +7,7 @@ use RainTPL;
7use Shaarli\ApplicationUtils; 7use Shaarli\ApplicationUtils;
8use Shaarli\Bookmark\BookmarkServiceInterface; 8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager; 9use Shaarli\Config\ConfigManager;
10use Shaarli\Security\SessionManager;
10use Shaarli\Thumbnailer; 11use Shaarli\Thumbnailer;
11 12
12/** 13/**
@@ -136,17 +137,28 @@ class PageBuilder
136 $this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width')); 137 $this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width'));
137 $this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height')); 138 $this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height'));
138 139
139 if (!empty($_SESSION['warnings'])) {
140 $this->tpl->assign('global_warnings', $_SESSION['warnings']);
141 unset($_SESSION['warnings']);
142 }
143
144 $this->tpl->assign('formatter', $this->conf->get('formatter', 'default')); 140 $this->tpl->assign('formatter', $this->conf->get('formatter', 'default'));
145 141
146 // To be removed with a proper theme configuration. 142 // To be removed with a proper theme configuration.
147 $this->tpl->assign('conf', $this->conf); 143 $this->tpl->assign('conf', $this->conf);
148 } 144 }
149 145
146 protected function finalize(): void
147 {
148 // TODO: use the SessionManager
149 $messageKeys = [
150 SessionManager::KEY_SUCCESS_MESSAGES,
151 SessionManager::KEY_WARNING_MESSAGES,
152 SessionManager::KEY_ERROR_MESSAGES
153 ];
154 foreach ($messageKeys as $messageKey) {
155 if (!empty($_SESSION[$messageKey])) {
156 $this->tpl->assign('global_' . $messageKey, $_SESSION[$messageKey]);
157 unset($_SESSION[$messageKey]);
158 }
159 }
160 }
161
150 /** 162 /**
151 * The following assign() method is basically the same as RainTPL (except lazy loading) 163 * The following assign() method is basically the same as RainTPL (except lazy loading)
152 * 164 *
@@ -196,6 +208,8 @@ class PageBuilder
196 $this->initialize(); 208 $this->initialize();
197 } 209 }
198 210
211 $this->finalize();
212
199 $this->tpl->draw($page); 213 $this->tpl->draw($page);
200 } 214 }
201 215
@@ -213,6 +227,8 @@ class PageBuilder
213 $this->initialize(); 227 $this->initialize();
214 } 228 }
215 229
230 $this->finalize();
231
216 return $this->tpl->draw($page, true); 232 return $this->tpl->draw($page, true);
217 } 233 }
218 234
diff --git a/application/security/SessionManager.php b/application/security/SessionManager.php
index 8b77d362..0ac17d9a 100644
--- a/application/security/SessionManager.php
+++ b/application/security/SessionManager.php
@@ -12,6 +12,10 @@ class SessionManager
12 public const KEY_VISIBILITY = 'visibility'; 12 public const KEY_VISIBILITY = 'visibility';
13 public const KEY_UNTAGGED_ONLY = 'untaggedonly'; 13 public const KEY_UNTAGGED_ONLY = 'untaggedonly';
14 14
15 public const KEY_SUCCESS_MESSAGES = 'successes';
16 public const KEY_WARNING_MESSAGES = 'warnings';
17 public const KEY_ERROR_MESSAGES = 'errors';
18
15 /** @var int Session expiration timeout, in seconds */ 19 /** @var int Session expiration timeout, in seconds */
16 public static $SHORT_TIMEOUT = 3600; // 1 hour 20 public static $SHORT_TIMEOUT = 3600; // 1 hour
17 21