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

AuthenticatorTest::supportsPreAuthToken()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 8
Ratio 100 %

Importance

Changes 0
Metric Value
dl 8
loc 8
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
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\Authenticator;
11
use KleijnWeb\JwtBundle\Authenticator\JwtKey;
12
use KleijnWeb\JwtBundle\Authenticator\JwtToken;
13
use KleijnWeb\JwtBundle\User\JwtUserProvider;
14
use KleijnWeb\JwtBundle\User\UnsafeGroupsUserInterface;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
17
use Symfony\Component\Security\Core\User\User;
18
use Symfony\Component\Security\Core\User\UserProviderInterface;
19
20
/**
21
 * @author John Kleijn <[email protected]>
22
 */
23
class AuthenticatorTest extends \PHPUnit_Framework_TestCase
24
{
25
    // @codingStandardsIgnoreStart
26
27
    /**
28
     * Created using jwt.io
29
     */
30
    const TEST_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleU9uZSJ9.eyJwcm4iOiJqb2huIiwiaXNzIjoiaHR0cDovL2FwaS5zZXJ2ZXIxLmNvbS9vYXV0aDIvdG9rZW4ifQ._jXjAWMzwwG1v5N3ZOEUoLGSINtmwLsvQdfYkYAcWiY';
31
32
    const JKEY_CLASS = 'KleijnWeb\JwtBundle\Authenticator\JwtKey';
33
34
    /**
35
     * @var array
36
     */
37
    private static $keyConfig = [
38
        'keyOne' =>
39
            [
40
                'issuer' => 'http://api.server1.com/oauth2/token',
41
                'secret' => 'A Pre-Shared Key',
42
                'type'   => 'HS256',
43
            ],
44
        'keyTwo' =>
45
            [
46
                'issuer' => 'http://api.server2.com/oauth2/token',
47
                'type'   => 'RS256',
48
                'secret' => 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0F',
49
            ],
50
    ];
51
52
    // @codingStandardsIgnoreEnd
53
54
    /**
55
     * @var JwtKey[]
56
     */
57
    private $keys = [];
58
59
    protected function setUp()
60
    {
61
        foreach (self::$keyConfig as $keyId => $config) {
62
            $config['kid']      = $keyId;
63
            $this->keys[$keyId] = new JwtKey($config);
64
        }
65
    }
66
67
    /**
68
     * @test
69
     */
70
    public function getGetKeysUsingIndexesInConfig()
71
    {
72
        $authenticator = new Authenticator($this->keys);
73
74
        $this->assertInstanceOf(self::JKEY_CLASS, $authenticator->getKeyById('keyOne'));
75
        $this->assertInstanceOf(self::JKEY_CLASS, $authenticator->getKeyById('keyTwo'));
76
    }
77
78
    /**
79
     * @test
80
     */
81
    public function willGetSingleKeyWhenKeyIdIsNull()
82
    {
83
        $config = $this->keys;
84
        unset($config['keyTwo']);
85
86
        $authenticator = new Authenticator($config);
87
88
        $this->assertInstanceOf(self::JKEY_CLASS, $authenticator->getKeyById(null));
89
    }
90
91
    /**
92
     * @test
93
     * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException
94
     */
95
    public function willFailWhenTryingToGetKeyWithoutIdWhenThereAreMoreThanOne()
96
    {
97
        $authenticator = new Authenticator($this->keys);
98
99
        $this->assertInstanceOf(self::JKEY_CLASS, $authenticator->getKeyById(null));
100
    }
101
102
    /**
103
     * @test
104
     * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException
105
     */
106
    public function willFailWhenTryingToGetUnknownKey()
107
    {
108
        $authenticator = new Authenticator($this->keys);
109
110
        $this->assertInstanceOf(self::JKEY_CLASS, $authenticator->getKeyById('blah'));
111
    }
112
113
    /**
114
     * @test
115
     */
116
    public function authenticateTokenWillSetUserFetchedFromUserProviderOnToken()
117
    {
118
        $jwtToken      = $this->createToken(['sub' => 'john']);
119
        $authenticator = new Authenticator($this->keys);
120
        $anonToken     = new PreAuthenticatedToken('foo', $jwtToken, 'myprovider');
121
122
        $userProvider = $this->getMockBuilder(
123
            'Symfony\Component\Security\Core\User\UserProviderInterface'
124
        )->getMockForAbstractClass();
125
126
        $userProvider->expects($this->once())
127
            ->method('loadUserByUsername')
128
            ->with('john')
129
            ->willReturn(new User('john', 'hi there'));
130
131
        $authenticator->authenticateToken($anonToken, $userProvider, 'myprovider');
132
    }
133
134
    /**
135
     * @test
136
     * @expectedException \UnexpectedValueException
137
     */
138 View Code Duplication
    public function authenticateTokenWillFailIfCredentialsAreNotJwtToken()
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...
139
    {
140
        $authenticator = new Authenticator($this->keys);
141
        $anonToken     = new PreAuthenticatedToken('foo', ['sub' => 'john'], 'myprovider');
142
143
        $userProvider = $this->getMockBuilder(
144
            'Symfony\Component\Security\Core\User\UserProviderInterface'
145
        )->getMockForAbstractClass();
146
147
        $authenticator->authenticateToken($anonToken, $userProvider, 'myprovider');
148
    }
149
150
    /**
151
     * @test
152
     */
153 View Code Duplication
    public function supportsPreAuthToken()
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...
154
    {
155
        $authenticator = new Authenticator($this->keys);
156
157
        $securityToken = new PreAuthenticatedToken('foo', 'bar', 'myprovider');
158
        $actual        = $authenticator->supportsToken($securityToken, 'myprovider');
159
        $this->assertTrue($actual);
160
    }
161
162
    /**
163
     * @test
164
     * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
165
     */
166
    public function willFailWhenApiKeyNotFoundInHeader()
167
    {
168
        $authenticator = new Authenticator($this->keys);
169
        $request       = new Request();
170
        $authenticator->createToken($request, 'myprovider');
171
    }
172
173
    /**
174
     * @test
175
     */
176
    public function canGetAnonTokenWithClaims()
177
    {
178
        $authenticator = new Authenticator($this->keys);
179
        $request       = new Request();
180
        $request->headers->set('Authorization', 'Bearer ' . self::TEST_TOKEN);
181
        $token = $authenticator->createToken($request, 'myprovider');
182
183
        $expected = new JwtToken(self::TEST_TOKEN);
184
        $this->assertEquals($expected, $token->getCredentials());
185
    }
186
187
    /**
188
     * @test
189
     */
190 View Code Duplication
    public function willSetClaimsOnJwtUserProvider()
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...
191
    {
192
        $authenticator = new Authenticator($this->keys);
193
        $token         = $this->createToken(['sub' => 'john', 'aud' => 'guests']);
194
        $anonToken     = new PreAuthenticatedToken('foo', $token, 'myprovider');
195
196
        $user = $this->getMockBuilder(UnsafeGroupsUserInterface::class)->getMockForAbstractClass();
197
198
        $userProvider = $this->getMockBuilder(JwtUserProvider::class)->disableOriginalConstructor()->getMock();
199
200
        $userProvider
201
            ->expects($this->once())
202
            ->method('loadUserByUsername')
203
            ->willReturn($user);
204
205
        $userProvider
206
            ->expects($this->once())
207
            ->method('setClaimsUsingToken')
208
            ->with($token);
209
210
        $user->expects($this->once())
211
            ->method('getRoles')
212
            ->willReturn(['ROLE_GUESTS']);
213
214
        $authenticator->authenticateToken($anonToken, $userProvider, 'myprovider');
215
    }
216
217
    /**
218
     * @deprecated
219
     * @test
220
     */
221 View Code Duplication
    public function willAddRolesFromAudienceClaimsInToken()
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...
222
    {
223
        $authenticator = new Authenticator($this->keys);
224
        $token         = $this->createToken(['sub' => 'john', 'aud' => 'guests']);
225
        $anonToken     = new PreAuthenticatedToken('foo', $token, 'myprovider');
226
227
        $user = $this->getMockBuilder(UnsafeGroupsUserInterface::class)->getMockForAbstractClass();
228
229
        $userProvider = $this->getMockBuilder(UserProviderInterface::class)->getMockForAbstractClass();
230
231
        $userProvider->expects($this->once())
232
            ->method('loadUserByUsername')
233
            ->willReturn($user);
234
235
        $user->expects($this->once())
236
            ->method('addRole')
237
            ->with('ROLE_GUESTS');
238
239
        $user->expects($this->once())
240
            ->method('getRoles')
241
            ->willReturn(['ROLE_GUESTS']);
242
243
        $authenticator->authenticateToken($anonToken, $userProvider, 'myprovider');
244
    }
245
246
    /**
247
     * @deprecated
248
     * @test
249
     */
250
    public function willAddMultipleRolesFromAudienceClaimsInToken()
251
    {
252
        $authenticator = new Authenticator($this->keys);
253
        $token         = $this->createToken(['sub' => 'john', 'aud' => ['guests', 'users']]);
254
        $anonToken     = new PreAuthenticatedToken('foo', $token, 'myprovider');
255
256
        $user = $this->getMockBuilder(UnsafeGroupsUserInterface::class)->getMockForAbstractClass();
257
258
        $userProvider = $this->getMockBuilder(UserProviderInterface::class)->getMockForAbstractClass();
259
260
        $userProvider->expects($this->once())
261
            ->method('loadUserByUsername')
262
            ->willReturn($user);
263
264
        $user->expects($this->exactly(2))
265
            ->method('addRole');
266
267
        $user->expects($this->once())
268
            ->method('getRoles')
269
            ->willReturn(['guests', 'users']);
270
271
        $authenticator->authenticateToken($anonToken, $userProvider, 'myprovider');
272
    }
273
274
    /**
275
     * @param array $claims
276
     *
277
     * @return JwtToken
278
     */
279 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...
280
    {
281
        return new JwtToken([
282
            'header' => [
283
                'alg' => 'HS256',
284
                'typ' => 'JWT',
285
                'kid' => 'keyOne'
286
            ],
287
            'claims' => $claims,
288
            'secret' => 'secret'
289
        ]);
290
    }
291
}
292