Passed
Push — master ( 8dcff2...10a865 )
by Fran
03:32
created

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