Completed
Push — master ( ff92f6...3ef1d3 )
by John
09:08
created

willAddRolesFromAudienceClaimsInToken()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 17
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\Jwt;
9
10
use KleijnWeb\JwtBundle\Authentication\JwtAuthenticationProvider;
11
use KleijnWeb\JwtBundle\Authentication\JwtAuthenticationToken;
12
use KleijnWeb\JwtBundle\Jwt\JwtKey;
13
use KleijnWeb\JwtBundle\Jwt\JwtToken;
14
use KleijnWeb\JwtBundle\User\JwtUserProvider;
15
use KleijnWeb\JwtBundle\User\UnsafeGroupsUserInterface;
16
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
17
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
18
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
19
use Symfony\Component\Security\Core\User\User;
20
use Symfony\Component\Security\Core\User\UserProviderInterface;
21
22
/**
23
 * @author John Kleijn <[email protected]>
24
 */
25
class JwtAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
26
{
27
    const ISSUER = 'http://api.server1.com/oauth2/token';
28
    const SECRET = 'A Pre-Shared Key';
29
30
    // @codingStandardsIgnoreStart
31
    /**
32
     * Created using jwt.io
33
     */
34
    const TEST_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleU9uZSJ9.eyJwcm4iOiJqb2huIiwiaXNzIjoiaHR0cDovL2FwaS5zZXJ2ZXIxLmNvbS9vYXV0aDIvdG9rZW4ifQ._jXjAWMzwwG1v5N3ZOEUoLGSINtmwLsvQdfYkYAcWiY';
35
    // @codingStandardsIgnoreEnd
36
37
    /**
38
     * @var array
39
     */
40
    private static $keyConfig = [
41
        'keyOne' =>
42
            [
43
                'issuer' => self::ISSUER,
44
                'secret' => self::SECRET,
45
                'type'   => 'HS256',
46
            ],
47
        'keyTwo' =>
48
            [
49
                'issuer' => self::ISSUER,
50
                'type'   => 'RS256',
51
                'secret' => 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0F',
52
            ],
53
    ];
54
55
    /**
56
     * @var JwtKey[]
57
     */
58
    private $keys = [];
59
60
    /**
61
     * @var UserProviderInterface
62
     */
63
    private $standardUserProviderMock;
64
65
    protected function setUp()
66
    {
67
        foreach (self::$keyConfig as $keyId => $config) {
68
            $config['kid']      = $keyId;
69
            $this->keys[$keyId] = new JwtKey($config);
70
        }
71
72
        $this->standardUserProviderMock = $this->getMockForAbstractClass(UserProviderInterface::class);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getMockForAbstrac...oviderInterface::class) of type object<PHPUnit_Framework_MockObject_MockObject> is incompatible with the declared type object<Symfony\Component...\UserProviderInterface> of property $standardUserProviderMock.

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...
73
    }
74
75
    /**
76
     * @test
77
     */
78
    public function getGetKeysUsingIndexesInConfig()
79
    {
80
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
81
82
        $this->assertInstanceOf(JwtKey::class, $jwtAuthenticationProvider->getKeyById('keyOne'));
83
        $this->assertInstanceOf(JwtKey::class, $jwtAuthenticationProvider->getKeyById('keyTwo'));
84
    }
85
86
    /**
87
     * @test
88
     */
89
    public function willGetSingleKeyWhenKeyIdIsNull()
90
    {
91
        $config = $this->keys;
92
        unset($config['keyTwo']);
93
94
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $config);
95
96
        $this->assertInstanceOf(JwtKey::class, $jwtAuthenticationProvider->getKeyById(null));
97
    }
98
99
    /**
100
     * @test
101
     * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException
102
     */
103
    public function willFailWhenTryingToGetKeyWithoutIdWhenThereAreMoreThanOne()
104
    {
105
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
106
107
        $this->assertInstanceOf(JwtKey::class, $jwtAuthenticationProvider->getKeyById(null));
108
    }
109
110
    /**
111
     * @test
112
     * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException
113
     */
114
    public function willFailWhenTryingToGetUnknownKey()
115
    {
116
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
117
118
        $this->assertInstanceOf(JwtKey::class, $jwtAuthenticationProvider->getKeyById('blah'));
119
    }
120
121
    /**
122
     * @test
123
     */
124
    public function supportsJwtAuthenticationToken()
125
    {
126
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
127
128
        $this->assertTrue($jwtAuthenticationProvider->supports(new JwtAuthenticationToken()));
129
    }
130
131
    /**
132
     * @test
133
     */
134
    public function doesNotSupportAnonToken()
135
    {
136
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
137
138
        $this->assertFalse($jwtAuthenticationProvider->supports(new AnonymousToken('secret', 'john')));
139
    }
140
141
    /**
142
     * @test
143
     */
144 View Code Duplication
    public function authenticateTokenWillThrowExceptionWhenTokenUnsupportedType()
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...
145
    {
146
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
147
        $anonToken                 = new PreAuthenticatedToken('foo', '', 'myprovider');
148
149
        $this->expectException(\LogicException::class);
150
151
        $jwtAuthenticationProvider->authenticate($anonToken);
152
    }
153
154
    /**
155
     * @test
156
     */
157 View Code Duplication
    public function authenticateTokenWillSetUserFetchedFromUserProviderOnToken()
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...
158
    {
159
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
160
        $authToken                 = new JwtAuthenticationToken([], self::TEST_TOKEN);
161
162
        /** @var \PHPUnit_Framework_MockObject_MockObject $mock */
163
        $mock = $this->standardUserProviderMock;
164
        $mock
165
            ->expects($this->once())
166
            ->method('loadUserByUsername')
167
            ->with('john')
168
            ->willReturn(new User('john', 'hi there'));
169
170
        $jwtAuthenticationProvider->authenticate($authToken);
171
    }
172
173
    /**
174
     * @test
175
     * @expectedException \UnexpectedValueException
176
     */
177 View Code Duplication
    public function authenticateTokenWillFailWhenTokenStringInvalid()
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...
178
    {
179
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
180
        $authToken                 = new JwtAuthenticationToken([], 'invalid');
181
182
        $this->expectException(BadCredentialsException::class);
183
184
        $jwtAuthenticationProvider->authenticate($authToken);
185
    }
186
187
    /**
188
     * @test
189
     * @expectedException \UnexpectedValueException
190
     */
191 View Code Duplication
    public function authenticateTokenWillWillNotCallUserProviderWhenTokenStringInvalid()
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...
192
    {
193
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
194
        $authToken                 = new JwtAuthenticationToken([], 'invalid');
195
196
        /** @var \PHPUnit_Framework_MockObject_MockObject $mock */
197
        $mock = $this->standardUserProviderMock;
198
        $mock
199
            ->expects($this->never())
200
            ->method('loadUserByUsername')
201
            ->with('john')
202
            ->willReturn(new User('john', 'hi there'));
203
204
        $this->expectException(BadCredentialsException::class);
205
206
        $jwtAuthenticationProvider->authenticate($authToken);
207
    }
208
209
    /**
210
     * @test
211
     */
212
    public function willSetClaimsOnJwtUserProvider()
213
    {
214
        $userProvider = $this->getMockBuilder(JwtUserProvider::class)->disableOriginalConstructor()->getMock();
215
216
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($userProvider, $this->keys);
217
        $authToken                 = new JwtAuthenticationToken([], self::TEST_TOKEN);
218
219
        $user = $this->getMockBuilder(UnsafeGroupsUserInterface::class)->getMockForAbstractClass();
220
221
        $userProvider
222
            ->expects($this->once())
223
            ->method('loadUserByUsername')
224
            ->willReturn($user);
225
226
        $userProvider
227
            ->expects($this->once())
228
            ->method('setClaimsUsingToken')
229
            ->with($this->isInstanceOf(JwtToken::class));
230
231
        $user->expects($this->once())
232
            ->method('getRoles')
233
            ->willReturn(['ROLE_GUESTS']);
234
235
        $jwtAuthenticationProvider->authenticate($authToken);
236
    }
237
238
    /**
239
     * @deprecated
240
     * @test
241
     */
242
    public function willAddRolesFromAudienceClaimsInToken()
243
    {
244
        $token = $this->createToken(['sub' => 'john', 'aud' => 'guests', 'iss' => self::ISSUER]);
245
246
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
247
        $authToken                 = new JwtAuthenticationToken([], $token->getTokenString());
248
249
        $user = $this->getMockBuilder(UnsafeGroupsUserInterface::class)->getMockForAbstractClass();
250
251
        /** @var \PHPUnit_Framework_MockObject_MockObject $mock */
252
        $mock = $this->standardUserProviderMock;
253
        $mock
254
            ->expects($this->once())
255
            ->method('loadUserByUsername')
256
            ->willReturn($user);
257
258
        $user
259
            ->expects($this->once())
260
            ->method('addRole');
261
262
        $user->expects($this->once())
263
            ->method('getRoles')
264
            ->willReturn(['ROLE_GUESTS']);
265
266
        $jwtAuthenticationProvider->authenticate($authToken);
267
    }
268
269
    /**
270
     * @deprecated
271
     * @test
272
     */
273
    public function willAddMultipleRolesFromAudienceClaimsInToken()
274
    {
275
        $token = $this->createToken(['sub' => 'john', 'aud' => ['guests', 'users'], 'iss' => self::ISSUER]);
276
277
        $jwtAuthenticationProvider = new JwtAuthenticationProvider($this->standardUserProviderMock, $this->keys);
278
        $authToken                 = new JwtAuthenticationToken([], $token->getTokenString());
279
280
        $user = $this->getMockBuilder(UnsafeGroupsUserInterface::class)->getMockForAbstractClass();
281
282
        /** @var \PHPUnit_Framework_MockObject_MockObject $mock */
283
        $mock = $this->standardUserProviderMock;
284
        $mock
285
            ->expects($this->once())
286
            ->method('loadUserByUsername')
287
            ->willReturn($user);
288
289
        $user->expects($this->exactly(2))
290
            ->method('addRole');
291
292
        $user
293
            ->expects($this->once())
294
            ->method('getRoles')
295
            ->willReturn(['ROLE_GUESTS', 'ROLE_USERS']);
296
297
        $jwtAuthenticationProvider->authenticate($authToken);
298
    }
299
300
    /**
301
     * @param array $claims
302
     *
303
     * @return JwtToken
304
     */
305 View Code Duplication
    private function createToken(array $claims)
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...
306
    {
307
        return new JwtToken([
308
            'header' => [
309
                'alg' => 'HS256',
310
                'typ' => 'JWT',
311
                'kid' => 'keyOne'
312
            ],
313
            'claims' => $claims,
314
            'secret' => self::SECRET
315
        ]);
316
    }
317
}
318