FileJwtBlockList   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 142
Duplicated Lines 0 %

Test Coverage

Coverage 82.22%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 43
c 1
b 0
f 0
dl 0
loc 142
ccs 37
cts 45
cp 0.8222
rs 10
wmc 15

8 Methods

Rating   Name   Duplication   Size   Complexity  
A minutesToObsolescence() 0 3 1
A isBlockListed() 0 8 2
A __construct() 0 10 1
A add() 0 12 2
A maybeRemoveObsoleteRecords() 0 9 2
A getFileData() 0 14 3
A fileExtension() 0 3 1
A removeObsoleteRecords() 0 13 3
1
<?php
2
3
namespace JWTAuth\BlockList;
4
5
use Carbon\Carbon;
6
use Illuminate\Support\Facades\Cache;
7
use Illuminate\Support\Facades\Storage;
8
use JWTAuth\Contracts\HasObsoleteRecords;
9
use JWTAuth\Contracts\JwtBlockListContract;
10
use JWTAuth\Exceptions\JWTConfigurationException;
11
use JWTAuth\JWTManager;
12
13
/**
14
 * Class FileJwtBlockList
15
 * @package JWTAuth\BlockList
16
 */
17
class FileJwtBlockList implements JwtBlockListContract, HasObsoleteRecords
18
{
19
20
    /**
21
     * Filesystem disk name
22
     * @var string
23
     */
24
    protected string $disk;
25
26
    /**
27
     * Filesystem directory name
28
     * @var string
29
     */
30
    protected string $directory;
31
32
    /**
33
     * Minutes count before blocklist removing
34
     * @var int
35
     */
36
    protected int $minutesToObsolescence;
37
38
    /**
39
     * Seconds for remove obsoleted files
40
     * @var int
41
     */
42
    protected int $periodRemoveObsolete;
43
44 4
    public function __construct(array $configs = [])
45
    {
46 4
        $this->disk = $configs['disk'] ?? '';
47 4
        throw_if(empty($this->disk), JWTConfigurationException::class, 'FileJwtBlockList: Disk is empty');
48
49 4
        $this->directory = rtrim($configs['directory'] ?? '', '/');
50 4
        throw_if(empty($this->directory), JWTConfigurationException::class, 'FileJwtBlockList: Directory is empty');
51
52 4
        $this->minutesToObsolescence = (int) ($configs['minutes_to_obsolescence']         ?? 0);
53 4
        $this->periodRemoveObsolete  = (int) ($configs['remove_obsoleted_each_x_seconds'] ?? 0);
54
    }
55
56
    /**
57
     * @inheritDoc
58
     */
59 1
    public function add(JWTManager $token): static
60
    {
61 1
        $this->maybeRemoveObsoleteRecords();
62 1
        if ($token->payload()->isValid()) {
63 1
            $data                       = $this->getFileData($token->payload()->exp());
64 1
            $data[ $token->getToken() ] = Carbon::now()->toString();
65
66 1
            Storage::disk($this->disk)->makeDirectory($this->directory);
67 1
            Storage::disk($this->disk)->put("{$this->directory}/{$token->payload()->exp()}{$this->fileExtension()}", json_encode($data));
68
        }
69
70 1
        return $this;
71
    }
72
73
    /**
74
     * @inheritDoc
75
     */
76 2
    public function isBlockListed(JWTManager $token): bool
77
    {
78 2
        if (!$token->payload()->isValid()) {
79 1
            return true;
80
        }
81 2
        $data = $this->getFileData($token->payload()->exp());
82
83 2
        return isset($data[ $token->getToken() ]);
84
    }
85
86
    /**
87
     * Get content of blocklist file
88
     *
89
     * @param string $filePrefix
90
     *
91
     * @return array
92
     */
93 2
    protected function getFileData(string $filePrefix): array
94
    {
95
        try {
96 2
            $data = Storage::disk($this->disk)->get("{$this->directory}/{$filePrefix}{$this->fileExtension()}");
97 2
            $data = json_decode($data, true);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type null; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

97
            $data = json_decode(/** @scrutinizer ignore-type */ $data, true);
Loading history...
98 2
            if (!$data) {
99 2
                $data = [];
100
            }
101
        } catch (\Exception $e) {
102
            // File not found
103
            return [];
104
        }
105
106 2
        return $data;
107
    }
108
109
    /**
110
     * Remove obsolete records if not removed recently
111
     *
112
     * @return bool
113
     */
114 1
    protected function maybeRemoveObsoleteRecords(): bool
115
    {
116 1
        if (!Cache::has('__FileJwtBlackList_REMOVED')) {
117 1
            Cache::put('__FileJwtBlackList_REMOVED', true, $this->periodRemoveObsolete);
118
119 1
            return $this->removeObsoleteRecords();
120
        }
121
122
        return false;
123
    }
124
125
    /**
126
     * @inheritDoc
127
     */
128 1
    public function removeObsoleteRecords(): bool
129
    {
130 1
        collect(Storage::disk($this->disk)->listContents($this->directory, true))
0 ignored issues
show
Bug introduced by
The method listContents() does not exist on Illuminate\Contracts\Filesystem\Filesystem. It seems like you code against a sub-type of Illuminate\Contracts\Filesystem\Filesystem such as Illuminate\Filesystem\FilesystemAdapter. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

130
        collect(Storage::disk($this->disk)->/** @scrutinizer ignore-call */ listContents($this->directory, true))
Loading history...
131 1
            ->each(function ($file) {
132
                if (
133
                    $file['type'] == 'file' &&
134
                    $file['lastModified'] < Carbon::now()->subMinutes($this->minutesToObsolescence())->getTimestamp()
135
                ) {
136
                    Storage::disk($this->disk)->delete($file['path']);
137
                }
138 1
            });
139
140 1
        return true;
141
    }
142
143
    /**
144
     * @inheritDoc
145
     */
146
    public function minutesToObsolescence(): int
147
    {
148
        return $this->minutesToObsolescence;
149
    }
150
151
    /**
152
     * File extension.
153
     *
154
     * @return string
155
     */
156 2
    protected function fileExtension(): string
157
    {
158 2
        return '.json';
159
    }
160
}
161