aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/front/controller/visitor/LoginController.php
blob: 4b881535c4174e30bb20f7a3587fd1401e0a97f8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<?php

declare(strict_types=1);

namespace Shaarli\Front\Controller\Visitor;

use Shaarli\Front\Exception\CantLoginException;
use Shaarli\Front\Exception\LoginBannedException;
use Shaarli\Front\Exception\WrongTokenException;
use Shaarli\Render\TemplatePage;
use Shaarli\Security\CookieManager;
use Shaarli\Security\SessionManager;
use Slim\Http\Request;
use Slim\Http\Response;

/**
 * Class LoginController
 *
 * Slim controller used to render the login page.
 *
 * The login page is not available if the user is banned
 * or if open shaarli setting is enabled.
 */
class LoginController extends ShaarliVisitorController
{
    /**
     * GET /login - Display the login page.
     */
    public function index(Request $request, Response $response): Response
    {
        try {
            $this->checkLoginState();
        } catch (CantLoginException $e) {
            return $this->redirect($response, '/');
        }

        if ($request->getParam('login') !== null) {
            $this->assignView('username', escape($request->getParam('login')));
        }

        $returnUrl = $request->getParam('returnurl') ?? $this->container->environment['HTTP_REFERER'] ?? null;

        $this
            ->assignView('returnurl', escape($returnUrl))
            ->assignView('remember_user_default', $this->container->conf->get('privacy.remember_user_default', true))
            ->assignView('pagetitle', t('Login') . ' - ' . $this->container->conf->get('general.title', 'Shaarli'))
        ;

        return $response->write($this->render(TemplatePage::LOGIN));
    }

    /**
     * POST /login - Process login
     */
    public function login(Request $request, Response $response): Response
    {
        if (!$this->container->sessionManager->checkToken($request->getParam('token'))) {
            throw new WrongTokenException();
        }

        try {
            $this->checkLoginState();
        } catch (CantLoginException $e) {
            return $this->redirect($response, '/');
        }

        if (
            !$this->container->loginManager->checkCredentials(
                client_ip_id($this->container->environment),
                $request->getParam('login'),
                $request->getParam('password')
            )
        ) {
            $this->container->loginManager->handleFailedLogin($this->container->environment);

            $this->container->sessionManager->setSessionParameter(
                SessionManager::KEY_ERROR_MESSAGES,
                [t('Wrong login/password.')]
            );

            // Call controller directly instead of unnecessary redirection
            return $this->index($request, $response);
        }

        $this->container->loginManager->handleSuccessfulLogin($this->container->environment);

        $cookiePath = $this->container->basePath . '/';
        $expirationTime = $this->saveLongLastingSession($request, $cookiePath);
        $this->renewUserSession($cookiePath, $expirationTime);

        // Force referer from given return URL
        $this->container->environment['HTTP_REFERER'] = $request->getParam('returnurl');

        return $this->redirectFromReferer($request, $response, ['login', 'install']);
    }

    /**
     * Make sure that the user is allowed to login and/or displaying the login page:
     *   - not already logged in
     *   - not open shaarli
     *   - not banned
     */
    protected function checkLoginState(): bool
    {
        if (
            $this->container->loginManager->isLoggedIn()
            || $this->container->conf->get('security.open_shaarli', false)
        ) {
            throw new CantLoginException();
        }

        if (true !== $this->container->loginManager->canLogin($this->container->environment)) {
            throw new LoginBannedException();
        }

        return true;
    }

    /**
     * @return int Session duration in seconds
     */
    protected function saveLongLastingSession(Request $request, string $cookiePath): int
    {
        if (empty($request->getParam('longlastingsession'))) {
            // Standard session expiration (=when browser closes)
            $expirationTime = 0;
        } else {
            // Keep the session cookie even after the browser closes
            $this->container->sessionManager->setStaySignedIn(true);
            $expirationTime = $this->container->sessionManager->extendSession();
        }

        $this->container->cookieManager->setCookieParameter(
            CookieManager::STAY_SIGNED_IN,
            $this->container->loginManager->getStaySignedInToken(),
            $expirationTime,
            $cookiePath
        );

        return $expirationTime;
    }

    protected function renewUserSession(string $cookiePath, int $expirationTime): void
    {
        // Send cookie with the new expiration date to the browser
        $this->container->sessionManager->destroy();
        $this->container->sessionManager->cookieParameters(
            $expirationTime,
            $cookiePath,
            $this->container->environment['SERVER_NAME']
        );
        $this->container->sessionManager->start();
        $this->container->sessionManager->regenerateId(true);
    }
}