Passed
Push — master ( 02345f...0e807a )
by Fran
05:53 queued 14s
created

Security::__construct()   B

Complexity

Conditions 7
Paths 64

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 7.0145

Importance

Changes 0
Metric Value
cc 7
eloc 11
nc 64
nop 0
dl 0
loc 16
ccs 14
cts 15
cp 0.9333
crap 7.0145
rs 8.2222
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 2
    public function __construct()
54
    {
55 2
        if (PHP_SESSION_NONE === session_status()) {
56 1
            session_start();
57
        }
58 1
        $this->session = (is_null($_SESSION)) ? array() : $_SESSION;
59 1
        if (NULL === $this->getSessionKey('__FLASH_CLEAR__')) {
60 1
            $this->clearFlashes();
61 1
            $this->setSessionKey('__FLASH_CLEAR__', microtime(TRUE));
62 1
        }
63 1
        $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 1
        $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 1
        if (null === $this->admin) {
66 1
            $this->checkAdmin();
67 1
        }
68 1
    }
69
70
    /**
71
     * Método estático que devuelve los perfiles de la plataforma
72
     * @return array
73
     */
74 2
    public static function getProfiles()
75
    {
76
        return array(
77 2
            self::ADMIN_ID_TOKEN => _('Administrador'),
78 2
            self::MANAGER_ID_TOKEN => _('Gestor'),
79 2
            self::USER_ID_TOKEN => _('Usuario'),
80 2
        );
81
    }
82
83
    /**
84
     * Method that returns all the available profiles
85
     * @return array
86
     */
87 1
    public function getAdminProfiles()
88
    {
89 1
        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 1
    public static function getCleanProfiles()
97
    {
98
        return array(
99 1
            '__SUPER_ADMIN__' => self::ADMIN_ID_TOKEN,
100 1
            '__ADMIN__' => self::MANAGER_ID_TOKEN,
101 1
            '__USER__' => self::USER_ID_TOKEN,
102 1
        );
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 1
    public function getAdminCleanProfiles()
110
    {
111 1
        return static::getCleanProfiles();
112
    }
113
114
    /**
115
     * Método que guarda los administradores
116
     *
117
     * @param array $user
118
     *
119
     * @return bool
120
     */
121 1
    public static function save($user)
122
    {
123 1
        $saved = true;
124
        try {
125 1
            $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 1
            $admins[$user['username']]['hash'] = sha1($user['username'] . $user['password']);
127 1
            $admins[$user['username']]['profile'] = $user['profile'];
128
129 1
            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 1
        } catch(\Exception $e) {
131
            Logger::log($e->getMessage(), LOG_ERR);
132
            $saved = false;
133
        }
134 1
        return $saved;
135
    }
136
137
    /**
138
     * Method to save a new admin user
139
     * @param array $user
140
     * @return bool
141
     */
142 1
    public function saveUser($user)
143
    {
144 1
        $saved = false;
145 1
        if (!empty($user)) {
146 1
            $saved = static::save($user);
147 1
        }
148 1
        return $saved;
149
    }
150
151
    /**
152
     * Servicio que actualiza los datos del usuario
153
     *
154
     * @param $user
155
     */
156 1
    public function updateUser($user)
157
    {
158 1
        $this->user = $user;
159 1
    }
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 3
    public function getAdmins()
166
    {
167 3
        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
     */
180 2
    public function checkAdmin($user = NULL, $pass = NULL, $force = false)
181
    {
182 2
        Logger::log('Checking admin session');
183 2
        if ((!$this->authorized && !$this->checked) || $force) {
184 2
            $admins = $this->getAdmins();
185 2
            if (null !== $admins) {
186 1
                $request = Request::getInstance();
187
                //Sacamos las credenciales de la petición
188 1
                $user = $user ?: $request->getServer('PHP_AUTH_USER');
189 1
                $pass = $pass ?: $request->getServer('PHP_AUTH_PW');
190 1
                if (NULL === $user || (array_key_exists($user, $admins) && empty($admins[$user]))) {
191
                    list($user, $pass) = $this->getAdminFromCookie();
192
                }
193 1
                if (!empty($user) && !empty($admins[$user])) {
194 1
                    $auth = $admins[$user]['hash'];
195 1
                    $this->authorized = ($auth == sha1($user . $pass));
196 1
                    if ($this->authorized) {
197 1
                        $this->admin = array(
198 1
                            'alias' => $user,
199 1
                            'profile' => $admins[$user]['profile'],
200
                        );
201 1
                        $this->setSessionKey(self::ADMIN_ID_TOKEN, serialize($this->admin));
202 1
                    }
203 1
                }
204 1
                $this->checked = true;
205 1
            }
206 2
        }
207
208 2
        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 1
    public function getUser()
240
    {
241 1
        return $this->user;
242
    }
243
244
    /**
245
     * Método que devuelve el usuario administrador logado
246
     * @return array
247
     */
248 1
    public function getAdmin()
249
    {
250 1
        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
     */
257 1
    public function canAccessRestrictedAdmin()
258
    {
259 1
        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
     */
300 2
    public function getSessionKey($key)
301
    {
302 2
        $data = NULL;
303 2
        if (array_key_exists($key, $this->session)) {
304 1
            $data = $this->session[$key];
305 1
        }
306
307 2
        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
     */
318 3
    public function setSessionKey($key, $data = NULL)
319
    {
320 3
        $this->session[$key] = $data;
321
322 3
        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
     */
340 1
    public function clearFlashes()
341
    {
342 1
        $this->setSessionKey(self::FLASH_MESSAGE_TOKEN, NULL);
343
344 1
        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