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

Security::checkAdmin()   C

Complexity

Conditions 14
Paths 16

Size

Total Lines 37
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 28.1816

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 14
eloc 25
c 2
b 0
f 1
nc 16
nop 3
dl 0
loc 37
ccs 14
cts 24
cp 0.5833
crap 28.1816
rs 6.2666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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