User::logout()   A
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4.0092

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 12
c 4
b 0
f 0
dl 0
loc 23
ccs 11
cts 12
cp 0.9167
rs 9.8666
cc 4
nc 8
nop 0
crap 4.0092
1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * php version 7.1.0
11
 *
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <[email protected]>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20
21
declare(strict_types=1);
22
23
namespace Shieldon\Firewall\Panel;
24
25
use Psr\Http\Message\ResponseInterface;
26
use Shieldon\Firewall\Panel\BaseController;
27
use Shieldon\Firewall\Kernel\Enum;
28
use Shieldon\Firewall\Firewall\Captcha\CaptchaFactory;
29
use Shieldon\Firewall\Captcha\Foundation;
30
use Shieldon\Event\Event;
31
use function Shieldon\Firewall\__;
32
use function Shieldon\Firewall\get_request;
33
use function Shieldon\Firewall\get_response;
34
use function Shieldon\Firewall\get_session_instance;
35
use function Shieldon\Firewall\unset_superglobal;
36
use function password_verify;
37
38
/**
39
 * User
40
 */
41
class User extends BaseController
42
{
43
    /**
44
     *   Public methods       | Desctiotion
45
     *  ----------------------|---------------------------------------------
46
     *   login                | Display the login form.
47
     *   logout               | Remove the login status.
48
     *  ----------------------|---------------------------------------------
49
     */
50
51
    /**
52
     * Constructor.
53 10
     */
54
    public function __construct()
55 10
    {
56
        parent::__construct();
57
    }
58
59
    /**
60
     * Login
61
     *
62
     * @param string $mode login mode.
63
     *
64
     * @return ResponseInterface
65 9
     */
66
    public function login(): ResponseInterface
67 9
    {
68
        $this->applyCaptchaForms();
69 9
70 9
        $postParams = get_request()->getParsedBody();
71 9
        $login = false;
72
        $data = [];
73 9
74 9
        $data['error'] = '';
75
        $addonTitle = $this->markAsDemo;
76
77 9
        if (isset($postParams['s_user']) &&
78 9
            isset($postParams['s_pass'])
79
        ) {
80 5
            if ($this->mode === 'demo') {
81 2
                $loginResult = $this->userLoginAsDemo(
82 2
                    $postParams['s_user'],
83 2
                    $postParams['s_pass']
84 2
                );
85
            } else {
86 3
                $loginResult = $this->userLoginAsAdmin(
87 3
                    $postParams['s_user'],
88 3
                    $postParams['s_pass']
89 3
                );
90
            }
91
92 5
            $login = $loginResult['result'];
93 5
            $data['error'] = $loginResult['message'];
94
        }
95
96 9
        if ($login) {
97
            // This session variable is to mark current session as a logged user.
98 2
            get_session_instance()->set('shieldon_user_login', true);
99
100 2
            Event::doDispatch('user_login');
101
102
            // Redirect to overview page if logged in successfully.
103 2
            return get_response()->withHeader('Location', $this->url('home/overview'));
0 ignored issues
show
Bug Best Practice introduced by
The expression return get_response()->w...->url('home/overview')) returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
104
        }
105
106
        // Start to prompt a login form is not logged.
107 8
        if (!defined('SHIELDON_VIEW')) {
108 8
            define('SHIELDON_VIEW', true);
109
        }
110
111
        // `$ui` will be used in `css-default.php`. Do not remove it.
112 8
        $ui = [
113 8
            'background_image' => '',
114 8
            'bg_color'         => '#ffffff',
115 8
            'header_bg_color'  => '#212531',
116 8
            'header_color'     => '#ffffff',
117 8
            'shadow_opacity'   => '0.2',
118 8
        ];
119
120 8
        $data['csrf'] = $this->fieldCsrf();
121 8
        $data['form'] = get_request()->getUri()->getPath();
122 8
        $data['captchas'] = $this->captcha;
123
124 8
        $data['css'] = include Enum::KERNEL_DIR . '/../../templates/frontend/css/default.php';
125
126 8
        unset($ui);
127
128 8
        $data['title'] = __('panel', 'title_login', 'Login') . $addonTitle;
129
130 8
        return $this->respond(
131 8
            $this->loadView('frontend/login', $data)
132 8
        );
133
    }
134
135
    /**
136
     * Logout
137
     *
138
     * @return ResponseInterface
139
     */
140 1
    public function logout(): ResponseInterface
141
    {
142 1
        $sessionLoginStatus = get_session_instance()->get('shieldon_user_login');
143 1
        $sessionPanelLang = get_session_instance()->get('shieldon_panel_lang');
144 1
        $response = get_response();
145
146 1
        if (isset($sessionLoginStatus)) {
147 1
            unset_superglobal('shieldon_user_login', 'session');
148
        }
149
150 1
        if (isset($sessionPanelLang)) {
151 1
            unset_superglobal('shieldon_panel_lang', 'session');
152
        }
153
154
        // @codeCoverageIgnoreStart
155 1
        if ($this->kernel->psr7) {
156
            unset_superglobal('_shieldon', 'cookie');
157
        } else {
158 1
            setcookie('_shieldon', '', time() - 3600, '/');
159
        }
160
        // // @codeCoverageIgnoreEnd
161
162 1
        return $response->withHeader('Location', $this->url('user/login'));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response->withHe...his->url('user/login')) returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
163
    }
164
165
    /**
166
     * Set the Captcha modules.
167
     *
168
     * @return void
169
     */
170 9
    protected function applyCaptchaForms(): void
171
    {
172 9
        $this->captcha[] = new Foundation();
173
174 9
        $captchaList = [
175 9
            'recaptcha',
176 9
            'image',
177 9
        ];
178
179 9
        foreach ($captchaList as $captcha) {
180 9
            $setting = $this->getConfig('captcha_modules.' . $captcha);
181
182 9
            if (is_array($setting)) {
183 9
                if (CaptchaFactory::check($setting)) {
184 1
                    $this->captcha[] = CaptchaFactory::getInstance($captcha, $setting);
185
                }
186
            }
187 9
            unset($setting);
188
        }
189
    }
190
191
    /**
192
     * Login as demonstration.
193
     *
194
     * @param string $username The username.
195
     * @param string $password The password.
196
     *
197
     * @return array
198
     */
199 2
    private function userLoginAsDemo($username, $password): array
200
    {
201 2
        $login = false;
202 2
        $errorMsg = '';
203
204 2
        if ($username === 'demo' && $password === 'demo') {
205 2
            $login = true;
206
        }
207
208 2
        $captcha = $this->checkCaptchaValidation($login, $errorMsg);
209 2
        $login = $captcha['result'];
210 2
        $errorMsg = $captcha['message'];
211
212 2
        return [
213 2
            'result' => $login,
214 2
            'message' => $errorMsg,
215 2
        ];
216
    }
217
218
    /**
219
     * Login as administration.
220
     *
221
     * @param string $username The username.
222
     * @param string $password The password.
223
     *
224
     * @return array
225
     */
226 3
    private function userLoginAsAdmin($username, $password): array
227
    {
228 3
        $admin = $this->getConfig('admin');
229
230 3
        $login = false;
231 3
        $errorMsg = '';
232
233
        if (
234
            // Default password, unencrypted.
235 3
            $admin['user'] === $username &&
236 3
            $admin['pass'] === $password
237
        ) {
238
            // @codeCoverageIgnoreStart
239
            $login = true;
240
            // @codeCoverageIgnoreEnd
241
        } elseif (
242
            // User has already changed password, encrypted.
243
            $admin['user'] === $username &&
244 3
            password_verify($password, $admin['pass'])
245 3
        ) {
246
            $login = true;
247 2
        } else {
248
            $errorMsg = __('panel', 'login_message_invalid_user_or_pass', 'Invalid username or password.');
249
        }
250 1
251
        $captcha = $this->checkCaptchaValidation($login, $errorMsg);
252
        $login = $captcha['result'];
253 3
        $errorMsg = $captcha['message'];
254 3
255 3
        return [
256
            'result' => $login,
257 3
            'message' => $errorMsg,
258 3
        ];
259 3
    }
260 3
261
    /**
262
     * Check Captcha.
263
     *
264
     * @param bool   $login    The login status that will be overwritten.
265
     * @param string $errorMsg The error message.
266
     *
267
     * @return array
268
     */
269
    private function checkCaptchaValidation(bool $login, string $errorMsg): array
270
    {
271 5
        foreach ($this->captcha as $captcha) {
272
            if (!$captcha->response()) {
273 5
                $login = false;
274 5
                $errorMsg = __('panel', 'login_message_invalid_captcha', 'Invalid Captcha code.');
275 2
            }
276 5
        }
277
278
        return [
279
            'result' => $login,
280 5
            'message' => $errorMsg,
281 5
        ];
282 5
    }
283
}
284