Passed
Branch develop (9d5aa6)
by Nikolay
04:57
created

SessionController::changeLanguageAction()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 11
rs 10
cc 3
nc 3
nop 0
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright © 2017-2023 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\AdminCabinet\Controllers;
21
22
use MikoPBX\AdminCabinet\Forms\LoginForm;
23
use MikoPBX\Common\Models\AuthTokens;
24
use MikoPBX\Common\Models\PbxSettings;
25
use MikoPBX\Common\Providers\AclProvider;
26
use MikoPBX\Common\Providers\PBXConfModulesProvider;
27
use MikoPBX\Modules\Config\WebUIConfigInterface;
28
29
/**
30
 * SessionController
31
 *
32
 * Allows to authenticate users
33
 */
34
class SessionController extends BaseController
35
{
36
    public const SESSION_ID = 'authAdminCabinet';
37
38
    public const ROLE = 'role';
39
40
    public const HOME_PAGE = 'homePage';
41
42
43
    /**
44
     * Renders the login page with form and settings values.
45
     */
46
    public function indexAction(): void
47
    {
48
        $this->view->NameFromSettings
49
            = PbxSettings::getValueByKey('Name');
50
        $this->view->DescriptionFromSettings
51
            = PbxSettings::getValueByKey('Description');
52
        $this->view->form = new LoginForm();
53
    }
54
55
    /**
56
     * Handles the login form submission and authentication.
57
     */
58
    public function startAction(): void
59
    {
60
        if (!$this->request->isPost()) {
61
            $this->forward('session/index');
62
        }
63
        $loginFromUser = $this->request->getPost('login');
64
        $passFromUser = $this->request->getPost('password');
65
        $this->flash->clear();
66
        $login = PbxSettings::getValueByKey('WebAdminLogin');
67
        $passwordHash = PbxSettings::getValueByKey('WebAdminPassword');
68
69
        $userLoggedIn = false;
70
        $sessionParams = [];
71
72
        // Check if the provided login and password match the stored values
73
        if ($login === $loginFromUser
74
            && ($this->security->checkHash($passFromUser, $passwordHash) || $passwordHash === $passFromUser))
75
            {
76
            $sessionParams = [
77
                SessionController::ROLE => AclProvider::ROLE_ADMINS,
78
                SessionController::HOME_PAGE => $this->url->get('extensions/index')
79
            ];
80
            $userLoggedIn = true;
81
        } else {
82
            // Try to authenticate user over additional module
83
            $additionalModules = PBXConfModulesProvider::hookModulesMethod(WebUIConfigInterface::AUTHENTICATE_USER, [$loginFromUser, $passFromUser]);
84
85
            // Check if any additional module successfully authenticated the user
86
            foreach ($additionalModules as $moduleUniqueId => $sessionData) {
87
                if (!empty($sessionData)) {
88
                    $this->loggerAuth->info("User $loginFromUser was authenticated over module $moduleUniqueId");
89
                    $sessionParams = $sessionData;
90
                    $userLoggedIn = true;
91
                    break;
92
                }
93
            }
94
        }
95
96
        if ($userLoggedIn) {
97
            // Register the session with the specified parameters
98
            $this->_registerSession($sessionParams);
99
            if ($this->session->has(LanguageController::WEB_ADMIN_LANGUAGE)){
100
                LanguageController::updateSystemLanguage($this->session->get(LanguageController::WEB_ADMIN_LANGUAGE));
101
            }
102
            $this->view->success = true;
103
            $backUri = $this->request->getPost('backUri');
104
            if (!empty($backUri)) {
105
                $this->view->reload = $backUri;
106
            } else {
107
                $this->view->reload = $this->session->get(SessionController::HOME_PAGE);
108
            }
109
        } else {
110
            // Authentication failed
111
            $this->view->success = false;
112
            $this->flash->error($this->translation->_('auth_WrongLoginPassword'));
113
            $remoteAddress = $this->request->getClientAddress(true);
114
            $userAgent = $this->request->getUserAgent();
115
            $this->loggerAuth->warning("From: {$remoteAddress} UserAgent:{$userAgent} Cause: Wrong password");
116
            $this->clearAuthCookies();
117
        }
118
119
    }
120
121
    /**
122
     * Register an authenticated user into session data
123
     *
124
     */
125
    private function _registerSession(array $sessionParams): void
126
    {
127
        $this->session->set(self::SESSION_ID, $sessionParams);
128
129
        if ($this->request->getPost('rememberMeCheckBox') === 'on') {
130
            $this->updateRememberMeCookies($sessionParams);
131
        } else {
132
            $this->clearAuthCookies();
133
        }
134
    }
135
136
    /**
137
     * Setups random password and selector to browser cookie storage to remember me facility
138
     *
139
     * @param array $sessionParams
140
     */
141
    private function updateRememberMeCookies(array $sessionParams): void
142
    {
143
        $cookieExpirationTime = time() + (30 * 24 * 60 * 60);  // for 1 month
144
145
        $randomPassword = $this->security->getSaltBytes(32);
146
        $this->cookies->set("random_token", $randomPassword, $cookieExpirationTime);
147
148
        $randomPasswordHash = $this->security->hash($randomPassword);
149
150
        $expiryDate = date("Y-m-d H:i:s", $cookieExpirationTime);
151
152
        // Get token for username
153
        $parameters = [
154
            'conditions' => 'tokenHash = :tokenHash:',
155
            'binds' => [
156
                'tokenHash' => $randomPasswordHash,
157
            ],
158
        ];
159
        $userToken = AuthTokens::findFirst($parameters);
160
        if ($userToken === null) {
161
            $userToken = new AuthTokens();
162
        }
163
        // Insert new token
164
        $userToken->tokenHash = $randomPasswordHash;
165
        $userToken->expiryDate = $expiryDate;
166
        $userToken->sessionParams = json_encode($sessionParams);
167
        $userToken->save();
168
    }
169
170
    /**
171
     * Clears remember me cookies
172
     */
173
    private function clearAuthCookies(): void
174
    {
175
        if ($this->cookies->has('random_token')) {
176
            $cookie = $this->cookies->get('random_token');
177
            $value = $cookie->getValue();
178
            $userTokens = AuthTokens::find();
179
            foreach ($userTokens as $userToken) {
180
                if ($this->security->checkHash($value, $userToken->tokenHash)) {
181
                    $userToken->delete();
182
                }
183
            }
184
            $cookie->delete();
185
        }
186
    }
187
188
    /**
189
     * Finishes the active session redirecting to the index
190
     *
191
     */
192
    public function endAction(): void
193
    {
194
        $this->session->remove(self::SESSION_ID);
195
        $this->session->destroy();
196
        $this->clearAuthCookies();
197
    }
198
}
199