Test Failed
Push — master ( d7aeee...22718f )
by Fran
09:31
created

Security::checkToken()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 3
dl 0
loc 4
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PSFS\base;
4
5
use PSFS\base\types\helpers\SecurityHelper;
6
use PSFS\base\types\SingletonTrait;
7
8
9
/**
10
 * Class Security
11
 * @package PSFS
12
 */
13
class Security
14
{
15
    // sha1('user')
16
    const USER_ID_TOKEN = '12dea96fec20593566ab75692c9949596833adc9';
17
    // sha1('admin')
18
    const MANAGER_ID_TOKEN = 'd033e22ae348aeb5660fc2140aec35850c4da997';
19
    // sha1('superadmin')
20
    const ADMIN_ID_TOKEN = '889a3a791b3875cfae413574b53da4bb8a90d53e';
21
    // sha1('FLASHES')
22
    const FLASH_MESSAGE_TOKEN = '4680c68435db1bfbf17c3fcc4f7b39d2c6122504';
23
24
    use SingletonTrait;
25
    /**
26
     * @var array $user
27
     */
28
    private $user = null;
29
30
    /**
31
     * @var array $admin
32
     */
33
    private $admin = null;
34
35
    /**
36
     * @var bool $authorized
37
     */
38
    private $authorized = FALSE;
39
40
    /**
41
     * @var bool $checked
42
     */
43
    private $checked = false;
44
45
    /**
46
     * @var array $session
47
     */
48
    protected $session;
49
50
    /**
51
     * Constructor por defecto
52
     */
53 3
    public function __construct()
54
    {
55 3
        if (PHP_SESSION_NONE === session_status()) {
56 1
            session_start();
57
        }
58 2
        $this->session = (is_null($_SESSION)) ? array() : $_SESSION;
59 2
        if (NULL === $this->getSessionKey('__FLASH_CLEAR__')) {
60 2
            $this->clearFlashes();
61 2
            $this->setSessionKey('__FLASH_CLEAR__', microtime(TRUE));
62
        }
63 2
        $this->user = (array_key_exists(self::USER_ID_TOKEN, $this->session)) ? unserialize($this->session[self::USER_ID_TOKEN]) : NULL;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
64 2
        $this->admin = (array_key_exists(self::ADMIN_ID_TOKEN, $this->session)) ? unserialize($this->session[self::ADMIN_ID_TOKEN]) : NULL;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 139 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
65 2
        if (null === $this->admin) {
66 2
            $this->checkAdmin();
67
        }
68 2
    }
69
70
    /**
71
     * Método estático que devuelve los perfiles de la plataforma
72
     * @return array
73
     */
74 1
    public static function getProfiles()
75
    {
76
        return array(
77 1
            self::ADMIN_ID_TOKEN => _('Administrador'),
78 1
            self::MANAGER_ID_TOKEN => _('Gestor'),
79 1
            self::USER_ID_TOKEN => _('Usuario'),
80
        );
81
    }
82
83
    /**
84
     * Method that returns all the available profiles
85
     * @return array
86
     */
87
    public function getAdminProfiles()
88
    {
89
        return static::getProfiles();
90
    }
91
92
    /**
93
     * Método estático que devuelve los perfiles disponibles
94
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
95
     */
96
    public static function getCleanProfiles()
97
    {
98
        return array(
99
            '__SUPER_ADMIN__' => self::ADMIN_ID_TOKEN,
100
            '__ADMIN__' => self::MANAGER_ID_TOKEN,
101
            '__USER__' => self::USER_ID_TOKEN,
102
        );
103
    }
104
105
    /**
106
     * Método estático que devuelve los perfiles disponibles
107
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
108
     */
109
    public function getAdminCleanProfiles()
110
    {
111
        return static::getCleanProfiles();
112
    }
113
114
    /**
115
     * Método que guarda los administradores
116
     *
117
     * @param array $user
118
     *
119
     * @return bool
120
     */
121
    public static function save($user)
122
    {
123
        $saved = true;
124
        try {
125
            $admins = Cache::getInstance()->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . 'admins.json', Cache::JSONGZ, true) ?: [];
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 137 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
126
            $admins[$user['username']]['hash'] = sha1($user['username'] . $user['password']);
127
            $admins[$user['username']]['profile'] = $user['profile'];
128
129
            Cache::getInstance()->storeData(CONFIG_DIR . DIRECTORY_SEPARATOR . 'admins.json', $admins, Cache::JSONGZ, true);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 124 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
130
        } catch(\Exception $e) {
131
            Logger::log($e->getMessage(), LOG_ERR);
132
            $saved = false;
133
        }
134
        return $saved;
135
    }
136
137
    /**
138
     * Method to save a new admin user
139
     * @param array $user
140
     * @return bool
141
     */
142
    public function saveUser($user)
143
    {
144
        $saved = false;
145
        if (!empty($user)) {
146
            $saved = static::save($user);
147
        }
148
        return $saved;
149
    }
150
151
    /**
152
     * Servicio que actualiza los datos del usuario
153
     *
154
     * @param $user
155
     */
156
    public function updateUser($user)
157
    {
158
        $this->user = $user;
159
    }
160
161
    /**
162
     * Método que devuelve los administradores de una plataforma
163
     * @return array|null
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
164
     */
165 2
    public function getAdmins()
166
    {
167 2
        return Cache::getInstance()->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . 'admins.json', Cache::JSONGZ, true);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 124 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
168
    }
169
170
    /**
171
     * Método que devuelve si un usuario tiene privilegios para acceder a la zona de administración
172
     *
173
     * @param null $user
174
     * @param null $pass
175
     * @param boolean $force
176
     *
177
     * @return bool
178
     * @throws \HttpException
179 1
     */
180
    public function checkAdmin($user = NULL, $pass = NULL, $force = false)
181 1
    {
182 1
        Logger::log('Checking admin session');
183 1
        if ((!$this->authorized && !$this->checked) || $force) {
184 1
            $admins = $this->getAdmins();
185
            if (null !== $admins) {
186
                $request = Request::getInstance();
187
                //Sacamos las credenciales de la petición
188
                $user = $user ?: $request->getServer('PHP_AUTH_USER');
189
                $pass = $pass ?: $request->getServer('PHP_AUTH_PW');
190
                if (NULL === $user || (array_key_exists($user, $admins) && empty($admins[$user]))) {
191
                    list($user, $pass) = $this->getAdminFromCookie();
192
                }
193
                if (!empty($user) && !empty($admins[$user])) {
194
                    $auth = $admins[$user]['hash'];
195
                    $this->authorized = ($auth == sha1($user . $pass));
196
                    if ($this->authorized) {
197
                        $this->admin = array(
198
                            'alias' => $user,
199
                            'profile' => $admins[$user]['profile'],
200
                        );
201
                        $this->setSessionKey(self::ADMIN_ID_TOKEN, serialize($this->admin));
202
                    }
203
                }
204 1
                $this->checked = true;
205
            }
206
        }
207 1
208
        return $this->authorized;
209
    }
210
211
    /**
212
     * Método que obtiene el usuario y contraseña de la cookie de sesión de administración
213
     * @return array
214
     */
215
    protected function getAdminFromCookie()
216
    {
217
        $auth_cookie = Request::getInstance()->getCookie($this->getHash());
218
        $user = $pass = array();
219
        if (!empty($auth_cookie)) {
220
            list($user, $pass) = explode(':', base64_decode($auth_cookie));
221
        }
222
223
        return array($user, $pass);
224
    }
225
226
    /**
227
     * Método privado para la generación del hash de la cookie de administración
228
     * @return string
229
     */
230
    public function getHash()
231
    {
232
        return substr(md5('admin'), 0, 8);
233
    }
234
235
    /**
236
     * Método que devuelve el usuario logado
237
     * @return array
238
     */
239
    public function getUser()
240
    {
241
        return $this->user;
242
    }
243
244
    /**
245
     * Método que devuelve el usuario administrador logado
246
     * @return array
247
     */
248
    public function getAdmin()
249
    {
250
        return $this->admin;
251
    }
252
253
    /**
254
     * Método que calcula si se está logado o para acceder a administración
255
     * @return bool
256 1
     */
257
    public function canAccessRestrictedAdmin()
258 1
    {
259
        return null !== $this->admin && !preg_match('/^\/admin\/login/i', Request::requestUri());
260
    }
261
262
    /**
263
     * Servicio que devuelve una pantalla de error porque se necesita estar authenticado
264
     *
265
     * @param string|null $route
266
     *
267
     * @return string|null
268
     */
269
    public function notAuthorized($route)
270
    {
271
        return Template::getInstance()->render('notauthorized.html.twig', array(
272
            'route' => $route,
273
        ));
274
    }
275
276
    /**
277
     * Servicio que chequea si un usuario es super administrador o no
278
     * @return bool
279
     */
280
    public function isSuperAdmin()
281
    {
282
        $users = $this->getAdmins();
283
        $logged = $this->getAdminFromCookie();
284
        if ($users[$logged[0]]) {
285
            $security = $users[$logged[0]]['profile'];
286
287
            return self::ADMIN_ID_TOKEN === $security;
288
        }
289
290
        return FALSE;
291
    }
292
293
    /**
294
     * Servicio que devuelve un dato de sesión
295
     *
296
     * @param string $key
297
     *
298
     * @return mixed
299 2
     */
300
    public function getSessionKey($key)
301 2
    {
302 2
        $data = NULL;
303 1
        if (array_key_exists($key, $this->session)) {
304
            $data = $this->session[$key];
305
        }
306 2
307
        return $data;
308
    }
309
310
    /**
311
     * Servicio que setea una variable de sesión
312
     *
313
     * @param string $key
314
     * @param mixed $data
315
     *
316
     * @return Security
317 2
     */
318
    public function setSessionKey($key, $data = NULL)
319 2
    {
320
        $this->session[$key] = $data;
321 2
322
        return $this;
323
    }
324
325
    /**
326
     * Servicio que devuelve los mensajes flash de sesiones
327
     * @return mixed
328
     */
329
    public function getFlashes()
330
    {
331
        $flashes = $this->getSessionKey(self::FLASH_MESSAGE_TOKEN);
332
333
        return (NULL !== $flashes) ? $flashes : array();
334
    }
335
336
    /**
337
     * Servicio que limpia los mensajes flash
338
     * @return $this
339 1
     */
340
    public function clearFlashes()
341 1
    {
342
        $this->setSessionKey(self::FLASH_MESSAGE_TOKEN, NULL);
343 1
344
        return $this;
345
    }
346
347
    /**
348
     * Servicio que inserta un flash en sesión
349
     *
350
     * @param string $key
351
     * @param mixed $data
352
     */
353
    public function setFlash($key, $data = NULL)
354
    {
355
        $flashes = $this->getFlashes();
356
        if (!is_array($flashes)) {
357
            $flashes = array();
358
        }
359
        $flashes[$key] = $data;
360
        $this->setSessionKey(self::FLASH_MESSAGE_TOKEN, $flashes);
361
    }
362
363
    /**
364
     * Servicio que devuelve un flash de sesión
365
     *
366
     * @param string $key
367
     *
368
     * @return mixed
369
     */
370
    public function getFlash($key)
371
    {
372
        $flashes = $this->getFlashes();
373
374
        return (NULL !== $key && array_key_exists($key, $flashes)) ? $flashes[$key] : NULL;
375
    }
376
377
    /**
378
     * Servicio que actualiza
379
     *
380
     * @param boolean $closeSession
381
     *
382
     * @return Security
383
     */
384
    public function updateSession($closeSession = FALSE)
385
    {
386
        Logger::log('Update session');
387
        $_SESSION = $this->session;
388
        $_SESSION[self::USER_ID_TOKEN] = serialize($this->user);
389
        $_SESSION[self::ADMIN_ID_TOKEN] = serialize($this->admin);
390
        if ($closeSession) {
391
            Logger::log('Close session');
392
            session_write_close();
393
            session_start();
394
        }
395
        Logger::log('Session updated');
396
        return $this;
397
    }
398
399
    /**
400
     * Servicio que limpia la sesión
401
     */
402
    public function closeSession()
403
    {
404
        session_destroy();
405
        session_regenerate_id(TRUE);
406
        session_start();
407
    }
408
409
}
410