diff options
author | ArthurHoaro <arthur@hoa.ro> | 2020-07-21 20:33:33 +0200 |
---|---|---|
committer | ArthurHoaro <arthur@hoa.ro> | 2020-07-23 21:19:21 +0200 |
commit | a8c11451e8d885a243c1ad52012093ba8d121e2c (patch) | |
tree | 19caf0ed75f5b26c34f3f979f2b95d3f1232dbae /application | |
parent | c4ad3d4f061d05a01db25aa54dda830ba776792d (diff) | |
download | Shaarli-a8c11451e8d885a243c1ad52012093ba8d121e2c.tar.gz Shaarli-a8c11451e8d885a243c1ad52012093ba8d121e2c.tar.zst Shaarli-a8c11451e8d885a243c1ad52012093ba8d121e2c.zip |
Process login through Slim controller
Diffstat (limited to 'application')
-rw-r--r-- | application/front/ShaarliMiddleware.php | 4 | ||||
-rw-r--r-- | application/front/controller/visitor/LoginController.php | 127 | ||||
-rw-r--r-- | application/front/exceptions/CantLoginException.php | 10 | ||||
-rw-r--r-- | application/security/SessionManager.php | 30 |
4 files changed, 160 insertions, 11 deletions
diff --git a/application/front/ShaarliMiddleware.php b/application/front/ShaarliMiddleware.php index 595182ac..e9f5552d 100644 --- a/application/front/ShaarliMiddleware.php +++ b/application/front/ShaarliMiddleware.php | |||
@@ -62,7 +62,9 @@ class ShaarliMiddleware | |||
62 | 62 | ||
63 | return $response->write($this->container->pageBuilder->render('error')); | 63 | return $response->write($this->container->pageBuilder->render('error')); |
64 | } catch (UnauthorizedException $e) { | 64 | } catch (UnauthorizedException $e) { |
65 | return $response->withRedirect($this->container->basePath . '/login'); | 65 | $returnUrl = urlencode($this->container->environment['REQUEST_URI']); |
66 | |||
67 | return $response->withRedirect($this->container->basePath . '/login?returnurl=' . $returnUrl); | ||
66 | } catch (\Throwable $e) { | 68 | } catch (\Throwable $e) { |
67 | // Unknown error encountered | 69 | // Unknown error encountered |
68 | $this->container->pageBuilder->reset(); | 70 | $this->container->pageBuilder->reset(); |
diff --git a/application/front/controller/visitor/LoginController.php b/application/front/controller/visitor/LoginController.php index a257766f..c40b8cc4 100644 --- a/application/front/controller/visitor/LoginController.php +++ b/application/front/controller/visitor/LoginController.php | |||
@@ -4,8 +4,12 @@ declare(strict_types=1); | |||
4 | 4 | ||
5 | namespace Shaarli\Front\Controller\Visitor; | 5 | namespace Shaarli\Front\Controller\Visitor; |
6 | 6 | ||
7 | use Shaarli\Front\Exception\CantLoginException; | ||
7 | use Shaarli\Front\Exception\LoginBannedException; | 8 | use Shaarli\Front\Exception\LoginBannedException; |
9 | use Shaarli\Front\Exception\WrongTokenException; | ||
8 | use Shaarli\Render\TemplatePage; | 10 | use Shaarli\Render\TemplatePage; |
11 | use Shaarli\Security\CookieManager; | ||
12 | use Shaarli\Security\SessionManager; | ||
9 | use Slim\Http\Request; | 13 | use Slim\Http\Request; |
10 | use Slim\Http\Response; | 14 | use Slim\Http\Response; |
11 | 15 | ||
@@ -19,29 +23,132 @@ use Slim\Http\Response; | |||
19 | */ | 23 | */ |
20 | class LoginController extends ShaarliVisitorController | 24 | class LoginController extends ShaarliVisitorController |
21 | { | 25 | { |
26 | /** | ||
27 | * GET /login - Display the login page. | ||
28 | */ | ||
22 | public function index(Request $request, Response $response): Response | 29 | public function index(Request $request, Response $response): Response |
23 | { | 30 | { |
24 | if ($this->container->loginManager->isLoggedIn() | 31 | try { |
25 | || $this->container->conf->get('security.open_shaarli', false) | 32 | $this->checkLoginState(); |
26 | ) { | 33 | } catch (CantLoginException $e) { |
27 | return $this->redirect($response, '/'); | 34 | return $this->redirect($response, '/'); |
28 | } | 35 | } |
29 | 36 | ||
30 | $userCanLogin = $this->container->loginManager->canLogin($request->getServerParams()); | 37 | if ($request->getParam('login') !== null) { |
31 | if ($userCanLogin !== true) { | 38 | $this->assignView('username', escape($request->getParam('login'))); |
32 | throw new LoginBannedException(); | ||
33 | } | 39 | } |
34 | 40 | ||
35 | if ($request->getParam('username') !== null) { | 41 | $returnUrl = $request->getParam('returnurl') ?? $this->container->environment['HTTP_REFERER'] ?? null; |
36 | $this->assignView('username', escape($request->getParam('username'))); | ||
37 | } | ||
38 | 42 | ||
39 | $this | 43 | $this |
40 | ->assignView('returnurl', escape($request->getServerParam('HTTP_REFERER'))) | 44 | ->assignView('returnurl', escape($returnUrl)) |
41 | ->assignView('remember_user_default', $this->container->conf->get('privacy.remember_user_default', true)) | 45 | ->assignView('remember_user_default', $this->container->conf->get('privacy.remember_user_default', true)) |
42 | ->assignView('pagetitle', t('Login') .' - '. $this->container->conf->get('general.title', 'Shaarli')) | 46 | ->assignView('pagetitle', t('Login') .' - '. $this->container->conf->get('general.title', 'Shaarli')) |
43 | ; | 47 | ; |
44 | 48 | ||
45 | return $response->write($this->render(TemplatePage::LOGIN)); | 49 | return $response->write($this->render(TemplatePage::LOGIN)); |
46 | } | 50 | } |
51 | |||
52 | /** | ||
53 | * POST /login - Process login | ||
54 | */ | ||
55 | public function login(Request $request, Response $response): Response | ||
56 | { | ||
57 | if (!$this->container->sessionManager->checkToken($request->getParam('token'))) { | ||
58 | throw new WrongTokenException(); | ||
59 | } | ||
60 | |||
61 | try { | ||
62 | $this->checkLoginState(); | ||
63 | } catch (CantLoginException $e) { | ||
64 | return $this->redirect($response, '/'); | ||
65 | } | ||
66 | |||
67 | if (!$this->container->loginManager->checkCredentials( | ||
68 | $this->container->environment['REMOTE_ADDR'], | ||
69 | client_ip_id($this->container->environment), | ||
70 | $request->getParam('login'), | ||
71 | $request->getParam('password') | ||
72 | ) | ||
73 | ) { | ||
74 | $this->container->loginManager->handleFailedLogin($this->container->environment); | ||
75 | |||
76 | $this->container->sessionManager->setSessionParameter( | ||
77 | SessionManager::KEY_ERROR_MESSAGES, | ||
78 | [t('Wrong login/password.')] | ||
79 | ); | ||
80 | |||
81 | // Call controller directly instead of unnecessary redirection | ||
82 | return $this->index($request, $response); | ||
83 | } | ||
84 | |||
85 | $this->container->loginManager->handleSuccessfulLogin($this->container->environment); | ||
86 | |||
87 | $cookiePath = $this->container->basePath . '/'; | ||
88 | $expirationTime = $this->saveLongLastingSession($request, $cookiePath); | ||
89 | $this->renewUserSession($cookiePath, $expirationTime); | ||
90 | |||
91 | // Force referer from given return URL | ||
92 | $this->container->environment['HTTP_REFERER'] = $request->getParam('returnurl'); | ||
93 | |||
94 | return $this->redirectFromReferer($request, $response, ['login']); | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Make sure that the user is allowed to login and/or displaying the login page: | ||
99 | * - not already logged in | ||
100 | * - not open shaarli | ||
101 | * - not banned | ||
102 | */ | ||
103 | protected function checkLoginState(): bool | ||
104 | { | ||
105 | if ($this->container->loginManager->isLoggedIn() | ||
106 | || $this->container->conf->get('security.open_shaarli', false) | ||
107 | ) { | ||
108 | throw new CantLoginException(); | ||
109 | } | ||
110 | |||
111 | if (true !== $this->container->loginManager->canLogin($this->container->environment)) { | ||
112 | throw new LoginBannedException(); | ||
113 | } | ||
114 | |||
115 | return true; | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * @return int Session duration in seconds | ||
120 | */ | ||
121 | protected function saveLongLastingSession(Request $request, string $cookiePath): int | ||
122 | { | ||
123 | if (empty($request->getParam('longlastingsession'))) { | ||
124 | // Standard session expiration (=when browser closes) | ||
125 | $expirationTime = 0; | ||
126 | } else { | ||
127 | // Keep the session cookie even after the browser closes | ||
128 | $this->container->sessionManager->setStaySignedIn(true); | ||
129 | $expirationTime = $this->container->sessionManager->extendSession(); | ||
130 | } | ||
131 | |||
132 | $this->container->cookieManager->setCookieParameter( | ||
133 | CookieManager::STAY_SIGNED_IN, | ||
134 | $this->container->loginManager->getStaySignedInToken(), | ||
135 | $expirationTime, | ||
136 | $cookiePath | ||
137 | ); | ||
138 | |||
139 | return $expirationTime; | ||
140 | } | ||
141 | |||
142 | protected function renewUserSession(string $cookiePath, int $expirationTime): void | ||
143 | { | ||
144 | // Send cookie with the new expiration date to the browser | ||
145 | $this->container->sessionManager->destroy(); | ||
146 | $this->container->sessionManager->cookieParameters( | ||
147 | $expirationTime, | ||
148 | $cookiePath, | ||
149 | $this->container->environment['SERVER_NAME'] | ||
150 | ); | ||
151 | $this->container->sessionManager->start(); | ||
152 | $this->container->sessionManager->regenerateId(true); | ||
153 | } | ||
47 | } | 154 | } |
diff --git a/application/front/exceptions/CantLoginException.php b/application/front/exceptions/CantLoginException.php new file mode 100644 index 00000000..cd16635d --- /dev/null +++ b/application/front/exceptions/CantLoginException.php | |||
@@ -0,0 +1,10 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Exception; | ||
6 | |||
7 | class CantLoginException extends \Exception | ||
8 | { | ||
9 | |||
10 | } | ||
diff --git a/application/security/SessionManager.php b/application/security/SessionManager.php index 82771c24..46219a3d 100644 --- a/application/security/SessionManager.php +++ b/application/security/SessionManager.php | |||
@@ -259,4 +259,34 @@ class SessionManager | |||
259 | { | 259 | { |
260 | return $this->savePath; | 260 | return $this->savePath; |
261 | } | 261 | } |
262 | |||
263 | /* | ||
264 | * Next public functions wrapping native PHP session API. | ||
265 | */ | ||
266 | |||
267 | public function destroy(): bool | ||
268 | { | ||
269 | $this->session = []; | ||
270 | |||
271 | return session_destroy(); | ||
272 | } | ||
273 | |||
274 | public function start(): bool | ||
275 | { | ||
276 | if (session_status() === PHP_SESSION_ACTIVE) { | ||
277 | $this->destroy(); | ||
278 | } | ||
279 | |||
280 | return session_start(); | ||
281 | } | ||
282 | |||
283 | public function cookieParameters(int $lifeTime, string $path, string $domain): bool | ||
284 | { | ||
285 | return session_set_cookie_params($lifeTime, $path, $domain); | ||
286 | } | ||
287 | |||
288 | public function regenerateId(bool $deleteOldSession = false): bool | ||
289 | { | ||
290 | return session_regenerate_id($deleteOldSession); | ||
291 | } | ||
262 | } | 292 | } |