1
|
|
|
<?php declare(strict_types = 1); |
2
|
|
|
|
3
|
|
|
namespace AtlassianConnectBundle\Tests\Security; |
4
|
|
|
|
5
|
|
|
use AtlassianConnectBundle\Entity\Tenant; |
6
|
|
|
use AtlassianConnectBundle\Security\JWTAuthenticator; |
7
|
|
|
use AtlassianConnectBundle\Security\JWTUserProvider; |
8
|
|
|
use AtlassianConnectBundle\Security\JWTUserProviderInterface; |
9
|
|
|
use Doctrine\ORM\EntityManagerInterface; |
10
|
|
|
use Doctrine\Persistence\ObjectRepository; |
11
|
|
|
use PHPUnit\Framework\MockObject\MockObject; |
12
|
|
|
use PHPUnit\Framework\TestCase; |
13
|
|
|
use Symfony\Component\HttpFoundation\Request; |
14
|
|
|
use Symfony\Component\HttpKernel\KernelInterface; |
15
|
|
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; |
16
|
|
|
use Symfony\Component\Security\Core\Exception\AuthenticationException; |
17
|
|
|
use Symfony\Component\Security\Core\User\UserInterface; |
18
|
|
|
use Symfony\Component\Security\Core\User\UserProviderInterface; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Class JWTAuthenticatorTest |
22
|
|
|
*/ |
23
|
|
|
final class JWTAuthenticatorTest extends TestCase |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* @var KernelInterface|MockObject |
27
|
|
|
*/ |
28
|
|
|
private $kernel; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var EntityManagerInterface|MockObject |
32
|
|
|
*/ |
33
|
|
|
private $em; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @var string |
37
|
|
|
*/ |
38
|
|
|
private $tenantEntityClass; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var int |
42
|
|
|
*/ |
43
|
|
|
private $devTenant; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var JWTAuthenticator |
47
|
|
|
*/ |
48
|
|
|
private $jwtAuthenticator; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Setup method |
52
|
|
|
*/ |
53
|
|
|
public function setUp(): void |
54
|
|
|
{ |
55
|
|
|
$this->kernel = $this->createMock(KernelInterface::class); |
56
|
|
|
$this->em = $this->createMock(EntityManagerInterface::class); |
57
|
|
|
$this->tenantEntityClass = Tenant::class; |
58
|
|
|
$this->devTenant = 1; |
59
|
|
|
|
60
|
|
|
$this->jwtAuthenticator = new JWTAuthenticator( |
61
|
|
|
$this->kernel, |
62
|
|
|
$this->em, |
63
|
|
|
$this->tenantEntityClass, |
64
|
|
|
$this->devTenant |
65
|
|
|
); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Test start method |
70
|
|
|
*/ |
71
|
|
|
public function testItSendsA401WhenNoAuthenticationHeaderIsSet(): void |
72
|
|
|
{ |
73
|
|
|
$response = $this->jwtAuthenticator->start(new Request()); |
74
|
|
|
|
75
|
|
|
$this->assertEquals('Authentication header required', $response->getContent()); |
76
|
|
|
$this->assertEquals(401, $response->getStatusCode()); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Tests if the request is supported |
81
|
|
|
*/ |
82
|
|
|
public function testSupportsRequest(): void |
83
|
|
|
{ |
84
|
|
|
$request = new Request(['jwt' => 'token']); |
85
|
|
|
$this->assertTrue($this->jwtAuthenticator->supports($request)); |
86
|
|
|
|
87
|
|
|
$request = new Request(); |
88
|
|
|
$request->headers->set('authorization', 'jwt token'); |
89
|
|
|
$this->assertTrue($this->jwtAuthenticator->supports($request)); |
90
|
|
|
|
91
|
|
|
$request = new Request(); |
92
|
|
|
|
93
|
|
|
$this->kernel |
94
|
|
|
->expects($this->once()) |
95
|
|
|
->method('getEnvironment') |
96
|
|
|
->willReturn('dev'); |
97
|
|
|
|
98
|
|
|
$this->assertTrue($this->jwtAuthenticator->supports($request)); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Tests if the request is not supportd |
103
|
|
|
*/ |
104
|
|
|
public function testDoesNotSupportRequest(): void |
105
|
|
|
{ |
106
|
|
|
$request = new Request(); |
107
|
|
|
$this->assertFalse($this->jwtAuthenticator->supports($request)); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Test if the getCredentials method returns a valid array |
112
|
|
|
*/ |
113
|
|
|
public function testGetsCredentials(): void |
114
|
|
|
{ |
115
|
|
|
$request = new Request(['jwt' => 'token']); |
116
|
|
|
$credentials = $this->jwtAuthenticator->getCredentials($request); |
117
|
|
|
$this->assertIsArray($credentials); |
118
|
|
|
$this->assertArrayHasKey('jwt', $credentials); |
119
|
|
|
$this->assertEquals('token', $credentials['jwt']); |
120
|
|
|
|
121
|
|
|
$request = new Request(); |
122
|
|
|
$request->headers->set('authorization', 'jwt token'); |
123
|
|
|
$credentials = $this->jwtAuthenticator->getCredentials($request); |
124
|
|
|
$this->assertIsArray($credentials); |
125
|
|
|
$this->assertArrayHasKey('jwt', $credentials); |
126
|
|
|
$this->assertEquals('token', $credentials['jwt']); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Test if the getCredentials method returns null when no jwt token is passed |
131
|
|
|
*/ |
132
|
|
|
public function testGetsCredentialsTokenDoesNotExist(): void |
133
|
|
|
{ |
134
|
|
|
$this->kernel |
135
|
|
|
->expects($this->once()) |
136
|
|
|
->method('getEnvironment') |
137
|
|
|
->willReturn('prod'); |
138
|
|
|
|
139
|
|
|
$request = new Request(); |
140
|
|
|
$credentials = $this->jwtAuthenticator->getCredentials($request); |
141
|
|
|
$this->assertNull($credentials); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Test if the getCredentials method can get the credentials in dev mode |
146
|
|
|
*/ |
147
|
|
|
public function testGetsCredentialsOnDevTenant(): void |
148
|
|
|
{ |
149
|
|
|
$tenant = new Tenant(); |
150
|
|
|
$tenant->setClientKey('client_key'); |
151
|
|
|
$tenant->setSharedSecret('shared_secret'); |
152
|
|
|
|
153
|
|
|
$repository = $this->createMock(ObjectRepository::class); |
154
|
|
|
$repository |
155
|
|
|
->expects($this->once()) |
156
|
|
|
->method('find') |
157
|
|
|
->with(1) |
158
|
|
|
->willReturn($tenant); |
159
|
|
|
|
160
|
|
|
$this->em |
161
|
|
|
->expects($this->once()) |
162
|
|
|
->method('getRepository') |
163
|
|
|
->willReturn($repository); |
164
|
|
|
|
165
|
|
|
$this->kernel |
166
|
|
|
->expects($this->once()) |
167
|
|
|
->method('getEnvironment') |
168
|
|
|
->willReturn('dev'); |
169
|
|
|
|
170
|
|
|
$request = new Request(); |
171
|
|
|
$credentials = $this->jwtAuthenticator->getCredentials($request); |
172
|
|
|
$this->assertIsArray($credentials); |
173
|
|
|
$this->assertArrayHasKey('jwt', $credentials); |
174
|
|
|
$this->assertIsString($credentials['jwt']); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Test it fails when no tenant exists |
179
|
|
|
*/ |
180
|
|
|
public function testItFailsWhenNoTenantExists(): void |
181
|
|
|
{ |
182
|
|
|
$this->expectException(\RuntimeException::class); |
183
|
|
|
|
184
|
|
|
$repository = $this->createMock(ObjectRepository::class); |
185
|
|
|
$repository |
186
|
|
|
->expects($this->once()) |
187
|
|
|
->method('find') |
188
|
|
|
->with(1) |
189
|
|
|
->willReturn(null); |
190
|
|
|
|
191
|
|
|
$this->em |
192
|
|
|
->expects($this->once()) |
193
|
|
|
->method('getRepository') |
194
|
|
|
->willReturn($repository); |
195
|
|
|
|
196
|
|
|
$this->kernel |
197
|
|
|
->expects($this->once()) |
198
|
|
|
->method('getEnvironment') |
199
|
|
|
->willReturn('dev'); |
200
|
|
|
|
201
|
|
|
$request = new Request(); |
202
|
|
|
$this->jwtAuthenticator->getCredentials($request); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Test get user gets invalid user provider |
207
|
|
|
*/ |
208
|
|
|
public function testGetUserGetsInvalidUserProvider(): void |
209
|
|
|
{ |
210
|
|
|
$this->expectException(\InvalidArgumentException::class); |
211
|
|
|
$this->expectExceptionMessage('UserProvider must implement AtlassianConnectBundle\Security\JWTUserProviderInterface'); |
212
|
|
|
|
213
|
|
|
$userProvider = $this->createMock(UserProviderInterface::class); |
214
|
|
|
|
215
|
|
|
$this->jwtAuthenticator->getUser('credentials', $userProvider); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Test get user without client key throws exception |
220
|
|
|
*/ |
221
|
|
|
public function testGetUserWithoutClientKeyThrowsException(): void |
222
|
|
|
{ |
223
|
|
|
$this->expectException(AuthenticationException::class); |
224
|
|
|
$this->expectExceptionMessage('API Key "token" does not exist.'); |
225
|
|
|
|
226
|
|
|
$token = [ |
227
|
|
|
'sub' => 'username', |
228
|
|
|
'iss' => null, |
229
|
|
|
]; |
230
|
|
|
|
231
|
|
|
$userProvider = $this->createMock(JWTUserProviderInterface::class); |
232
|
|
|
$userProvider |
233
|
|
|
->expects($this->once()) |
234
|
|
|
->method('getDecodedToken') |
235
|
|
|
->willReturn((object) $token); |
236
|
|
|
|
237
|
|
|
$this->jwtAuthenticator->getUser(['jwt' => 'token'], $userProvider); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Test UserProvider with loadByIdentifier metdho |
242
|
|
|
*/ |
243
|
|
|
public function testUserProviderHasLoadMethod(): void |
244
|
|
|
{ |
245
|
|
|
$token = [ |
246
|
|
|
'iss' => 'iss', |
247
|
|
|
'sub' => 'username', |
248
|
|
|
]; |
249
|
|
|
|
250
|
|
|
$tenant = new Tenant(); |
251
|
|
|
|
252
|
|
|
$userProvider = $this->createMock(JWTUserProvider::class); |
253
|
|
|
$userProvider |
254
|
|
|
->expects($this->once()) |
255
|
|
|
->method('getDecodedToken') |
256
|
|
|
->willReturn((object) $token); |
257
|
|
|
|
258
|
|
|
$userProvider |
259
|
|
|
->expects($this->once()) |
260
|
|
|
->method('loadUserByIdentifier') |
261
|
|
|
->with('iss') |
262
|
|
|
->willReturn($tenant); |
263
|
|
|
|
264
|
|
|
$user = $this->jwtAuthenticator->getUser(['jwt' => 'token'], $userProvider); |
265
|
|
|
|
266
|
|
|
$this->assertInstanceOf(Tenant::class, $user); |
267
|
|
|
$this->assertEquals('username', $tenant->getUsername()); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* Test if a user gets fetched |
272
|
|
|
*/ |
273
|
|
|
public function testGetsUser(): void |
274
|
|
|
{ |
275
|
|
|
$token = [ |
276
|
|
|
'iss' => 'iss', |
277
|
|
|
'sub' => 'username', |
278
|
|
|
]; |
279
|
|
|
|
280
|
|
|
$tenant = new Tenant(); |
281
|
|
|
|
282
|
|
|
$userProvider = $this->createMock(JWTUserProviderInterface::class); |
283
|
|
|
$userProvider |
284
|
|
|
->expects($this->once()) |
285
|
|
|
->method('getDecodedToken') |
286
|
|
|
->willReturn((object) $token); |
287
|
|
|
|
288
|
|
|
$userProvider |
289
|
|
|
->expects($this->once()) |
290
|
|
|
->method('loadUserByUsername') |
291
|
|
|
->with('iss') |
292
|
|
|
->willReturn($tenant); |
293
|
|
|
|
294
|
|
|
$user = $this->jwtAuthenticator->getUser(['jwt' => 'token'], $userProvider); |
295
|
|
|
|
296
|
|
|
$this->assertInstanceOf(Tenant::class, $user); |
297
|
|
|
$this->assertEquals('username', $tenant->getUsername()); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* test checkCredentials method |
302
|
|
|
*/ |
303
|
|
|
public function testItChecksCredentials(): void |
304
|
|
|
{ |
305
|
|
|
$this->assertTrue($this->jwtAuthenticator->checkCredentials(null, $this->createMock(UserInterface::class))); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* test onAuthenticationFailure Method |
310
|
|
|
*/ |
311
|
|
|
public function testItSendsAResponseOnAuthenticationFailure(): void |
312
|
|
|
{ |
313
|
|
|
$response = $this->jwtAuthenticator->onAuthenticationFailure(new Request(), new AuthenticationException('Error')); |
314
|
|
|
|
315
|
|
|
$this->assertEquals('Authentication Failed: Error', $response->getContent()); |
316
|
|
|
$this->assertEquals(403, $response->getStatusCode()); |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* test onAuthenticationSuccess method |
321
|
|
|
*/ |
322
|
|
|
public function testItDoesNotSendAResponseOnAuthenticationSuccess(): void |
323
|
|
|
{ |
324
|
|
|
$this->assertNull($this->jwtAuthenticator->onAuthenticationSuccess(new Request(), $this->createMock(TokenInterface::class), 'main')); |
|
|
|
|
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* test supportsRememberMe method |
329
|
|
|
*/ |
330
|
|
|
public function testItDoesNotSupportRememberMeFunctionality(): void |
331
|
|
|
{ |
332
|
|
|
$this->assertFalse($this->jwtAuthenticator->supportsRememberMe()); |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.