Failed Conditions
Push — JWKSet ( 3580c0...03e3f0 )
by Florent
02:43
created

StorableJWKSet::getFilename()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Object;
13
14
use Assert\Assertion;
15
use Base64Url\Base64Url;
16
use Jose\Factory\JWKFactory;
17
18
/**
19
 * Class StorableJWKSet.
20
 */
21
class StorableJWKSet implements StorableJWKSetInterface
22
{
23
    /**
24
     * @var \Jose\Object\JWKSetInterface
25
     */
26
    protected $jwkset;
27
28
    /**
29
     * @var string
30
     */
31
    protected $filename;
32
33
    /**
34
     * @var array
35
     */
36
    protected $parameters;
37
38
    /**
39
     * @var array
40
     */
41
    protected $nb_keys;
42
43
    /**
44
     * @var null|int
45
     */
46
    protected $file_last_modification_time = null;
47
48
    /**
49
     * StorableJWKSet constructor.
50
     *
51
     * @param string $filename
52
     * @param array  $parameters
53
     * @param int    $nb_keys
54
     */
55
    public function __construct($filename, array $parameters, $nb_keys)
56
    {
57
        Assertion::directory(dirname($filename), 'The selected directory does not exist.');
58
        Assertion::writeable(dirname($filename), 'The selected directory is not writable.');
59
        Assertion::integer($nb_keys, 'The key set must contain at least one key.');
60
        Assertion::greaterThan($nb_keys, 0, 'The key set must contain at least one key.');
61
        $this->filename = $filename;
62
        $this->parameters = $parameters;
63
        $this->nb_keys = $nb_keys;
0 ignored issues
show
Documentation Bug introduced by
It seems like $nb_keys of type integer is incompatible with the declared type array of property $nb_keys.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69
    public function current()
70
    {
71
        return $this->getJWKSet()->current();
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function next()
78
    {
79
        $this->getJWKSet()->next();
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85
    public function key()
86
    {
87
        return $this->getJWKSet()->key();
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93
    public function valid()
94
    {
95
        return $this->getJWKSet()->valid();
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function rewind()
102
    {
103
        $this->getJWKSet()->rewind();
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function offsetExists($offset)
110
    {
111
        return $this->getJWKSet()->offsetExists($offset);
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function offsetGet($offset)
118
    {
119
        return $this->getJWKSet()->offsetGet($offset);
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    public function offsetSet($offset, $value)
126
    {
127
        return $this->getJWKSet()->offsetSet($offset, $value);
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function offsetUnset($offset)
134
    {
135
        return $this->getJWKSet()->offsetUnset($offset);
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function getKey($index)
142
    {
143
        return $this->getJWKSet()->getKey($index);
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function hasKey($index)
150
    {
151
        return $this->getJWKSet()->hasKey($index);
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    public function getKeys()
158
    {
159
        return $this->getJWKSet()->getKeys();
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function addKey(JWKInterface $key)
166
    {
167
        return $this->getJWKSet()->addKey($key);
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function removeKey($index)
174
    {
175
        return $this->getJWKSet()->removeKey($index);
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181
    public function countKeys()
182
    {
183
        return $this->getJWKSet()->countKeys();
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    public function selectKey($type, $algorithm = null, array $restrictions = [])
190
    {
191
        return $this->getJWKSet()->selectKey($type, $algorithm, $restrictions);
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197
    public function count()
198
    {
199
        return $this->getJWKSet()->count();
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function jsonSerialize()
206
    {
207
        return $this->getJWKSet()->jsonSerialize();
208
    }
209
210
    /**
211
     * @return string
212
     */
213
    protected function getFilename()
214
    {
215
        return $this->filename;
216
    }
217
218
    /**
219
     * @return \Jose\Object\JWKSetInterface
220
     */
221
    protected function getJWKSet()
222
    {
223
        $this->loadJWKSetIfNeeded();
224
225
        return $this->jwkset;
226
    }
227
228
    /**
229
     * This function loads or creates it the file if needed
230
     */
231
    protected function loadJWKSetIfNeeded()
232
    {
233
        if (false === $this->hasFileBeenUpdated()) {
234
            return;
235
        }
236
        $content = $this->getFileContent();
237
        if (null === $content) {
238
            $this->createJWKSet();
239
        } else {
240
            $this->jwkset = new JWKSet($content);
241
            $this->file_last_modification_time = $this->getFileLastModificationTime();
242
        }
243
    }
244
245
    /**
246
     * @return bool
247
     */
248
    protected function hasFileBeenUpdated()
249
    {
250
        if (null === $this->file_last_modification_time || null === $this->getFileLastModificationTime()) {
251
            return true;
252
        }
253
254
        return $this->file_last_modification_time !== $this->getFileLastModificationTime();
255
    }
256
257
    protected function getFileLastModificationTime()
258
    {
259
        if (file_exists($this->getFilename())) {
260
            return filemtime($this->getFilename());
261
        }
262
    }
263
264
    /**
265
     * This function returns the content of the file only if it is an array
266
     *
267
     * @return null|array
268
     */
269
    protected function getFileContent()
270
    {
271
        if (!file_exists($this->getFilename())) {
272
            return;
273
        }
274
        $content = file_get_contents($this->getFilename());
275
        if (false === $content) {
276
            return;
277
        }
278
        $content = json_decode($content, true);
279
        if (!is_array($content)) {
280
            return;
281
        }
282
283
        return $content;
284
    }
285
286
    /**
287
     * This method creates the JWKSet and populate it with keys
288
     */
289
    protected function createJWKSet()
290
    {
291
        $this->jwkset = new JWKSet();
292
        for ($i = 0; $i < $this->nb_keys; $i++) {
293
            $key = $this->createJWK();
294
            $this->jwkset->addKey($key);
0 ignored issues
show
Bug introduced by
It seems like $key defined by $this->createJWK() on line 293 can also be of type object<Jose\Object\JWKSet>; however, Jose\Object\JWKSet::addKey() does only seem to accept object<Jose\Object\JWKInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
295
        }
296
297
        $this->save();
298
    }
299
300
    /**
301
     * @return \Jose\Object\JWKInterface
302
     */
303
    protected function createJWK()
304
    {
305
        $data = JWKFactory::createKey($this->parameters)->getAll();
306
        $data['kid'] = Base64Url::encode(random_bytes(64));
307
308
        return JWKFactory::createFromValues($data);
309
    }
310
311
    /**
312
     * This method saves the JWKSet in the file.
313
     */
314
    protected function save()
315
    {
316
        @unlink($this->getFilename());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
317
        file_put_contents($this->getFilename(), json_encode($this->jwkset));
318
        $this->file_last_modification_time = $this->getFileLastModificationTime();
319
    }
320
}
321