Token   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 99
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 0
dl 0
loc 99
ccs 37
cts 37
cp 1
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A addToken() 0 17 1
A getToken() 0 4 1
A getTokensForUser() 0 13 3
A hasUserForRole() 0 13 4
A revoke() 0 16 2
1
<?php
2
3
namespace BrainExe\Core\Authentication;
4
5
use BrainExe\Core\Annotations\Service;
6
use BrainExe\Core\Traits\IdGeneratorTrait;
7
use BrainExe\Core\Traits\RedisTrait;
8
use Generator;
9
10
/**
11
 * @Service
12
 */
13
class Token
14
{
15
16
    const USER_KEY  = 'tokens:user:%s';
17
    const TOKEN_KEY = 'tokens';
18
19
    use RedisTrait;
20
    use IdGeneratorTrait;
21
22
    /**
23
     * @param int $userId
24
     * @param string[] $roles
25
     * @param string $name
26
     * @return string
27
     */
28 1
    public function addToken(int $userId, array $roles = [], string $name = '') : string
29
    {
30 1
        $token = $this->generateRandomId(40);
31
32 1
        $redis = $this->getRedis()->pipeline();
33
34 1
        $redis->sadd(sprintf(self::USER_KEY, $userId), $token);
35 1
        $redis->hset(self::TOKEN_KEY, $token, json_encode([
36 1
            'userId' => $userId,
37 1
            'roles'  => $roles,
38 1
            'name'   => $name
39
        ]));
40
41 1
        $redis->execute();
42
43 1
        return $token;
44
    }
45
46
    /**
47
     * @param string $token
48
     * @return array
49
     */
50 6
    public function getToken(string $token)
51
    {
52 6
        return json_decode($this->getRedis()->hget(self::TOKEN_KEY, $token), true);
53
    }
54
55
    /**
56
     * @param int $userId
57
     * @return array[]|Generator
58
     */
59 2
    public function getTokensForUser(int $userId)
60
    {
61 2
        $redis     = $this->getRedis();
62 2
        $tokensIds = $redis->smembers(sprintf(self::USER_KEY, $userId));
63
64 2
        if (!empty($tokensIds)) {
65 1
            $tokens = $redis->hmget(self::TOKEN_KEY, $tokensIds);
66
67 1
            foreach ($tokens as $idx => $token) {
68 1
                yield $tokensIds[$idx] => json_decode($token, true);
69
            }
70
        }
71 2
    }
72
73
    /**
74
     * @param string $token
75
     * @param string|null $role
76
     * @return int|null
77
     */
78 3
    public function hasUserForRole(string $token, string $role = null) : ?int
79
    {
80 3
        $tokenData = $this->getToken($token);
81 3
        if (empty($tokenData)) {
82 1
            return null;
83
        }
84
85 2
        if ($role && !in_array($role, $tokenData['roles'], true)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $role of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
86 1
            return null;
87
        }
88
89 1
        return $tokenData['userId'];
90
    }
91
92
    /**
93
     * @param string $token
94
     */
95 2
    public function revoke(string $token)
96
    {
97 2
        $tokenData = $this->getToken($token);
98 2
        if (!$tokenData) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tokenData of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
99 1
            return;
100
        }
101
102 1
        $userId = $tokenData['userId'];
103
104 1
        $redis = $this->getRedis()->pipeline();
105
106 1
        $redis->srem(sprintf(self::USER_KEY, $userId), $token);
107 1
        $redis->hdel(self::TOKEN_KEY, $token);
108
109 1
        $redis->execute();
110 1
    }
111
}
112