Skip to content

Commit e15d2e0

Browse files
authored
Fix for ApiKeyIntegTests related to Expired API keys remover (#43477)
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 fab41a5 commit e15d2e0

File tree

1 file changed

+40
-12
lines changed

1 file changed

+40
-12
lines changed

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

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.elasticsearch.xpack.security.authc;
88

99
import com.google.common.collect.Sets;
10+
1011
import org.elasticsearch.ElasticsearchSecurityException;
1112
import org.elasticsearch.action.DocWriteResponse;
1213
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
@@ -287,9 +288,26 @@ public void testInvalidatedApiKeysDeletedByRemover() throws Exception {
287288
assertThat(invalidateResponse.getPreviouslyInvalidatedApiKeys().size(), equalTo(0));
288289
assertThat(invalidateResponse.getErrors().size(), equalTo(0));
289290

291+
awaitApiKeysRemoverCompletion();
292+
refreshSecurityIndex();
293+
290294
PlainActionFuture<GetApiKeyResponse> getApiKeyResponseListener = new PlainActionFuture<>();
291295
client.execute(GetApiKeyAction.INSTANCE, GetApiKeyRequest.usingRealmName("file"), getApiKeyResponseListener);
292-
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length, is(2));
296+
Set<String> expectedKeyIds = Sets.newHashSet(createdApiKeys.get(0).getId(), createdApiKeys.get(1).getId());
297+
boolean apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = false;
298+
for (ApiKey apiKey : getApiKeyResponseListener.get().getApiKeyInfos()) {
299+
assertThat(apiKey.getId(), isIn(expectedKeyIds));
300+
if (apiKey.getId().equals(createdApiKeys.get(0).getId())) {
301+
// has been invalidated but not yet deleted by ExpiredApiKeysRemover
302+
assertThat(apiKey.isInvalidated(), is(true));
303+
apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = true;
304+
} else if (apiKey.getId().equals(createdApiKeys.get(1).getId())) {
305+
// active api key
306+
assertThat(apiKey.isInvalidated(), is(false));
307+
}
308+
}
309+
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length,
310+
is((apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover) ? 2 : 1));
293311

294312
client = waitForExpiredApiKeysRemoverTriggerReadyAndGetClient().filterWithHeader(
295313
Collections.singletonMap("Authorization", UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.TEST_SUPERUSER,
@@ -302,16 +320,24 @@ public void testInvalidatedApiKeysDeletedByRemover() throws Exception {
302320
assertThat(listener.get().getInvalidatedApiKeys().size(), is(1));
303321

304322
awaitApiKeysRemoverCompletion();
305-
306323
refreshSecurityIndex();
307324

308-
// Verify that 1st invalidated API key is deleted whereas the next one is not
325+
// 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
326+
// indexed before ExpiredApiKeysRemover ran
309327
getApiKeyResponseListener = new PlainActionFuture<>();
310328
client.execute(GetApiKeyAction.INSTANCE, GetApiKeyRequest.usingRealmName("file"), getApiKeyResponseListener);
311-
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length, is(1));
312-
ApiKey apiKey = getApiKeyResponseListener.get().getApiKeyInfos()[0];
313-
assertThat(apiKey.getId(), is(createdApiKeys.get(1).getId()));
314-
assertThat(apiKey.isInvalidated(), is(true));
329+
expectedKeyIds = Sets.newHashSet(createdApiKeys.get(1).getId());
330+
apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = false;
331+
for (ApiKey apiKey : getApiKeyResponseListener.get().getApiKeyInfos()) {
332+
assertThat(apiKey.getId(), isIn(expectedKeyIds));
333+
if (apiKey.getId().equals(createdApiKeys.get(1).getId())) {
334+
// has been invalidated but not yet deleted by ExpiredApiKeysRemover
335+
assertThat(apiKey.isInvalidated(), is(true));
336+
apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = true;
337+
}
338+
}
339+
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length,
340+
is((apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover) ? 1 : 0));
315341
}
316342

317343
private Client waitForExpiredApiKeysRemoverTriggerReadyAndGetClient() throws Exception {
@@ -334,7 +360,6 @@ private Client waitForExpiredApiKeysRemoverTriggerReadyAndGetClient() throws Exc
334360
return internalCluster().client(nodeWithMostRecentRun);
335361
}
336362

337-
@AwaitsFix( bugUrl = "https://github.com/elastic/elasticsearch/issues/41747")
338363
public void testExpiredApiKeysBehaviorWhenKeysExpired1WeekBeforeAnd1DayBefore() throws Exception {
339364
Client client = waitForExpiredApiKeysRemoverTriggerReadyAndGetClient().filterWithHeader(
340365
Collections.singletonMap("Authorization", UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.TEST_SUPERUSER,
@@ -359,7 +384,7 @@ public void testExpiredApiKeysBehaviorWhenKeysExpired1WeekBeforeAnd1DayBefore()
359384
.get();
360385
assertThat(expirationDateUpdatedResponse.getResult(), is(DocWriteResponse.Result.UPDATED));
361386

362-
// Expire the 2nd key such that it cannot be deleted by the remover
387+
// Expire the 2nd key such that it can be deleted by the remover
363388
// hack doc to modify the expiration time to the week before
364389
Instant weekBefore = created.minus(8L, ChronoUnit.DAYS);
365390
assertTrue(Instant.now().isAfter(weekBefore));
@@ -379,23 +404,24 @@ public void testExpiredApiKeysBehaviorWhenKeysExpired1WeekBeforeAnd1DayBefore()
379404

380405
refreshSecurityIndex();
381406

382-
// Verify get API keys does not return expired and deleted key
407+
// Verify get API keys does not return api keys deleted by ExpiredApiKeysRemover
383408
getApiKeyResponseListener = new PlainActionFuture<>();
384409
client.execute(GetApiKeyAction.INSTANCE, GetApiKeyRequest.usingRealmName("file"), getApiKeyResponseListener);
385-
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length, is(3));
386410

387411
Set<String> expectedKeyIds = Sets.newHashSet(createdApiKeys.get(0).getId(), createdApiKeys.get(2).getId(),
388412
createdApiKeys.get(3).getId());
413+
boolean apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = false;
389414
for (ApiKey apiKey : getApiKeyResponseListener.get().getApiKeyInfos()) {
390415
assertThat(apiKey.getId(), isIn(expectedKeyIds));
391416
if (apiKey.getId().equals(createdApiKeys.get(0).getId())) {
392417
// has been expired, not invalidated
393418
assertTrue(apiKey.getExpiration().isBefore(Instant.now()));
394419
assertThat(apiKey.isInvalidated(), is(false));
395420
} else if (apiKey.getId().equals(createdApiKeys.get(2).getId())) {
396-
// has not been expired as no expiration, but invalidated
421+
// has not been expired as no expiration, is invalidated but not yet deleted by ExpiredApiKeysRemover
397422
assertThat(apiKey.getExpiration(), is(nullValue()));
398423
assertThat(apiKey.isInvalidated(), is(true));
424+
apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover = true;
399425
} else if (apiKey.getId().equals(createdApiKeys.get(3).getId())) {
400426
// has not been expired as no expiration, not invalidated
401427
assertThat(apiKey.getExpiration(), is(nullValue()));
@@ -404,6 +430,8 @@ public void testExpiredApiKeysBehaviorWhenKeysExpired1WeekBeforeAnd1DayBefore()
404430
fail("unexpected API key " + apiKey);
405431
}
406432
}
433+
assertThat(getApiKeyResponseListener.get().getApiKeyInfos().length,
434+
is((apiKeyInvalidatedButNotYetDeletedByExpiredApiKeysRemover) ? 3 : 2));
407435
}
408436

409437
private void refreshSecurityIndex() throws Exception {

0 commit comments

Comments
 (0)