Skip to content

Commit e0c5fa3

Browse files
authored
Fix for ApiKeyIntegTests related to Expired API keys remover (#43477) (#47547) (#47628)
When API key is invalidated we do two things first it tries to trigger `ExpiredApiKeysRemover` task and second, we do index the invalidation for the API key. The index invalidation may happen before the `ExpiredApiKeysRemover` task is run and in that case, the API key invalidated will also get deleted. If the `ExpiredApiKeysRemover` runs before the API key invalidation is indexed then the API key is not deleted and will be deleted in the future run. This behavior was not captured in the tests related to `ExpiredApiKeysRemover` causing intermittent failures. This commit fixes those tests by checking if the API key invalidated is reported back when we get API keys after invalidation and perform the checks based on that. Closes #41747
1 parent 3a95894 commit e0c5fa3

File tree

1 file changed

+39
-11
lines changed

1 file changed

+39
-11
lines changed

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,26 @@ public void testInvalidatedApiKeysDeletedByRemover() throws Exception {
267267
assertThat(invalidateResponse.getPreviouslyInvalidatedApiKeys().size(), equalTo(0));
268268
assertThat(invalidateResponse.getErrors().size(), equalTo(0));
269269

270+
awaitApiKeysRemoverCompletion();
271+
refreshSecurityIndex();
272+
270273
PlainActionFuture<GetApiKeyResponse> getApiKeyResponseListener = new PlainActionFuture<>();
271274
securityClient.getApiKey(GetApiKeyRequest.usingRealmName("file"), getApiKeyResponseListener);
272-
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length, is(2));
275+
Set<String> expectedKeyIds = Sets.newHashSet(createdApiKeys.get(0).getId(), createdApiKeys.get(1).getId());
276+
boolean apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = false;
277+
for (ApiKey apiKey : getApiKeyResponseListener.get().getApiKeyInfos()) {
278+
assertThat(apiKey.getId(), isIn(expectedKeyIds));
279+
if (apiKey.getId().equals(createdApiKeys.get(0).getId())) {
280+
// has been invalidated but not yet deleted by ExpiredApiKeysRemover
281+
assertThat(apiKey.isInvalidated(), is(true));
282+
apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = true;
283+
} else if (apiKey.getId().equals(createdApiKeys.get(1).getId())) {
284+
// active api key
285+
assertThat(apiKey.isInvalidated(), is(false));
286+
}
287+
}
288+
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length,
289+
is((apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover) ? 2 : 1));
273290

274291
client = waitForExpiredApiKeysRemoverTriggerReadyAndGetClient().filterWithHeader(
275292
Collections.singletonMap("Authorization", UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.TEST_SUPERUSER,
@@ -282,16 +299,24 @@ public void testInvalidatedApiKeysDeletedByRemover() throws Exception {
282299
assertThat(listener.get().getInvalidatedApiKeys().size(), is(1));
283300

284301
awaitApiKeysRemoverCompletion();
285-
286302
refreshSecurityIndex();
287303

288-
// Verify that 1st invalidated API key is deleted whereas the next one is not
304+
// Verify that 1st invalidated API key is deleted whereas the next one may be or may not be as it depends on whether update was
305+
// indexed before ExpiredApiKeysRemover ran
289306
getApiKeyResponseListener = new PlainActionFuture<>();
290307
securityClient.getApiKey(GetApiKeyRequest.usingRealmName("file"), getApiKeyResponseListener);
291-
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length, is(1));
292-
ApiKey apiKey = getApiKeyResponseListener.get().getApiKeyInfos()[0];
293-
assertThat(apiKey.getId(), is(createdApiKeys.get(1).getId()));
294-
assertThat(apiKey.isInvalidated(), is(true));
308+
expectedKeyIds = Sets.newHashSet(createdApiKeys.get(1).getId());
309+
apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = false;
310+
for (ApiKey apiKey : getApiKeyResponseListener.get().getApiKeyInfos()) {
311+
assertThat(apiKey.getId(), isIn(expectedKeyIds));
312+
if (apiKey.getId().equals(createdApiKeys.get(1).getId())) {
313+
// has been invalidated but not yet deleted by ExpiredApiKeysRemover
314+
assertThat(apiKey.isInvalidated(), is(true));
315+
apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = true;
316+
}
317+
}
318+
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length,
319+
is((apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover) ? 1 : 0));
295320
}
296321

297322
private Client waitForExpiredApiKeysRemoverTriggerReadyAndGetClient() throws Exception {
@@ -338,7 +363,7 @@ public void testExpiredApiKeysBehaviorWhenKeysExpired1WeekBeforeAnd1DayBefore()
338363
.setDoc("expiration_time", dayBefore.toEpochMilli()).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get();
339364
assertThat(expirationDateUpdatedResponse.getResult(), is(DocWriteResponse.Result.UPDATED));
340365

341-
// Expire the 2nd key such that it cannot be deleted by the remover
366+
// Expire the 2nd key such that it can be deleted by the remover
342367
// hack doc to modify the expiration time to the week before
343368
Instant weekBefore = created.minus(8L, ChronoUnit.DAYS);
344369
assertTrue(Instant.now().isAfter(weekBefore));
@@ -356,23 +381,24 @@ public void testExpiredApiKeysBehaviorWhenKeysExpired1WeekBeforeAnd1DayBefore()
356381

357382
refreshSecurityIndex();
358383

359-
// Verify get API keys does not return expired and deleted key
384+
// Verify get API keys does not return api keys deleted by ExpiredApiKeysRemover
360385
getApiKeyResponseListener = new PlainActionFuture<>();
361386
securityClient.getApiKey(GetApiKeyRequest.usingRealmName("file"), getApiKeyResponseListener);
362-
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length, is(3));
363387

364388
Set<String> expectedKeyIds = Sets.newHashSet(createdApiKeys.get(0).getId(), createdApiKeys.get(2).getId(),
365389
createdApiKeys.get(3).getId());
390+
boolean apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = false;
366391
for (ApiKey apiKey : getApiKeyResponseListener.get().getApiKeyInfos()) {
367392
assertThat(apiKey.getId(), isIn(expectedKeyIds));
368393
if (apiKey.getId().equals(createdApiKeys.get(0).getId())) {
369394
// has been expired, not invalidated
370395
assertTrue(apiKey.getExpiration().isBefore(Instant.now()));
371396
assertThat(apiKey.isInvalidated(), is(false));
372397
} else if (apiKey.getId().equals(createdApiKeys.get(2).getId())) {
373-
// has not been expired as no expiration, but invalidated
398+
// has not been expired as no expiration, is invalidated but not yet deleted by ExpiredApiKeysRemover
374399
assertThat(apiKey.getExpiration(), is(nullValue()));
375400
assertThat(apiKey.isInvalidated(), is(true));
401+
apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = true;
376402
} else if (apiKey.getId().equals(createdApiKeys.get(3).getId())) {
377403
// has not been expired as no expiration, not invalidated
378404
assertThat(apiKey.getExpiration(), is(nullValue()));
@@ -381,6 +407,8 @@ public void testExpiredApiKeysBehaviorWhenKeysExpired1WeekBeforeAnd1DayBefore()
381407
fail("unexpected API key " + apiKey);
382408
}
383409
}
410+
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length,
411+
is((apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover) ? 3 : 2));
384412
}
385413

386414
private void refreshSecurityIndex() throws Exception {

0 commit comments

Comments
 (0)