diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStore.java index dcaa3be5a6dd2..2a9bd40d48925 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStore.java @@ -20,12 +20,17 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.security.action.service.TokenInfo.TokenSource; import org.elasticsearch.xpack.core.security.authc.support.Hasher; -import org.elasticsearch.xpack.core.security.support.CacheIteratorHelper; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; public abstract class CachingServiceAccountTokenStore implements ServiceAccountTokenStore, CacheInvalidatorRegistry.CacheInvalidator { @@ -42,7 +47,6 @@ public abstract class CachingServiceAccountTokenStore implements ServiceAccountT private final Settings settings; private final ThreadPool threadPool; private final Cache> cache; - private CacheIteratorHelper> cacheIteratorHelper; private final Hasher hasher; CachingServiceAccountTokenStore(Settings settings, ThreadPool threadPool) { @@ -54,10 +58,8 @@ public abstract class CachingServiceAccountTokenStore implements ServiceAccountT .setExpireAfterWrite(ttl) .setMaximumWeight(CACHE_MAX_TOKENS_SETTING.get(settings)) .build(); - cacheIteratorHelper = new CacheIteratorHelper<>(cache); } else { cache = null; - cacheIteratorHelper = null; } hasher = Hasher.resolve(CACHE_HASH_ALGO_SETTING.get(settings)); } @@ -126,16 +128,29 @@ private void authenticateWithCache(ServiceAccountToken token, ActionListener qualifiedTokenNames) { if (cache != null) { - logger.trace("invalidating cache for service token [{}]", - Strings.collectionToCommaDelimitedString(qualifiedTokenNames)); - for (String qualifiedTokenName : qualifiedTokenNames) { - if (qualifiedTokenName.endsWith("/")) { - // Wildcard case of invalidating all tokens for a service account, e.g. "elastic/fleet-server/" - cacheIteratorHelper.removeKeysIf(key -> key.startsWith(qualifiedTokenName)); - } else { - cache.invalidate(qualifiedTokenName); + logger.trace("invalidating cache for service token [{}]", Strings.collectionToCommaDelimitedString(qualifiedTokenNames)); + final Set exacts = new HashSet<>(qualifiedTokenNames); + final Set prefixes = new HashSet<>(); + final Iterator it = exacts.iterator(); + while (it.hasNext()) { + final String name = it.next(); + if (name.endsWith("/")) { + prefixes.add(name); + it.remove(); } } + + exacts.forEach(cache::invalidate); + if (false == prefixes.isEmpty()) { + final Predicate predicate = k -> prefixes.stream().anyMatch(k::startsWith); + final List keys = new ArrayList<>(); + cache.forEach((k, v) -> { + if (predicate.test(k)) { + keys.add(k); + } + }); + keys.forEach(cache::invalidate); + } } }