Completed
Push — master ( a6f5e7...505dfd )
by John
02:13
created

validationWillFailWhenIssuerIsConfiguredAndNotInClaims()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
/*
3
 * This file is part of the KleijnWeb\JwtBundle package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
namespace KleijnWeb\JwtBundle\Tests\Authenticator;
9
10
use KleijnWeb\JwtBundle\Authenticator\Exception\InvalidTimeException;
11
use KleijnWeb\JwtBundle\Authenticator\Exception\MissingClaimsException;
12
use KleijnWeb\JwtBundle\Authenticator\JwtKey;
13
use KleijnWeb\JwtBundle\Authenticator\JwtToken;
14
use KleijnWeb\JwtBundle\Authenticator\SecretLoader;
15
use KleijnWeb\JwtBundle\Authenticator\SignatureValidator\HmacValidator;
16
use KleijnWeb\JwtBundle\Authenticator\SignatureValidator\RsaValidator;
17
18
/**
19
 * @author John Kleijn <[email protected]>
20
 */
21
class JwtKeyTest extends \PHPUnit_Framework_TestCase
22
{
23
    /**
24
     * @test
25
     * @expectedException \InvalidArgumentException
26
     */
27
    public function constructionWillFailWhenSecretNorLoaderPassed()
28
    {
29
        new JwtKey([]);
30
    }
31
32
    /**
33
     * @test
34
     */
35
    public function canOmitSecretWhenPassingLoader()
36
    {
37
        $actual   = new JwtKey(['loader' => 'foo']);
38
        $refl     = new \ReflectionClass($actual);
39
        $property = $refl->getProperty('secret');
40
        $property->setAccessible(true);
41
        $this->assertNull($property->getValue($actual));
42
    }
43
44
    /**
45
     * @test
46
     * @expectedException \InvalidArgumentException
47
     */
48
    public function cannotPassBothSecretAndLoader()
49
    {
50
        new JwtKey(['loader' => 'foo', 'secret' => 'bar']);
51
    }
52
53
    /**
54
     * @test
55
     */
56
    public function serializingWillClearSecret()
57
    {
58
        $key      = new JwtKey(['secret' => 'Buy the book']);
59
        $actual   = unserialize(serialize($key));
60
        $refl     = new \ReflectionClass($actual);
61
        $property = $refl->getProperty('secret');
62
        $property->setAccessible(true);
63
        $this->assertNull($property->getValue($actual));
64
    }
65
66
    /**
67
     * @test
68
     */
69
    public function validateTokenWillCallVerifySignatureOnToken()
70
    {
71
        $secret = (string)rand();
72
        $key    = new JwtKey(['secret' => $secret]);
73
        $key->validateToken($this->createTokenMock($secret, $key));
74
    }
75
76
    /**
77
     * @test
78
     */
79
    public function willValidateIfAudienceIsConfiguredAndMatchedAny()
80
    {
81
        $key = new JwtKey(['secret' => 'Buy the book', 'audience' => ['author', 'reader']]);
82
        $key->validateClaims(['sub' => 'john', 'aud' => 'reader']);
83
    }
84
85
    /**
86
     * @test
87
     */
88
    public function canLoadSecretFromLoader()
89
    {
90
        $secret = (string)rand();
91
        $token  = $this->createTokenMock($secret);
92
93
        $loaderMock = $this->getMockBuilder(SecretLoader::class)->getMock();
94
        $loaderMock->expects($this->once())->method('load')->with($token)->willReturn($secret);
95
96
        $key = new JwtKey(['loader' => $loaderMock]);
97
98
        $key->validateToken($token);
99
    }
100
101
    /**
102
     * @test
103
     */
104
    public function willGetRsaSignatureValidatorWhenTypeIsNotSpecified()
105
    {
106
        $key    = new JwtKey(['secret' => 'Buy the book']);
107
        $actual = $key->getSignatureValidator();
108
        $this->assertInstanceOf(HmacValidator::class, $actual);
109
    }
110
111
    /**
112
     * @test
113
     */
114
    public function willGetRsaSignatureValidatorWhenTypeIsRsa()
115
    {
116
        $key    = new JwtKey(['secret' => 'Buy the book', 'type' => JwtKey::TYPE_RSA]);
117
        $actual = $key->getSignatureValidator();
118
        $this->assertInstanceOf(RsaValidator::class, $actual);
119
    }
120
121
    /**
122
     * @test
123
     */
124 View Code Duplication
    public function validationWillFailWhenPrincipleIsMissing()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
125
    {
126
        $claims = ['prn' => 'joe'];
127
128
        $key = new JwtKey(['secret' => 'Buy the book']);
129
        $key->validateClaims($claims);
130
131
        unset($claims['prn']);
132
133
        $this->setExpectedException(MissingClaimsException::class);
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0; use expectException() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
134
135
        $key = new JwtKey(['secret' => 'Buy the book']);
136
        $key->validateClaims($claims);
137
    }
138
139
140
    /**
141
     * @test
142
     */
143 View Code Duplication
    public function validationWillFailWhenSubjectMissing()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144
    {
145
        $claims = ['sub' => 'joe'];
146
147
        $key = new JwtKey(['secret' => 'Buy the book']);
148
        $key->validateClaims($claims);
149
150
        unset($claims['sub']);
151
152
        $this->setExpectedException(MissingClaimsException::class);
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0; use expectException() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
153
154
        $key = new JwtKey(['secret' => 'Buy the book']);
155
        $key->validateClaims($claims);
156
    }
157
158
    /**
159
     * @test
160
     */
161
    public function validationWillFailWhenExpiredByExp()
162
    {
163
        $this->setExpectedException(InvalidTimeException::class, "Token is expired by 'exp'");
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0; use expectException() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
164
        $key = new JwtKey(['secret' => 'Buy the book']);
165
        $key->validateClaims(['sub' => 'john', 'exp' => time() - 2]);
166
    }
167
168
    /**
169
     * @test
170
     */
171
    public function validationWillNotFailWhenExpiredByExpButWithinLeeway()
172
    {
173
        $key = new JwtKey(['secret' => 'Buy the book', 'leeway' => 3]);
174
        $key->validateClaims(['sub' => 'john', 'exp' => time() - 2]);
175
    }
176
177
    /**
178
     * @test
179
     */
180
    public function validationWillFailWhenExpiredByIatAndMinIssueTime()
181
    {
182
        $this->setExpectedException(InvalidTimeException::class, "Server deemed your token too old");
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0; use expectException() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
183
        $key = new JwtKey(['secret' => 'Buy the book', 'minIssueTime' => time() + 2, 'leeway' => 3]);
184
        $key->validateClaims(['sub' => 'john', 'iat' => time()]);
185
    }
186
187
    /**
188
     * @test
189
     */
190
    public function validationWillFailWhenNotValidYet()
191
    {
192
        $this->setExpectedException(InvalidTimeException::class, "Token not valid yet");
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0; use expectException() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
193
        $key = new JwtKey(['secret' => 'Buy the book']);
194
        $key->validateClaims(['sub' => 'john', 'nbf' => time() + 2]);
195
    }
196
197
    /**
198
     * @test
199
     */
200
    public function validationWillNotFailWhenNotValidYetButWithinLeeway()
201
    {
202
        $key = new JwtKey(['secret' => 'Buy the book', 'leeway' => 3]);
203
        $key->validateClaims(['sub' => 'john', 'nbf' => time() + 2]);
204
    }
205
206
    /**
207
     * @test
208
     * @expectedException \InvalidArgumentException
209
     */
210
    public function validationWillFailWhenIssuerDoesNotMatch()
211
    {
212
        $key = new JwtKey(['secret' => 'Buy the book', 'issuer' => 'me']);
213
        $key->validateClaims(['sub' => 'john', 'iss' => 'you']);
214
    }
215
216
    /**
217
     * @test
218
     * @expectedException \InvalidArgumentException
219
     */
220
    public function validationWillFailWhenAudienceDoesNotMatch()
221
    {
222
        $key = new JwtKey(['secret' => 'Buy the book', 'audience' => 'me']);
223
        $key->validateClaims(['sub' => 'john', 'aud' => 'the neighbours']);
224
    }
225
226
227
    /**
228
     * @test
229
     * @expectedException \InvalidArgumentException
230
     */
231
    public function validationWillFailWhenIssuerIsConfiguredAndNotInClaims()
232
    {
233
        $key = new JwtKey(['secret' => 'Buy the book', 'issuer' => 'me']);
234
        $key->validateClaims(['sub' => 'john']);
235
    }
236
237
    /**
238
     * @test
239
     * @expectedException \InvalidArgumentException
240
     */
241
    public function validationWillFailWhenMinIssueTimeIsConfiguredAndIatNotInClaims()
242
    {
243
        $key = new JwtKey(['secret' => 'Buy the book', 'minIssueTime' => time()]);
244
        $key->validateClaims(['sub' => 'john']);
245
    }
246
247
    /**
248
     * @test
249
     * @expectedException \InvalidArgumentException
250
     */
251
    public function validationWillFailWhenAudienceIsConfiguredAndNotInClaims()
252
    {
253
        $key = new JwtKey(['secret' => 'Buy the book', 'audience' => time()]);
254
        $key->validateClaims(['sub' => 'john']);
255
    }
256
257
    /**
258
     * @test
259
     * @expectedException \InvalidArgumentException
260
     */
261
    public function validationWillFailWhenIgnoreOtherReservedAndArbitraryClaimsAreRequiredButNotInClaims()
262
    {
263
        $key = new JwtKey(
264
            ['secret' => 'Buy the book', 'require' => ['jti', 'typ', 'and now for something completely different']]
265
        );
266
        $key->validateClaims(['sub' => 'john']);
267
    }
268
269
    /**
270
     * @test
271
     * @expectedException \InvalidArgumentException
272
     */
273
    public function headerValidationWillFailWhenAlgoIsMissing()
274
    {
275
        $key = new JwtKey(['secret' => 'Buy the book']);
276
        $key->validateHeader(['typ' => 'JWT']);
277
    }
278
279
    /**
280
     * @test
281
     * @expectedException \InvalidArgumentException
282
     */
283
    public function headerValidationWillFailWhenTypeIsMissing()
284
    {
285
        $key = new JwtKey(['secret' => 'Buy the book']);
286
        $key->validateHeader(['alg' => JwtKey::TYPE_HMAC]);
287
    }
288
289
    /**
290
     * @test
291
     * @expectedException \InvalidArgumentException
292
     */
293
    public function headerValidationWillFailWhenKeyAlgoDoesNotMatchTokenAlgo()
294
    {
295
        $key = new JwtKey(['secret' => 'Buy the book', 'type' => JwtKey::TYPE_RSA]);
296
        $key->validateHeader(['typ' => 'JWT', 'alg' => JwtKey::TYPE_HMAC]);
297
    }
298
299
    /**
300
     * @test
301
     * @expectedException \InvalidArgumentException
302
     */
303
    public function headerValidationWillFailWhenTypeIsNotJwt()
304
    {
305
        $key = new JwtKey(['secret' => 'Buy the book']);
306
        $key->validateHeader(['typ' => 'Something']);
307
    }
308
309
    /**
310
     * @param string      $secret
311
     * @param JwtKey|null $key
312
     *
313
     * @return JwtToken
314
     */
315
    private function createTokenMock($secret, JwtKey $key = null)
316
    {
317
        /** @var JwtToken $token */
318
        $token = $tokenMock = $this->getMockBuilder(
319
            'KleijnWeb\JwtBundle\Authenticator\JwtToken'
320
        )->disableOriginalConstructor()->getMock();
321
322
        $tokenMock->expects($this->once())
323
            ->method('validateSignature')
324
            ->with($secret, $key ? $key->getSignatureValidator() : $this->anything());
325
326
        $tokenMock->expects($this->once())
327
            ->method('getClaims')
328
            ->willReturn(['sub' => 'john']);
329
330
        $tokenMock->expects($this->once())
331
            ->method('getHeader')
332
            ->willReturn(['alg' => JwtKey::TYPE_HMAC, 'typ' => 'JWT']);
333
334
        return $token;
335
    }
336
}
337