This is an automated email from the ASF dual-hosted git repository.

smolnar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 4be5227  KNOX-2136 - Caching credentials in DefaultKeystoreService 
when an alias is being added or loaded from keystore and using a different 
cache implementation (#213)
4be5227 is described below

commit 4be5227269695efc862d2876e65f10dc18c40c33
Author: Sandor Molnar <[email protected]>
AuthorDate: Fri Dec 13 07:50:16 2019 +0100

    KNOX-2136 - Caching credentials in DefaultKeystoreService when an alias is 
being added or loaded from keystore and using a different cache implementation 
(#213)
---
 gateway-server/pom.xml                             |  4 ++
 .../security/impl/DefaultKeystoreService.java      | 60 ++++++++++++++--------
 .../security/impl/DefaultKeystoreServiceTest.java  | 33 ++++++++++++
 pom.xml                                            | 16 ++++++
 4 files changed, 91 insertions(+), 22 deletions(-)

diff --git a/gateway-server/pom.xml b/gateway-server/pom.xml
index dd7eb26..2924c22 100644
--- a/gateway-server/pom.xml
+++ b/gateway-server/pom.xml
@@ -371,6 +371,10 @@
             <groupId>org.apache.knox</groupId>
             <artifactId>gateway-util-configinjector</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
 
         <!-- ********** ********** ********** ********** ********** ********** 
-->
         <!-- ********** Test Dependencies                           ********** 
-->
diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
index 6d6445a..a195c1f 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
@@ -19,6 +19,8 @@ package org.apache.knox.gateway.services.security.impl;
 
 import static 
org.apache.knox.gateway.services.security.AliasService.NO_CLUSTER_NAME;
 
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.knox.gateway.GatewayMessages;
 import org.apache.knox.gateway.GatewayResources;
 import org.apache.knox.gateway.config.GatewayConfig;
@@ -31,6 +33,9 @@ import 
org.apache.knox.gateway.services.security.KeystoreServiceException;
 import org.apache.knox.gateway.services.security.MasterService;
 import org.apache.knox.gateway.util.X509CertificateUtil;
 
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -54,10 +59,9 @@ import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.text.MessageFormat;
-import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 
 import javax.crypto.spec.SecretKeySpec;
 
@@ -71,8 +75,9 @@ public class DefaultKeystoreService implements 
KeystoreService, Service {
   private static GatewayMessages LOG = 
MessagesFactory.get(GatewayMessages.class);
   private static GatewayResources RES = 
ResourcesFactory.get(GatewayResources.class);
 
+  //let's configure the cache with hard-coded attributes now; we can introduce 
new gateway configuration later on if needed
+  private final Cache<CacheKey, String> cache = 
Caffeine.newBuilder().expireAfterAccess(10, 
TimeUnit.MINUTES).maximumSize(1000).build();
   private GatewayConfig config;
-  private Map<String, Map<String, String>> cache = new ConcurrentHashMap<>();
 
   private MasterService masterService;
   private Path keyStoreDirPath;
@@ -290,6 +295,7 @@ public class DefaultKeystoreService implements 
KeystoreService, Service {
 
         final Path keyStoreFilePath = keyStoreDirPath.resolve(clusterName + 
CREDENTIALS_SUFFIX);
         writeKeyStoreToFile(ks, keyStoreFilePath, 
masterService.getMasterSecret());
+        addToCache(clusterName, alias, value);
       } catch (KeyStoreException | IOException | CertificateException | 
NoSuchAlgorithmException e) {
         LOG.failedToAddCredentialForCluster(clusterName, e);
       }
@@ -323,7 +329,6 @@ public class DefaultKeystoreService implements 
KeystoreService, Service {
 
   @Override
   public void removeCredentialForCluster(String clusterName, String alias) 
throws KeystoreServiceException {
-    removeFromCache(clusterName, alias);
     KeyStore ks = getCredentialStoreForCluster(clusterName);
     if (ks != null) {
       try {
@@ -333,6 +338,7 @@ public class DefaultKeystoreService implements 
KeystoreService, Service {
 
         final Path keyStoreFilePath = keyStoreDirPath.resolve(clusterName + 
CREDENTIALS_SUFFIX);
         writeKeyStoreToFile(ks, keyStoreFilePath, 
masterService.getMasterSecret());
+        removeFromCache(clusterName, alias);
       } catch (KeyStoreException | IOException | CertificateException | 
NoSuchAlgorithmException e) {
         LOG.failedToRemoveCredentialForCluster(clusterName, e);
       }
@@ -343,36 +349,22 @@ public class DefaultKeystoreService implements 
KeystoreService, Service {
    * Called only from within critical sections of other methods above.
    */
   private char[] checkCache(String clusterName, String alias) {
-    char[] c = null;
-    String cred;
-    Map<String, String> clusterCache = cache.get(clusterName);
-    if (clusterCache == null) {
-      return null;
-    }
-    cred = clusterCache.get(alias);
-    if (cred != null) {
-      c = cred.toCharArray();
-    }
-    return c;
+    final String cachedCredential = 
cache.getIfPresent(CacheKey.of(clusterName, alias));
+    return cachedCredential == null ? null : cachedCredential.toCharArray();
   }
 
   /**
    * Called only from within critical sections of other methods above.
    */
   private void addToCache(String clusterName, String alias, String 
credentialString) {
-    Map<String, String> clusterCache = cache.computeIfAbsent(clusterName, k -> 
new HashMap<>());
-    clusterCache.put(alias, credentialString);
+    cache.put(CacheKey.of(clusterName, alias), credentialString);
   }
 
   /**
    * Called only from within critical sections of other methods above.
    */
   private void removeFromCache(String clusterName, String alias) {
-    Map<String, String> clusterCache = cache.get(clusterName);
-    if (clusterCache == null) {
-      return;
-    }
-    clusterCache.remove(alias);
+    cache.invalidate(CacheKey.of(clusterName, alias));
   }
 
   @Override
@@ -508,4 +500,28 @@ public class DefaultKeystoreService implements 
KeystoreService, Service {
     }
     return (password == null) ? masterService.getMasterSecret() : password;
   }
+
+  private static class CacheKey {
+    private final String clusterName;
+    private final String alias;
+
+    private CacheKey(String clusterName, String alias) {
+      this.clusterName = clusterName;
+      this.alias = alias;
+    }
+
+    private static CacheKey of(String clusterName, String alias) {
+      return new CacheKey(clusterName, alias);
+    }
+
+    @Override
+    public int hashCode() {
+      return HashCodeBuilder.reflectionHashCode(this);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return EqualsBuilder.reflectionEquals(this, obj);
+    }
+  }
 }
diff --git 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreServiceTest.java
 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreServiceTest.java
index f2971f9..b970934 100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreServiceTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreServiceTest.java
@@ -409,6 +409,39 @@ public class DefaultKeystoreServiceTest {
   }
 
   @Test
+  public void testCredentialCache() throws Exception {
+    final String clusterName = "my_cluster";
+    final String credentialAlias = "my_alias";
+    final String credentialValue = "my_value";
+
+    final MasterService masterService = createMock(MasterService.class);
+    
expect(masterService.getMasterSecret()).andReturn("master_password".toCharArray()).anyTimes();
+    replay(masterService);
+
+    final GatewayConfigImpl gatewayConfig = 
createGatewayConfig(testFolder.newFolder().toPath());
+
+    final DefaultKeystoreService keystoreService = new 
DefaultKeystoreService();
+    keystoreService.setMasterService(masterService);
+    keystoreService.init(gatewayConfig, null);
+
+    keystoreService.addCredentialForCluster(clusterName, credentialAlias, 
credentialValue);
+
+    // changing the security folder so that we "invalidate" the previously 
created
+    // keystore and rely on the cache when fetching a credential for an alias
+    gatewayConfig.set(GatewayConfigImpl.SECURITY_DIR, 
gatewayConfig.getGatewaySecurityDir() + "_other");
+    keystoreService.init(gatewayConfig, null);
+
+    // A request for a existing alias should return the expected value from 
cache.
+    // Since the service now has a new security folder set it should return 
'null'
+    // if the cache did not contain the previously added value
+    final char[] keyValue = 
keystoreService.getCredentialForCluster(clusterName, credentialAlias);
+    assertNotNull(keyValue);
+    assertEquals(credentialValue, new String(keyValue));
+
+    verify(masterService);
+  }
+
+  @Test
   public void testAddSelfSignedCertForGatewayLocalhost() throws Exception {
     testAddSelfSignedCertForGateway(null);
   }
diff --git a/pom.xml b/pom.xml
index 2187e7d..2c789c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -154,6 +154,7 @@
         <asm.version>7.2</asm.version>
         <aspectj.version>1.9.4</aspectj.version>
         <bcprov-jdk15on.version>1.64</bcprov-jdk15on.version>
+        <ben-manes.caffeine.version>2.8.0</ben-manes.caffeine.version>
         
<buildnumber-maven-plugin.version>1.4</buildnumber-maven-plugin.version>
         <cglib.version>3.3.0</cglib.version>
         <checkstyle.version>8.26</checkstyle.version>
@@ -2203,6 +2204,21 @@
                 <artifactId>cloudera-manager-api-swagger</artifactId>
                 <version>${cloudera-manager-api-swagger.version}</version>  
<!-- or CM version 6.0 and above -->
             </dependency>
+            <dependency>
+                <groupId>com.github.ben-manes.caffeine</groupId>
+                <artifactId>caffeine</artifactId>
+                <version>${ben-manes.caffeine.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.checkerframework</groupId>
+                        <artifactId>checker-qual</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>com.google.errorprone</groupId>
+                        <artifactId>error_prone_annotations</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
 
             <!-- ********** ********** ********** ********** ********** 
********** -->
             <!-- ********** Test Dependencies                           
********** -->

Reply via email to