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));
     }
   }

Reply via email to