Test Failed
Push — master ( e4b2c6...b343ab )
by Fran
02:49
created

Security::getFlashes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 2
rs 10
1
<?php
2
namespace PSFS\base;
3
4
use PSFS\base\exception\ConfigException;
5
use PSFS\base\types\helpers\ResponseHelper;
6
use PSFS\base\types\traits\SecureTrait;
7
use PSFS\base\types\traits\SingletonTrait;
8
use PSFS\base\types\traits\TestTrait;
9
10
/**
11
 * Class Security
12
 * @package PSFS
13
 */
14
class Security
15
{
16
    // sha1('user')
17
    const USER_ID_TOKEN = '12dea96fec20593566ab75692c9949596833adc9';
18
    // sha1('admin')
19
    const MANAGER_ID_TOKEN = 'd033e22ae348aeb5660fc2140aec35850c4da997';
20
    // sha1('superadmin')
21
    const ADMIN_ID_TOKEN = '889a3a791b3875cfae413574b53da4bb8a90d53e';
22
    // sha1('FLASHES')
23
    const FLASH_MESSAGE_TOKEN = '4680c68435db1bfbf17c3fcc4f7b39d2c6122504';
24
    const LOGGED_USER_TOKEN = '__U_T_L__';
25
26
    use SecureTrait;
27
    use SingletonTrait;
28
    use TestTrait;
29
    /**
30
     * @var array $user
31
     */
32
    private $user = null;
33
34
    /**
35
     * @var array $admin
36
     */
37
    private $admin = null;
38
39
    /**
40
     * @var bool $authorized
41
     */
42
    private $authorized = FALSE;
43
44
    /**
45
     * @var bool $checked
46
     */
47
    private $checked = false;
48
49
    /**
50
     * @var array $session
51
     */
52
    protected $session;
53
54
    /**
55
     * Constructor por defecto
56
     */
57 2
    public function init()
58
    {
59 2
        $this->initSession();
60 2
        $this->session = null === $_SESSION ? array() : $_SESSION;
61 2
        if (NULL === $this->getSessionKey('__FLASH_CLEAR__')) {
62 2
            $this->clearFlashes();
63 2
            $this->setSessionKey('__FLASH_CLEAR__', microtime(TRUE));
64
        }
65 2
        $this->user = array_key_exists(self::USER_ID_TOKEN, $this->session) ? unserialize($this->session[self::USER_ID_TOKEN]) : NULL;
66 2
        $this->admin = array_key_exists(self::ADMIN_ID_TOKEN, $this->session) ? unserialize($this->session[self::ADMIN_ID_TOKEN]) : NULL;
67 2
        if (null === $this->admin) {
68 2
            $this->checkAdmin();
69
        }
70 2
        $this->setLoaded(true);
71 2
    }
72
73 2
    private function initSession() {
74 2
        if (PHP_SESSION_NONE === session_status() && !headers_sent()) {
75
            session_start();
76
        }
77
        // Fix for phpunits
78 2
        if(!isset($_SESSION)) {
79 1
            $_SESSION = [];
80
        }
81 2
    }
82
83
    /**
84
     * @return array
85
     */
86 2
    public static function getProfiles()
87
    {
88
        return array(
89 2
            self::ADMIN_ID_TOKEN => t('Administrador'),
90 2
            self::MANAGER_ID_TOKEN => t('Gestor'),
91 2
            self::USER_ID_TOKEN => t('Usuario'),
92
        );
93
    }
94
95
    /**
96
     * @return array
97
     */
98 1
    public function getAdminProfiles()
99
    {
100 1
        return static::getProfiles();
101
    }
102
103
    /**
104
     * @return array
105
     */
106 3
    public static function getCleanProfiles()
107
    {
108
        return array(
109 3
            '__SUPER_ADMIN__' => self::ADMIN_ID_TOKEN,
110 3
            '__ADMIN__' => self::MANAGER_ID_TOKEN,
111 3
            '__USER__' => self::USER_ID_TOKEN,
112
        );
113
    }
114
115
    /**
116
     * @return array
117
     */
118 1
    public function getAdminCleanProfiles()
119
    {
120 1
        return static::getCleanProfiles();
121
    }
122
123
    /**
124
     * @param mixed $user
125
     * @return bool
126
     * @throws exception\GeneratorException
127
     * @throws ConfigException
128
     */
129 1
    public static function save($user)
130
    {
131 1
        $saved = true;
132 1
        $admins = Cache::getInstance()->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . 'admins.json', Cache::JSONGZ, true) ?: [];
133 1
        $admins[$user['username']]['hash'] = sha1($user['username'] . $user['password']);
134 1
        $admins[$user['username']]['profile'] = $user['profile'];
135
136 1
        Cache::getInstance()->storeData(CONFIG_DIR . DIRECTORY_SEPARATOR . 'admins.json', $admins, Cache::JSONGZ, true);
137 1
        return $saved;
138
    }
139
140
    /**
141
     * @param mixed $user
142
     * @return bool
143
     * @throws exception\GeneratorException
144
     */
145 1
    public function saveUser($user)
146
    {
147 1
        $saved = false;
148 1
        if (!empty($user)) {
149 1
            $saved = static::save($user);
150
        }
151 1
        return $saved;
152
    }
153
154
    /**
155
     * @param mixed $user
156
     */
157 1
    public function updateUser($user)
158
    {
159 1
        $this->user = $user;
160 1
    }
161
162
    /**
163
     * @param $alias
164
     * @param $profile
165
     */
166
    public function updateAdmin($alias, $profile) {
167
        $this->admin = array(
168
            'alias' => $alias,
169
            'profile' => $profile,
170
        );
171
        $this->setSessionKey(self::ADMIN_ID_TOKEN, serialize($this->admin));
172
    }
173
174
    /**
175
     * @return array|null
176
     */
177 4
    public function getAdmins()
178
    {
179 4
        return Cache::getInstance()->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . 'admins.json', Cache::JSONGZ, true);
180
    }
181
182
    /**
183
     * @param string $user
184
     * @param string $pass
185
     * @param boolean $force
186
     *
187
     * @return bool
188
     */
189 3
    public function checkAdmin($user = NULL, $pass = NULL, $force = false)
190
    {
191 3
        Logger::log('Checking admin session');
192 3
        if ((!$this->authorized && !$this->checked) || $force) {
193 3
            $admins = $this->getAdmins();
194 3
            if (null !== $admins) {
195 1
                $request = Request::getInstance();
196
                //Sacamos las credenciales de la petición
197 1
                $user = $user ?: $request->getServer('PHP_AUTH_USER');
198 1
                $pass = $pass ?: $request->getServer('PHP_AUTH_PW');
199 1
                if (NULL === $user || (array_key_exists($user, $admins) && empty($admins[$user]))) {
200
                    list($user, $pass) = $this->getAdminFromCookie();
201
                }
202 1
                if (!empty($user) && !empty($admins[$user])) {
203
                    $auth = $admins[$user]['hash'];
204
                    $this->authorized = ($auth === sha1($user . $pass));
205
                    if ($this->authorized) {
206
                        $this->updateAdmin($user , $admins[$user]['profile']);
207
                        ResponseHelper::setCookieHeaders([
208
                            [
209
                                'name' => $this->getHash(),
210
                                'value' => base64_encode("$user:$pass"),
211
                                'http' => true,
212
                                'domain' => '',
213
                            ]
214
                        ]);
215
                        $this->setSessionKey(self::LOGGED_USER_TOKEN, base64_encode("{$user}:{$pass}"));
216
                    }
217
                } else {
218 1
                    $this->admin = null;
219 1
                    $this->setSessionKey(self::ADMIN_ID_TOKEN, null);
220
                }
221 1
                $this->checked = true;
222
            }
223
        }
224
225 3
        return $this->authorized || self::isTest();
226
    }
227
228
    /**
229
     * Método que obtiene el usuario y contraseña de la cookie de sesión de administración
230
     * @return array
231
     */
232
    protected function getAdminFromCookie()
233
    {
234
        $authCookie = Request::getInstance()->getCookie($this->getHash());
235
        $user = $pass = array();
236
        if (!empty($authCookie)) {
237
            list($user, $pass) = explode(':', base64_decode($authCookie));
238
        }
239
240
        return array($user, $pass);
241
    }
242
243
    /**
244
     * Método privado para la generación del hash de la cookie de administración
245
     * @return string
246
     */
247
    public function getHash()
248
    {
249
        return substr(self::MANAGER_ID_TOKEN, 0, 8);
250
    }
251
252
    /**
253
     * Método que devuelve el usuario logado
254
     * @return array
255
     */
256 3
    public function getUser()
257
    {
258 3
        return $this->user;
259
    }
260
261
    /**
262
     * Método que devuelve el usuario administrador logado
263
     * @return array
264
     */
265 3
    public function getAdmin()
266
    {
267 3
        return $this->admin;
268
    }
269
270
    /**
271
     * Método que calcula si se está logado o para acceder a administración
272
     * @return bool
273
     */
274 4
    public function canAccessRestrictedAdmin()
275
    {
276 4
        return (null !== $this->admin && !preg_match('/^\/admin\/login/i', Request::requestUri())) || self::isTest();
277
    }
278
279
    /**
280
     * Servicio que devuelve una pantalla de error porque se necesita estar authenticado
281
     *
282
     * @param string|null $route
283
     *
284
     * @return string|null
285
     */
286
    public function notAuthorized($route)
287
    {
288
        return Template::getInstance()->render('notauthorized.html.twig', array(
289
            'route' => $route,
290
        ));
291
    }
292
293
    /**
294
     * @return bool
295
     */
296
    public function isSuperAdmin()
297
    {
298
        $users = $this->getAdmins();
299
        $logged = $this->getAdmin();
300
        if (is_array($logged)
301
            && array_key_exists('alias', $logged)
302
            && array_key_exists($logged['alias'], $users)) {
0 ignored issues
show
Bug introduced by
It seems like $users can also be of type null; however, parameter $search of array_key_exists() does only seem to accept array, 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

302
            && array_key_exists($logged['alias'], /** @scrutinizer ignore-type */ $users)) {
Loading history...
303
            $security = $users[$logged['alias']]['profile'];
304
            return self::ADMIN_ID_TOKEN === $security;
305
        }
306
307
        return FALSE;
308
    }
309
310
    /**
311
     *
312
     * @param string $key
313
     *
314
     * @return mixed
315
     */
316 11
    public function getSessionKey($key)
317
    {
318 11
        $data = NULL;
319 11
        if (array_key_exists($key, $this->session)) {
320 5
            $data = $this->session[$key];
321
        }
322
323 11
        return $data;
324
    }
325
326
    /**
327
     *
328
     * @param string $key
329
     * @param mixed $data
330
     *
331
     * @return Security
332
     */
333 6
    public function setSessionKey($key, $data = NULL)
334
    {
335 6
        $this->session[$key] = $data;
336
337 6
        return $this;
338
    }
339
340
    /**
341
     * @return mixed
342
     */
343 2
    public function getFlashes()
344
    {
345 2
        $flashes = $this->getSessionKey(self::FLASH_MESSAGE_TOKEN);
346
347 2
        return (NULL !== $flashes) ? $flashes : array();
348
    }
349
350
    /**
351
     * @return $this
352
     */
353 2
    public function clearFlashes()
354
    {
355 2
        $this->setSessionKey(self::FLASH_MESSAGE_TOKEN, NULL);
356
357 2
        return $this;
358
    }
359
360
    /**
361
     *
362
     * @param string $key
363
     * @param mixed $data
364
     */
365
    public function setFlash($key, $data = NULL)
366
    {
367
        $flashes = $this->getFlashes();
368
        if (!is_array($flashes)) {
369
            $flashes = [];
370
        }
371
        $flashes[$key] = $data;
372
        $this->setSessionKey(self::FLASH_MESSAGE_TOKEN, $flashes);
373
    }
374
375
    /**
376
     *
377
     * @param string $key
378
     *
379
     * @return mixed
380
     */
381 1
    public function getFlash($key)
382
    {
383 1
        $flashes = $this->getFlashes();
384
385 1
        return (NULL !== $key && array_key_exists($key, $flashes)) ? $flashes[$key] : NULL;
386
    }
387
388
    /**
389
     *
390
     * @param boolean $closeSession
391
     *
392
     * @return Security
393
     */
394
    public function updateSession($closeSession = FALSE)
395
    {
396
        Logger::log('Update session');
397
        $_SESSION = $this->session;
398
        $_SESSION[self::USER_ID_TOKEN] = serialize($this->user);
399
        $_SESSION[self::ADMIN_ID_TOKEN] = serialize($this->admin);
400
        if ($closeSession) {
401
            Logger::log('Close session');
402
            /** @scrutinizer ignore-unhandled */ @session_write_close();
0 ignored issues
show
Bug introduced by
Are you sure the usage of session_write_close() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
403
            /** @scrutinizer ignore-unhandled */ @session_start();
404
        }
405
        Logger::log('Session updated');
406
        return $this;
407
    }
408
409
    public function closeSession()
410
    {
411
        unset($_SESSION);
412
        /** @scrutinizer ignore-unhandled */ @session_destroy();
413
        /** @scrutinizer ignore-unhandled */ @session_regenerate_id(TRUE);
414
        /** @scrutinizer ignore-unhandled */ @session_start();
415
    }
416
417
}
418