Passed
Push — devel-3.0 ( 6664a5...af8b21 )
by Rubén
03:03
created

Init::getSessionLifeTime()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 4
nop 0
dl 0
loc 15
rs 9.6111
c 0
b 0
f 0
1
<?php
2
/**
3
 * sysPass
4
 *
5
 * @author    nuxsmin
6
 * @link      http://syspass.org
7
 * @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
8
 *
9
 * This file is part of sysPass.
10
 *
11
 * sysPass is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation, either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * sysPass is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 *  along with sysPass.  If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace SP\Modules\Web;
26
27
use Defuse\Crypto\Exception\CryptoException;
28
use Psr\Container\ContainerInterface;
29
use SP\Bootstrap;
30
use SP\Core\Context\ContextInterface;
31
use SP\Core\Context\SessionContext;
32
use SP\Core\Crypt\CryptSessionHandler;
33
use SP\Core\Crypt\Session as CryptSession;
34
use SP\Core\Crypt\UUIDCookie;
35
use SP\Core\Language;
36
use SP\Core\ModuleBase;
37
use SP\Core\UI\Theme;
38
use SP\DataModel\ItemPreset\SessionTimeout;
39
use SP\Http\Address;
40
use SP\Plugin\PluginManager;
41
use SP\Services\Crypt\SecureSessionService;
42
use SP\Services\ItemPreset\ItemPresetInterface;
43
use SP\Services\ItemPreset\ItemPresetService;
44
use SP\Services\Upgrade\UpgradeAppService;
45
use SP\Services\Upgrade\UpgradeDatabaseService;
46
use SP\Services\Upgrade\UpgradeUtil;
47
use SP\Services\UserProfile\UserProfileService;
48
use SP\Storage\Database\Database;
49
use SP\Storage\Database\DatabaseUtil;
50
use SP\Util\HttpUtil;
51
52
/**
53
 * Class Init
54
 *
55
 * @property  itemPresetService
56
 * @package SP\Modules\Web
57
 */
58
final class Init extends ModuleBase
59
{
60
    /**
61
     * List of controllers that don't need to perform fully initialization
62
     * like: install/database checks, session/event handlers initialization
63
     */
64
    const PARTIAL_INIT = ['resource', 'install', 'bootstrap', 'status', 'upgrade', 'error'];
65
66
    /**
67
     * @var SessionContext
68
     */
69
    private $context;
70
    /**
71
     * @var Theme
72
     */
73
    private $theme;
74
    /**
75
     * @var Language
76
     */
77
    private $language;
78
    /**
79
     * @var SecureSessionService
80
     */
81
    private $secureSessionService;
82
    /**
83
     * @var PluginManager
84
     */
85
    private $pluginManager;
86
    /**
87
     * @var ItemPresetService
88
     */
89
    private $itemPresetService;
90
    /**
91
     * @var bool
92
     */
93
    private $isIndex = false;
94
95
    /**
96
     * Init constructor.
97
     *
98
     * @param ContainerInterface $container
99
     */
100
    public function __construct(ContainerInterface $container)
101
    {
102
        parent::__construct($container);
103
104
        $this->context = $container->get(ContextInterface::class);
105
        $this->theme = $container->get(Theme::class);
106
        $this->language = $container->get(Language::class);
107
        $this->secureSessionService = $container->get(SecureSessionService::class);
108
        $this->pluginManager = $container->get(PluginManager::class);
109
        $this->itemPresetService = $container->get(ItemPresetService::class);
110
    }
111
112
    /**
113
     * Initialize Web App
114
     *
115
     * @param string $controller
116
     *
117
     * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
118
     * @throws \SP\Core\Exceptions\ConstraintException
119
     * @throws \SP\Core\Exceptions\InvalidClassException
120
     * @throws \SP\Core\Exceptions\QueryException
121
     * @throws \SP\Repositories\NoSuchItemException
122
     * @throws \Exception
123
     */
124
    public function initialize($controller)
125
    {
126
        logger(__METHOD__);
127
128
        $this->isIndex = $controller === 'index';
129
130
        // Iniciar la sesión de PHP
131
        $this->initSession($this->configData->isEncryptSession());
132
133
        // Volver a cargar la configuración si se recarga la página
134
        if ($this->request->checkReload() === false) {
135
            // Cargar la configuración
136
            $this->config->loadConfig($this->context);
137
138
            // Cargar el lenguaje
139
            $this->language->setLanguage();
140
141
            // Initialize theme
142
            $this->theme->initialize();
143
        } else {
144
            logger('Browser reload');
145
146
            $this->context->setAppStatus(SessionContext::APP_STATUS_RELOADED);
147
148
            // Cargar la configuración
149
            $this->config->loadConfig($this->context, true);
150
151
            // Restablecer el idioma
152
            $this->language->setLanguage(true);
153
154
            // Re-Initialize theme
155
            $this->theme->initialize(true);
156
        }
157
158
        // Comprobar si es necesario cambiar a HTTPS
159
        HttpUtil::checkHttps($this->configData, $this->request);
160
161
        if (in_array($controller, self::PARTIAL_INIT, true) === false) {
162
            // Checks if sysPass is installed
163
            if ($this->checkInstalled() === false) {
164
                $this->router->response()
165
                    ->redirect('index.php?r=install/index')
166
                    ->send();
167
            }
168
169
            // Checks if maintenance mode is turned on
170
            if ($this->checkMaintenanceMode($this->context)) {
171
                $this->router->response()
172
                    ->redirect('index.php?r=error/maintenanceError')
173
                    ->send();
174
            }
175
176
            // Checks if upgrade is needed
177
            if ($this->checkUpgrade()) {
178
                $this->config->generateUpgradeKey();
179
180
                $this->router->response()
181
                    ->redirect('index.php?r=upgrade/index')
182
                    ->send();
183
            }
184
185
            // Checks if the database is set up
186
            if (!DatabaseUtil::checkDatabaseExist($this->container->get(Database::class)->getDbHandler(), $this->configData->getDbName())) {
187
                $this->router->response()
188
                    ->redirect('index.php?r=error/databaseError')
189
                    ->send();
190
            }
191
192
            // Initialize event handlers
193
            $this->initEventHandlers();
194
195
            // Initialize user session context
196
            $this->initUserSession();
197
198
            // Load plugins
199
            $this->pluginManager->loadPlugins();
200
201
            if ($this->context->isLoggedIn() && $this->context->getAppStatus() === SessionContext::APP_STATUS_RELOADED) {
0 ignored issues
show
introduced by
The condition $this->context->getAppSt...xt::APP_STATUS_RELOADED is always false.
Loading history...
202
                logger('Reload user profile');
203
                // Recargar los permisos del perfil de usuario
204
                $this->context->setUserProfile($this->container->get(UserProfileService::class)->getById($this->context->getUserData()->getUserProfileId())->getProfile());
205
            }
206
207
            return;
208
        }
209
210
        // Do not keep the PHP's session opened
211
        SessionContext::close();
212
    }
213
214
    /**
215
     * Iniciar la sesión PHP
216
     *
217
     * @param bool $encrypt Encriptar la sesión de PHP
218
     *
219
     * @throws \Exception
220
     */
221
    private function initSession($encrypt = false)
222
    {
223
        if ($encrypt === true
224
            && Bootstrap::$checkPhpVersion
225
            && ($key = $this->secureSessionService->getKey(UUIDCookie::factory($this->request))) !== false) {
226
            session_set_save_handler(new CryptSessionHandler($key), true);
227
        }
228
229
230
        try {
231
            $this->context->initialize();
232
        } catch (\Exception $e) {
233
            $this->router->response()->header('HTTP/1.1', '500 Internal Server Error');
234
235
            throw $e;
236
        }
237
    }
238
239
    /**
240
     * Comprueba que la aplicación esté instalada
241
     * Esta función comprueba si la aplicación está instalada. Si no lo está, redirige al instalador.
242
     */
243
    private function checkInstalled()
244
    {
245
        return $this->configData->isInstalled()
246
            && $this->router->request()->param('r') !== 'install/index';
247
    }
248
249
    /**
250
     * Comprobar si es necesario actualizar componentes
251
     */
252
    private function checkUpgrade()
253
    {
254
        return $this->configData->getUpgradeKey()
255
            || (UpgradeDatabaseService::needsUpgrade($this->configData->getDatabaseVersion()) ||
256
                UpgradeAppService::needsUpgrade(UpgradeUtil::fixVersionNumber($this->configData->getConfigVersion())));
257
    }
258
259
    /**
260
     * Inicializar la sesión de usuario
261
     *
262
     */
263
    private function initUserSession()
264
    {
265
        $lastActivity = $this->context->getLastActivity();
266
        $inMaintenance = $this->configData->isMaintenance();
267
268
        // Timeout de sesión
269
        if ($lastActivity > 0
270
            && !$inMaintenance
271
            && time() > ($lastActivity + $this->getSessionLifeTime())
272
        ) {
273
            if ($this->router->request()->cookies()->get(session_name()) !== null) {
274
                $this->router->response()->cookie(session_name(), '', time() - 42000);
275
            }
276
277
            SessionContext::restart();
278
        } else {
279
            $sidStartTime = $this->context->getSidStartTime();
280
281
            // Regenerar el Id de sesión periódicamente para evitar fijación
282
            if ($sidStartTime === 0) {
283
                // Intentar establecer el tiempo de vida de la sesión en PHP
284
                @ini_set('session.gc_maxlifetime', $this->getSessionLifeTime());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

284
                /** @scrutinizer ignore-unhandled */ @ini_set('session.gc_maxlifetime', $this->getSessionLifeTime());

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
285
286
                $this->context->setSidStartTime(time());
287
                $this->context->setStartActivity(time());
288
            } else if (!$inMaintenance
289
                && time() > ($sidStartTime + SessionContext::MAX_SID_TIME)
290
                && $this->context->isLoggedIn()
291
            ) {
292
                try {
293
                    CryptSession::reKey($this->context);
294
                } catch (CryptoException $e) {
295
                    logger($e->getMessage());
296
297
                    SessionContext::restart();
298
                    return;
299
                }
300
            }
301
302
            $this->context->setLastActivity(time());
303
        }
304
    }
305
306
    /**
307
     * Obtener el timeout de sesión desde la configuración.
308
     *
309
     * @return int con el tiempo en segundos
310
     */
311
    private function getSessionLifeTime()
312
    {
313
        $timeout = $this->context->getSessionTimeout();
314
315
        try {
316
            if ($this->isIndex || $timeout === null) {
317
                $userTimeout = $this->getSessionTimeoutForUser($timeout) ?: $this->configData->getSessionTimeout();
318
319
                return $this->context->setSessionTimeout($userTimeout);
320
            }
321
        } catch (\Exception $e) {
322
            processException($e);
323
        }
324
325
        return $timeout;
326
    }
327
328
    /**
329
     * @param int $default
330
     *
331
     * @return int
332
     * @throws \SP\Core\Exceptions\ConstraintException
333
     * @throws \SP\Core\Exceptions\InvalidArgumentException
334
     * @throws \SP\Core\Exceptions\NoSuchPropertyException
335
     * @throws \SP\Core\Exceptions\QueryException
336
     */
337
    private function getSessionTimeoutForUser(int $default = null)
338
    {
339
        if ($this->context->isLoggedIn()) {
340
            $itemPreset = $this->itemPresetService->getForCurrentUser(ItemPresetInterface::ITEM_TYPE_SESSION_TIMEOUT);
341
342
            if ($itemPreset !== null) {
343
                $sessionTimeout = $itemPreset->hydrate(SessionTimeout::class);
344
345
                if (Address::check($this->request->getClientAddress(), $sessionTimeout->getAddress(), $sessionTimeout->getMask())) {
0 ignored issues
show
Bug introduced by
It seems like $this->request->getClientAddress() can also be of type array; however, parameter $address of SP\Http\Address::check() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

345
                if (Address::check(/** @scrutinizer ignore-type */ $this->request->getClientAddress(), $sessionTimeout->getAddress(), $sessionTimeout->getMask())) {
Loading history...
346
                    return $sessionTimeout->getTimeout();
347
                }
348
            }
349
        }
350
351
        return $default;
352
    }
353
}