Passed
Push — master ( 510786...275623 )
by Fran
04:26
created

Security::isSuperAdmin()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.0116

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 0
dl 0
loc 12
ccs 6
cts 7
cp 0.8571
crap 2.0116
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace PSFS\base;
3
4
use PSFS\base\types\traits\SingletonTrait;
5
6
/**
7
 * Class Security
8
 * @package PSFS
9
 */
10
class Security
11
{
12
    // sha1('user')
13
    const USER_ID_TOKEN = '12dea96fec20593566ab75692c9949596833adc9';
14
    // sha1('admin')
15
    const MANAGER_ID_TOKEN = 'd033e22ae348aeb5660fc2140aec35850c4da997';
16
    // sha1('superadmin')
17
    const ADMIN_ID_TOKEN = '889a3a791b3875cfae413574b53da4bb8a90d53e';
18
    // sha1('FLASHES')
19
    const FLASH_MESSAGE_TOKEN = '4680c68435db1bfbf17c3fcc4f7b39d2c6122504';
20
21
    use SingletonTrait;
22
    /**
23
     * @var array $user
24
     */
25
    private $user = null;
26
27
    /**
28
     * @var array $admin
29
     */
30
    private $admin = null;
31
32
    /**
33
     * @var bool $authorized
34
     */
35
    private $authorized = FALSE;
36
37
    /**
38
     * @var bool $checked
39
     */
40
    private $checked = false;
41
42
    /**
43
     * @var array $session
44
     */
45
    protected $session;
46
47
    /**
48
     * Constructor por defecto
49
     */
50 3
    public function __construct()
51
    {
52 3
        if (PHP_SESSION_NONE === session_status()) {
53 1
            session_start();
54
        }
55 2
        $this->session = (is_null($_SESSION)) ? array() : $_SESSION;
56 2
        if (NULL === $this->getSessionKey('__FLASH_CLEAR__')) {
57 2
            $this->clearFlashes();
58 2
            $this->setSessionKey('__FLASH_CLEAR__', microtime(TRUE));
59 2
        }
60 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...
61 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...
62 2
        if (null === $this->admin) {
63 2
            $this->checkAdmin();
64 2
        }
65 2
    }
66
67
    /**
68
     * Método estático que devuelve los perfiles de la plataforma
69
     * @return array
70
     */
71 2
    public static function getProfiles()
72
    {
73
        return array(
74 2
            self::ADMIN_ID_TOKEN => _('Administrador'),
75 2
            self::MANAGER_ID_TOKEN => _('Gestor'),
76 2
            self::USER_ID_TOKEN => _('Usuario'),
77 2
        );
78
    }
79
80
    /**
81
     * Method that returns all the available profiles
82
     * @return array
83
     */
84 1
    public function getAdminProfiles()
85
    {
86 1
        return static::getProfiles();
87
    }
88
89
    /**
90
     * Método estático que devuelve los perfiles disponibles
91
     * @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...
92
     */
93 1
    public static function getCleanProfiles()
94
    {
95
        return array(
96 1
            '__SUPER_ADMIN__' => self::ADMIN_ID_TOKEN,
97 1
            '__ADMIN__' => self::MANAGER_ID_TOKEN,
98 1
            '__USER__' => self::USER_ID_TOKEN,
99 1
        );
100
    }
101
102
    /**
103
     * Método estático que devuelve los perfiles disponibles
104
     * @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...
105
     */
106 1
    public function getAdminCleanProfiles()
107
    {
108 1
        return static::getCleanProfiles();
109
    }
110
111
    /**
112
     * Método que guarda los administradores
113
     *
114
     * @param array $user
115
     *
116
     * @return bool
117
     */
118 1
    public static function save($user)
119
    {
120 1
        $saved = true;
121
        try {
122 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...
123 1
            $admins[$user['username']]['hash'] = sha1($user['username'] . $user['password']);
124 1
            $admins[$user['username']]['profile'] = $user['profile'];
125
126 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...
127 1
        } catch(\Exception $e) {
128
            Logger::log($e->getMessage(), LOG_ERR);
129
            $saved = false;
130
        }
131 1
        return $saved;
132
    }
133
134
    /**
135
     * Method to save a new admin user
136
     * @param array $user
137
     * @return bool
138
     */
139 1
    public function saveUser($user)
140
    {
141 1
        $saved = false;
142 1
        if (!empty($user)) {
143 1
            $saved = static::save($user);
144 1
        }
145 1
        return $saved;
146
    }
147
148
    /**
149
     * Servicio que actualiza los datos del usuario
150
     *
151
     * @param $user
152
     */
153 1
    public function updateUser($user)
154
    {
155 1
        $this->user = $user;
156 1
    }
157
158
    /**
159
     * Método que devuelve los administradores de una plataforma
160
     * @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...
161
     */
162 4
    public function getAdmins()
163
    {
164 4
        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...
165
    }
166
167
    /**
168
     * Método que devuelve si un usuario tiene privilegios para acceder a la zona de administración
169
     *
170
     * @param null $user
171
     * @param null $pass
172
     * @param boolean $force
173
     *
174
     * @return bool
175
     * @throws \HttpException
176
     */
177 3
    public function checkAdmin($user = NULL, $pass = NULL, $force = false)
178
    {
179 3
        Logger::log('Checking admin session');
180 3
        if ((!$this->authorized && !$this->checked) || $force) {
181 3
            $admins = $this->getAdmins();
182 3
            if (null !== $admins) {
183 1
                $request = Request::getInstance();
184
                //Sacamos las credenciales de la petición
185 1
                $user = $user ?: $request->getServer('PHP_AUTH_USER');
186 1
                $pass = $pass ?: $request->getServer('PHP_AUTH_PW');
187 1
                if (NULL === $user || (array_key_exists($user, $admins) && empty($admins[$user]))) {
188 1
                    list($user, $pass) = $this->getAdminFromCookie();
189 1
                }
190 1
                if (!empty($user) && !empty($admins[$user])) {
191 1
                    $auth = $admins[$user]['hash'];
192 1
                    $this->authorized = ($auth == sha1($user . $pass));
193 1
                    if ($this->authorized) {
194 1
                        $this->admin = array(
195 1
                            'alias' => $user,
196 1
                            'profile' => $admins[$user]['profile'],
197
                        );
198 1
                        $this->setSessionKey(self::ADMIN_ID_TOKEN, serialize($this->admin));
199 1
                    }
200 1
                }
201 1
                $this->checked = true;
202 1
            }
203 3
        }
204
205 3
        return $this->authorized;
206
    }
207
208
    /**
209
     * Método que obtiene el usuario y contraseña de la cookie de sesión de administración
210
     * @return array
211
     */
212 1
    protected function getAdminFromCookie()
213
    {
214 1
        $auth_cookie = Request::getInstance()->getCookie($this->getHash());
215 1
        $user = $pass = array();
216 1
        if (!empty($auth_cookie)) {
217 1
            list($user, $pass) = explode(':', base64_decode($auth_cookie));
218 1
        }
219
220 1
        return array($user, $pass);
221
    }
222
223
    /**
224
     * Método privado para la generación del hash de la cookie de administración
225
     * @return string
226
     */
227 1
    public function getHash()
228
    {
229 1
        return substr(self::MANAGER_ID_TOKEN, 0, 8);
230
    }
231
232
    /**
233
     * Método que devuelve el usuario logado
234
     * @return array
235
     */
236 1
    public function getUser()
237
    {
238 1
        return $this->user;
239
    }
240
241
    /**
242
     * Método que devuelve el usuario administrador logado
243
     * @return array
244
     */
245 1
    public function getAdmin()
246
    {
247 1
        return $this->admin;
248
    }
249
250
    /**
251
     * Método que calcula si se está logado o para acceder a administración
252
     * @return bool
253
     */
254 2
    public function canAccessRestrictedAdmin()
255
    {
256 2
        return null !== $this->admin && !preg_match('/^\/admin\/login/i', Request::requestUri());
257
    }
258
259
    /**
260
     * Servicio que devuelve una pantalla de error porque se necesita estar authenticado
261
     *
262
     * @param string|null $route
263
     *
264
     * @return string|null
265
     */
266
    public function notAuthorized($route)
267
    {
268
        return Template::getInstance()->render('notauthorized.html.twig', array(
269
            'route' => $route,
270
        ));
271
    }
272
273
    /**
274
     * Servicio que chequea si un usuario es super administrador o no
275
     * @return bool
276
     */
277 1
    public function isSuperAdmin()
278
    {
279 1
        $users = $this->getAdmins();
280 1
        $logged = $this->getAdminFromCookie();
281 1
        if ($users[$logged[0]]) {
282 1
            $security = $users[$logged[0]]['profile'];
283
284 1
            return self::ADMIN_ID_TOKEN === $security;
285
        }
286
287
        return FALSE;
288
    }
289
290
    /**
291
     * Servicio que devuelve un dato de sesión
292
     *
293
     * @param string $key
294
     *
295
     * @return mixed
296
     */
297 5
    public function getSessionKey($key)
298
    {
299 5
        $data = NULL;
300 5
        if (array_key_exists($key, $this->session)) {
301 3
            $data = $this->session[$key];
302 3
        }
303
304 5
        return $data;
305
    }
306
307
    /**
308
     * Servicio que setea una variable de sesión
309
     *
310
     * @param string $key
311
     * @param mixed $data
312
     *
313
     * @return Security
314
     */
315 5
    public function setSessionKey($key, $data = NULL)
316
    {
317 5
        $this->session[$key] = $data;
318
319 5
        return $this;
320
    }
321
322
    /**
323
     * Servicio que devuelve los mensajes flash de sesiones
324
     * @return mixed
325
     */
326 1
    public function getFlashes()
327
    {
328 1
        $flashes = $this->getSessionKey(self::FLASH_MESSAGE_TOKEN);
329
330 1
        return (NULL !== $flashes) ? $flashes : array();
331
    }
332
333
    /**
334
     * Servicio que limpia los mensajes flash
335
     * @return $this
336
     */
337 3
    public function clearFlashes()
338
    {
339 3
        $this->setSessionKey(self::FLASH_MESSAGE_TOKEN, NULL);
340
341 3
        return $this;
342
    }
343
344
    /**
345
     * Servicio que inserta un flash en sesión
346
     *
347
     * @param string $key
348
     * @param mixed $data
349
     */
350 1
    public function setFlash($key, $data = NULL)
351
    {
352 1
        $flashes = $this->getFlashes();
353 1
        if (!is_array($flashes)) {
354
            $flashes = array();
355
        }
356 1
        $flashes[$key] = $data;
357 1
        $this->setSessionKey(self::FLASH_MESSAGE_TOKEN, $flashes);
358 1
    }
359
360
    /**
361
     * Servicio que devuelve un flash de sesión
362
     *
363
     * @param string $key
364
     *
365
     * @return mixed
366
     */
367 1
    public function getFlash($key)
368
    {
369 1
        $flashes = $this->getFlashes();
370
371 1
        return (NULL !== $key && array_key_exists($key, $flashes)) ? $flashes[$key] : NULL;
372
    }
373
374
    /**
375
     * Servicio que actualiza
376
     *
377
     * @param boolean $closeSession
378
     *
379
     * @return Security
380
     */
381 2
    public function updateSession($closeSession = FALSE)
382
    {
383 2
        Logger::log('Update session');
384 2
        $_SESSION = $this->session;
385 2
        $_SESSION[self::USER_ID_TOKEN] = serialize($this->user);
386 2
        $_SESSION[self::ADMIN_ID_TOKEN] = serialize($this->admin);
387 2
        if ($closeSession) {
388 1
            Logger::log('Close session');
389 1
            @session_write_close();
390 1
            @session_start();
391 1
        }
392 2
        Logger::log('Session updated');
393 2
        return $this;
394
    }
395
396
    /**
397
     * Servicio que limpia la sesión
398
     */
399 1
    public function closeSession()
400
    {
401 1
        @session_destroy();
402 1
        @session_regenerate_id(TRUE);
403 1
        @session_start();
404 1
    }
405
406
}
407