aboutsummaryrefslogtreecommitdiffhomepage
path: root/application
diff options
context:
space:
mode:
Diffstat (limited to 'application')
-rw-r--r--application/front/ShaarliMiddleware.php4
-rw-r--r--application/front/controller/visitor/LoginController.php127
-rw-r--r--application/front/exceptions/CantLoginException.php10
-rw-r--r--application/security/SessionManager.php30
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
5namespace Shaarli\Front\Controller\Visitor; 5namespace Shaarli\Front\Controller\Visitor;
6 6
7use Shaarli\Front\Exception\CantLoginException;
7use Shaarli\Front\Exception\LoginBannedException; 8use Shaarli\Front\Exception\LoginBannedException;
9use Shaarli\Front\Exception\WrongTokenException;
8use Shaarli\Render\TemplatePage; 10use Shaarli\Render\TemplatePage;
11use Shaarli\Security\CookieManager;
12use Shaarli\Security\SessionManager;
9use Slim\Http\Request; 13use Slim\Http\Request;
10use Slim\Http\Response; 14use Slim\Http\Response;
11 15
@@ -19,29 +23,132 @@ use Slim\Http\Response;
19 */ 23 */
20class LoginController extends ShaarliVisitorController 24class 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
3declare(strict_types=1);
4
5namespace Shaarli\Front\Exception;
6
7class 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}