This is an automated email from the ASF dual-hosted git repository. houston pushed a commit to branch add-pki-caching in repository https://gitbox.apache.org/repos/asf/solr.git
commit 48d80c74ce17a22ca5ac0310041b3b3b0b39c324 Author: Houston Putman <[email protected]> AuthorDate: Fri Apr 18 13:14:33 2025 -0500 Add PKI Auth header caching --- .../solr/security/PKIAuthenticationPlugin.java | 41 ++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java index 06c52022c55..1922065fe81 100644 --- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java @@ -18,6 +18,8 @@ package org.apache.solr.security; import static java.nio.charset.StandardCharsets.UTF_8; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.common.annotations.VisibleForTesting; import java.io.IOException; import java.lang.invoke.MethodHandles; @@ -33,6 +35,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -93,6 +96,9 @@ public class PKIAuthenticationPlugin extends AuthenticationPlugin private final Map<String, PublicKey> keyCache = new ConcurrentHashMap<>(); private final PublicKeyHandler publicKeyHandler; private final CoreContainer cores; + private final LoadingCache<String, PKIHeaderData> validatedHeaderCache; + private final LoadingCache<String, String> generatedV1TokenCache; + private final LoadingCache<String, String> generatedV2TokenCache; private static final int MAX_VALIDITY = Integer.getInteger("pkiauth.ttl", 5000); private final String myNodeName; private final HttpHeaderClientInterceptor interceptor = new HttpHeaderClientInterceptor(); @@ -113,6 +119,25 @@ public class PKIAuthenticationPlugin extends AuthenticationPlugin this.cores = cores; myNodeName = nodeName; + // Don't expire after read, because there is no reason to add the overhead of updating expiry + // information after each read. + validatedHeaderCache = + Caffeine.newBuilder() + .maximumSize(1000) + .expireAfterWrite(MAX_VALIDITY, TimeUnit.MILLISECONDS) + .build(this::decipherHeaderV2); + long maxGeneratedCacheTime = Math.min(1000, MAX_VALIDITY / 5); + generatedV1TokenCache = + Caffeine.newBuilder() + .maximumSize(100) + .refreshAfterWrite(maxGeneratedCacheTime, TimeUnit.MILLISECONDS) + .build(this::generateToken); + generatedV2TokenCache = + Caffeine.newBuilder() + .maximumSize(100) + .refreshAfterWrite(maxGeneratedCacheTime, TimeUnit.MILLISECONDS) + .build(this::generateTokenV2); + Set<String> knownPkiVersions = Set.of("v1", "v2"); // We always accept v2 even if it is not specified String[] versions = System.getProperty(ACCEPT_VERSIONS, "v2").split(","); @@ -144,6 +169,7 @@ public class PKIAuthenticationPlugin extends AuthenticationPlugin PKIHeaderData headerData = null; String headerV2 = request.getHeader(HEADER_V2); String headerV1 = request.getHeader(HEADER); + String header; if (headerV1 == null && headerV2 == null) { return sendError(response, true, "No PKI auth header was provided"); } else if (headerV2 != null) { @@ -154,7 +180,7 @@ public class PKIAuthenticationPlugin extends AuthenticationPlugin return sendError(response, true, "Could not parse node name from SolrAuthV2 header."); } - headerData = decipherHeaderV2(headerV2, headerV2.substring(0, nodeNameEnd)); + headerData = validatedHeaderCache.get(headerV2); } else if (headerV1 != null && acceptPkiV1) { List<String> authInfo = StrUtils.splitWS(headerV1, false); if (authInfo.size() != 2) { @@ -222,7 +248,8 @@ public class PKIAuthenticationPlugin extends AuthenticationPlugin return key; } - private PKIHeaderData decipherHeaderV2(String header, String nodeName) { + private PKIHeaderData decipherHeaderV2(String header) { + String nodeName = header.substring(0, header.indexOf(' ')); PublicKey key = getOrFetchPublicKey(nodeName); int sigStart = header.lastIndexOf(' '); @@ -412,11 +439,11 @@ public class PKIAuthenticationPlugin extends AuthenticationPlugin final Optional<String> preFetchedUser = getUserFromJettyRequest(request); if ("v1".equals(System.getProperty(SEND_VERSION))) { preFetchedUser - .map(PKIAuthenticationPlugin.this::generateToken) + .map(generatedV1TokenCache::get) .ifPresent(token -> request.header(HEADER, token)); } else { preFetchedUser - .map(PKIAuthenticationPlugin.this::generateTokenV2) + .map(generatedV2TokenCache::get) .ifPresent(token -> request.header(HEADER_V2, token)); } } @@ -517,10 +544,12 @@ public class PKIAuthenticationPlugin extends AuthenticationPlugin void setHeader(HttpRequest httpRequest) { if ("v1".equals(System.getProperty(SEND_VERSION))) { - getUser().map(this::generateToken).ifPresent(token -> httpRequest.setHeader(HEADER, token)); + getUser() + .map(generatedV1TokenCache::get) + .ifPresent(token -> httpRequest.setHeader(HEADER, token)); } else { getUser() - .map(this::generateTokenV2) + .map(generatedV2TokenCache::get) .ifPresent(token -> httpRequest.setHeader(HEADER_V2, token)); } }
