|
18 | 18 | import static org.mockito.Mockito.doReturn; |
19 | 19 | import static org.mockito.Mockito.mock; |
20 | 20 | import static org.mockito.Mockito.spy; |
| 21 | +import static org.mockito.Mockito.when; |
21 | 22 |
|
22 | 23 | import java.io.IOException; |
23 | 24 | import java.net.URI; |
|
28 | 29 | import java.time.Duration; |
29 | 30 | import java.util.List; |
30 | 31 | import java.util.Map; |
| 32 | +import java.util.function.Supplier; |
31 | 33 |
|
32 | 34 | import org.junit.jupiter.api.BeforeEach; |
33 | 35 | import org.junit.jupiter.api.Test; |
@@ -401,15 +403,47 @@ void testZeroTrustClientIdentity() |
401 | 403 | { |
402 | 404 | final KeyStore ks = KeyStore.getInstance("JKS"); |
403 | 405 | ks.load(null, null); |
404 | | - ClientIdentity identity = new ZtisClientIdentity("id", ks); |
| 406 | + ClientIdentity identity = new ZtisClientIdentity("id", () -> ks); |
405 | 407 | OAuth2Service service = OAuth2Service.builder().withTokenUri(SERVER_1.baseUrl()).withIdentity(identity).build(); |
406 | 408 |
|
407 | 409 | final OAuth2TokenService result = service.getTokenService(null); |
408 | 410 | assertThat(result).isSameAs(service.getTokenService(null)); |
409 | 411 |
|
410 | | - identity = new ZtisClientIdentity("other-id", ks); |
| 412 | + identity = new ZtisClientIdentity("other-id", () -> ks); |
411 | 413 | service = OAuth2Service.builder().withTokenUri(SERVER_1.baseUrl()).withIdentity(identity).build(); |
412 | 414 |
|
413 | 415 | assertThat(result).isNotSameAs(service.getTokenService(null)); |
414 | 416 | } |
| 417 | + |
| 418 | + @Test |
| 419 | + @SneakyThrows |
| 420 | + void testZeroTrustCertificateRotationCausesCacheMiss() |
| 421 | + { |
| 422 | + // we need to use actual KeyStores here because the code will build an HTTP Client and mocks don't suffice |
| 423 | + final KeyStore ks1 = KeyStore.getInstance("JKS"); |
| 424 | + final KeyStore ks2 = KeyStore.getInstance("JKS"); |
| 425 | + ks1.load(null, null); |
| 426 | + ks2.load(null, null); |
| 427 | + |
| 428 | + assertThat(ks1).describedAs("Sanity check: objects should be different").isNotSameAs(ks2).isNotEqualTo(ks2); |
| 429 | + |
| 430 | + @SuppressWarnings( "unchecked" ) |
| 431 | + final Supplier<KeyStore> mockZtis = mock(Supplier.class); |
| 432 | + when(mockZtis.get()).thenReturn(ks1); |
| 433 | + |
| 434 | + final ZtisClientIdentity identity = new ZtisClientIdentity("id", mockZtis); |
| 435 | + |
| 436 | + final OAuth2Service service = |
| 437 | + OAuth2Service.builder().withTokenUri(SERVER_1.baseUrl()).withIdentity(identity).build(); |
| 438 | + |
| 439 | + // Before rotation: same KeyStore → cache hit |
| 440 | + final OAuth2TokenService tokenService1 = service.getTokenService(null); |
| 441 | + assertThat(tokenService1).isSameAs(service.getTokenService(null)); |
| 442 | + |
| 443 | + when(mockZtis.get()).thenReturn(ks2); |
| 444 | + |
| 445 | + // After rotation: different KeyStore → cache miss → new token service with new certificate |
| 446 | + final OAuth2TokenService tokenService2 = service.getTokenService(null); |
| 447 | + assertThat(tokenService2).isNotSameAs(tokenService1); |
| 448 | + } |
415 | 449 | } |
0 commit comments