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 eac453e  KNOX-2134 - Checking if password/alias is available via local 
alias service before going to fetch it from remote ZK server (#218)
eac453e is described below

commit eac453ecc8f986dd852fc08a65aa2d0b3c08437f
Author: Sandor Molnar <[email protected]>
AuthorDate: Wed Dec 18 08:51:43 2019 +0100

    KNOX-2134 - Checking if password/alias is available via local alias service 
before going to fetch it from remote ZK server (#218)
    
    * KNOX-2134 - Formatted code and did a minor cleanup
    
    * KNOX-2134 - Checking if password/alias is available via local alias 
service before going to fetch it from remote ZK server
---
 .../security/impl/ZookeeperRemoteAliasService.java | 757 ++++++++++-----------
 .../impl/ZookeeperRemoteAliasMonitorTest.java      |   2 +-
 .../impl/ZookeeperRemoteAliasServiceTest.java      |  51 ++
 3 files changed, 404 insertions(+), 406 deletions(-)

diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasService.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasService.java
index cf096ac..35a7324 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasService.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasService.java
@@ -44,461 +44,408 @@ import java.util.Map;
  * An {@link AliasService} implementation based on zookeeper remote service 
registry.
  */
 public class ZookeeperRemoteAliasService implements AliasService {
-  public static final String TYPE = "zookeeper";
-
-  public static final String PATH_KNOX = "/knox";
-  public static final String PATH_KNOX_SECURITY = PATH_KNOX + "/security";
-  public static final String PATH_KNOX_ALIAS_STORE_TOPOLOGY =
-      PATH_KNOX_SECURITY + "/topology";
-  public static final String PATH_SEPARATOR = "/";
-
-  private static final GatewayMessages LOG = 
MessagesFactory.get(GatewayMessages.class);
-  // N.B. This is ZooKeeper-specific, and should be abstracted when another 
registry is supported
-  private static final RemoteConfigurationRegistryClient.EntryACL 
AUTHENTICATED_USERS_ALL =
-      new RemoteConfigurationRegistryClient.EntryACL() {
+    public static final String TYPE = "zookeeper";
+    public static final String PATH_KNOX = "/knox";
+    public static final String PATH_KNOX_SECURITY = PATH_KNOX + "/security";
+    public static final String PATH_KNOX_ALIAS_STORE_TOPOLOGY = 
PATH_KNOX_SECURITY + "/topology";
+    public static final String PATH_SEPARATOR = "/";
+
+    private static final GatewayMessages LOG = 
MessagesFactory.get(GatewayMessages.class);
+    // N.B. This is ZooKeeper-specific, and should be abstracted when another 
registry is supported
+    private static final RemoteConfigurationRegistryClient.EntryACL 
AUTHENTICATED_USERS_ALL = new RemoteConfigurationRegistryClient.EntryACL() {
         @Override
         public String getId() {
-          return "";
+            return "";
         }
 
         @Override
         public String getType() {
-          return "auth";
+            return "auth";
         }
 
         @Override
         public Object getPermissions() {
-          return ZooDefs.Perms.ALL;
+            return ZooDefs.Perms.ALL;
         }
 
         @Override
         public boolean canRead() {
-          return true;
+            return true;
         }
 
         @Override
         public boolean canWrite() {
-          return true;
+            return true;
         }
-      };
-
-  private final AliasService localAliasService;
-  private final MasterService ms;
-  private final RemoteConfigurationRegistryClientService 
remoteConfigurationRegistryClientService;
-
-  private RemoteConfigurationRegistryClient remoteClient;
-  private ConfigurableEncryptor encryptor;
-  private GatewayConfig config;
-
-  ZookeeperRemoteAliasService(AliasService localAliasService, MasterService ms,
-      RemoteConfigurationRegistryClientService 
remoteConfigurationRegistryClientService) {
-    this.localAliasService = localAliasService;
-    this.ms = ms;
-    this.remoteConfigurationRegistryClientService = 
remoteConfigurationRegistryClientService;
-  }
-
-  /**
-   * Build an entry path for the given cluster and alias
-   */
-  private static String buildAliasEntryName(final String clusterName,
-      final String alias) {
-    // Convert all alias names to lower case (JDK-4891485)
-    return buildClusterEntryName(clusterName) + PATH_SEPARATOR + 
alias.toLowerCase(Locale.ROOT);
-  }
-
-  /**
-   * Build an entry path for the given cluster
-   */
-  private static String buildClusterEntryName(final String clusterName) {
-    return PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + clusterName;
-  }
-
-  /**
-   * Ensure that the given entry path exists.
-   */
-  private static void ensureEntry(final String path,
-      final RemoteConfigurationRegistryClient remoteClient) {
-    if (!remoteClient.entryExists(path)) {
-      remoteClient.createEntry(path);
-    } else {
-      // Validate the ACL
-      List<RemoteConfigurationRegistryClient.EntryACL> entryACLs = remoteClient
-          .getACL(path);
-      for (RemoteConfigurationRegistryClient.EntryACL entryACL : entryACLs) {
-        // N.B. This is ZooKeeper-specific, and should be abstracted when 
another registry is supported
-        // For now, check for world:anyone with ANY permissions (even 
read-only)
-        if (entryACL.getType().equals("world") && entryACL.getId()
-            .equals("anyone")) {
-          LOG.suspectWritableRemoteConfigurationEntry(path);
-
-          // If the client is authenticated, but "anyone" can write the 
content, then the content may not
-          // be trustworthy.
-          if (remoteClient.isAuthenticationConfigured()) {
-            LOG.correctingSuspectWritableRemoteConfigurationEntry(path);
-
-            // Replace the existing ACL with one that permits only 
authenticated users
-            remoteClient.setACL(path,
-                Collections.singletonList(AUTHENTICATED_USERS_ALL));
-          }
+    };
+
+    private final AliasService localAliasService;
+    private final MasterService ms;
+    private final RemoteConfigurationRegistryClientService 
remoteConfigurationRegistryClientService;
+
+    private RemoteConfigurationRegistryClient remoteClient;
+    private ConfigurableEncryptor encryptor;
+    private GatewayConfig config;
+
+    ZookeeperRemoteAliasService(AliasService localAliasService, MasterService 
ms, RemoteConfigurationRegistryClientService 
remoteConfigurationRegistryClientService) {
+        this.localAliasService = localAliasService;
+        this.ms = ms;
+        this.remoteConfigurationRegistryClientService = 
remoteConfigurationRegistryClientService;
+    }
+
+    /**
+     * Build an entry path for the given cluster and alias
+     */
+    private static String buildAliasEntryName(final String clusterName, final 
String alias) {
+        // Convert all alias names to lower case (JDK-4891485)
+        return buildClusterEntryName(clusterName) + PATH_SEPARATOR + 
alias.toLowerCase(Locale.ROOT);
+    }
+
+    /**
+     * Build an entry path for the given cluster
+     */
+    private static String buildClusterEntryName(final String clusterName) {
+        return PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + clusterName;
+    }
+
+    /**
+     * Ensure that the given entry path exists.
+     */
+    private static void ensureEntry(final String path, final 
RemoteConfigurationRegistryClient remoteClient) {
+        if (!remoteClient.entryExists(path)) {
+            remoteClient.createEntry(path);
+        } else {
+            // Validate the ACL
+            List<RemoteConfigurationRegistryClient.EntryACL> entryACLs = 
remoteClient.getACL(path);
+            for (RemoteConfigurationRegistryClient.EntryACL entryACL : 
entryACLs) {
+                // N.B. This is ZooKeeper-specific, and should be abstracted 
when another registry is supported
+                // For now, check for world:anyone with ANY permissions (even 
read-only)
+                if ("world".equals(entryACL.getType()) && 
"anyone".equals(entryACL.getId())) {
+                    LOG.suspectWritableRemoteConfigurationEntry(path);
+
+                    // If the client is authenticated, but "anyone" can write 
the content, then the
+                    // content may not be trustworthy.
+                    if (remoteClient.isAuthenticationConfigured()) {
+                        
LOG.correctingSuspectWritableRemoteConfigurationEntry(path);
+
+                        // Replace the existing ACL with one that permits only 
authenticated users
+                        remoteClient.setACL(path, 
Collections.singletonList(AUTHENTICATED_USERS_ALL));
+                    }
+                }
+            }
         }
-      }
     }
-  }
-
-  /**
-   * Check to make sure all the required entries are properly set up
-   */
-  private static void checkPathsExist(
-      final RemoteConfigurationRegistryClient remoteClient) {
-    ensureEntry(PATH_KNOX, remoteClient);
-    ensureEntry(PATH_KNOX_SECURITY, remoteClient);
-    ensureEntry(PATH_KNOX_ALIAS_STORE_TOPOLOGY, remoteClient);
-    ensureEntry(
-        PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + NO_CLUSTER_NAME,
-        remoteClient);
-  }
-
-  /**
-   * Get a list of all aliases for a given cluster.
-   * Remote aliases are preferred over local.
-   *
-   * @param clusterName cluster name
-   * @return List of all the aliases
-   */
-  @Override
-  public List<String> getAliasesForCluster(final String clusterName) throws 
AliasServiceException {
-    List<String> remoteAliases = new ArrayList<>();
-
-    /* If we have remote registry configured, query it */
-    if (remoteClient != null) {
-      remoteAliases = remoteClient
-          .listChildEntries(buildClusterEntryName(clusterName));
+
+    /**
+     * Check to make sure all the required entries are properly set up
+     */
+    private static void checkPathsExist(final 
RemoteConfigurationRegistryClient remoteClient) {
+        ensureEntry(PATH_KNOX, remoteClient);
+        ensureEntry(PATH_KNOX_SECURITY, remoteClient);
+        ensureEntry(PATH_KNOX_ALIAS_STORE_TOPOLOGY, remoteClient);
+        ensureEntry(PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + 
NO_CLUSTER_NAME, remoteClient);
     }
 
-    return remoteAliases;
-  }
-
-  @Override
-  public void addAliasForCluster(final String clusterName,
-      final String alias, final String value)
-      throws AliasServiceException {
-    if (remoteClient != null) {
-      final String aliasEntryPath = buildAliasEntryName(clusterName, alias);
-
-      /* Ensure the entries are properly set up */
-      checkPathsExist(remoteClient);
-      ensureEntry(buildClusterEntryName(clusterName), remoteClient);
-      try {
-        remoteClient.createEntry(aliasEntryPath, encrypt(value));
-      } catch (Exception e) {
-        throw new AliasServiceException(e);
-      }
-
-      if (remoteClient.getEntryData(aliasEntryPath) == null) {
-        throw new IllegalStateException(String.format(Locale.ROOT,
-            "Failed to store alias %s for cluster %s in remote registry", 
alias,
-            clusterName));
-      }
+    /**
+     * Get a list of all aliases for a given cluster. Remote aliases are 
preferred over local.
+     *
+     * @param clusterName
+     *            cluster name
+     * @return List of all the aliases
+     */
+    @Override
+    public List<String> getAliasesForCluster(final String clusterName) throws 
AliasServiceException {
+        final List<String> localAliases = 
localAliasService.getAliasesForCluster(clusterName);
+        if (localAliases == null || localAliases.isEmpty()) {
+          return remoteClient == null ? new ArrayList<>() : 
remoteClient.listChildEntries(buildClusterEntryName(clusterName));
+        } else {
+          return localAliases;
+        }
     }
-  }
-
-  @Override
-  public void removeAliasForCluster(final String clusterName, final String 
alias)
-      throws AliasServiceException {
-    /* If we have remote registry configured, query it */
-    if (remoteClient != null) {
-      final String aliasEntryPath = buildAliasEntryName(clusterName, alias);
-
-      if (remoteClient.entryExists(aliasEntryPath)) {
-        remoteClient.deleteEntry(aliasEntryPath);
-
-        if (remoteClient.entryExists(aliasEntryPath)) {
-          throw new IllegalStateException(String.format(Locale.ROOT,
-              "Failed to delete alias %s for cluster %s in remote registry",
-              alias, clusterName));
+
+    @Override
+    public void addAliasForCluster(final String clusterName, final String 
alias, final String value) throws AliasServiceException {
+        if (remoteClient != null) {
+            final String aliasEntryPath = buildAliasEntryName(clusterName, 
alias);
+
+            /* Ensure the entries are properly set up */
+            checkPathsExist(remoteClient);
+            ensureEntry(buildClusterEntryName(clusterName), remoteClient);
+            try {
+                remoteClient.createEntry(aliasEntryPath, encrypt(value));
+            } catch (Exception e) {
+                throw new AliasServiceException(e);
+            }
+
+            if (remoteClient.getEntryData(aliasEntryPath) == null) {
+                throw new IllegalStateException(String.format(Locale.ROOT, 
"Failed to store alias %s for cluster %s in remote registry", alias, 
clusterName));
+            }
         }
-      }
     }
-  }
 
-  @Override
-  public char[] getPasswordFromAliasForCluster(String clusterName, String 
alias)
-      throws AliasServiceException {
-    return getPasswordFromAliasForCluster(clusterName, alias, false);
-  }
+    @Override
+    public void removeAliasForCluster(final String clusterName, final String 
alias) throws AliasServiceException {
+        /* If we have remote registry configured, query it */
+        if (remoteClient != null) {
+            final String aliasEntryPath = buildAliasEntryName(clusterName, 
alias);
+
+            if (remoteClient.entryExists(aliasEntryPath)) {
+                remoteClient.deleteEntry(aliasEntryPath);
+
+                if (remoteClient.entryExists(aliasEntryPath)) {
+                    throw new IllegalStateException(String.format(Locale.ROOT, 
"Failed to delete alias %s for cluster %s in remote registry", alias, 
clusterName));
+                }
+            }
+        }
+    }
 
-  @Override
-  public char[] getPasswordFromAliasForCluster(String clusterName,
-      String alias, boolean generate) throws AliasServiceException {
+    @Override
+    public char[] getPasswordFromAliasForCluster(String clusterName, String 
alias) throws AliasServiceException {
+        return getPasswordFromAliasForCluster(clusterName, alias, false);
+    }
 
-    char[] password = null;
+    @Override
+    public char[] getPasswordFromAliasForCluster(String clusterName, String 
alias, boolean generate) throws AliasServiceException {
+        char[] password = 
localAliasService.getPasswordFromAliasForCluster(clusterName, alias);
+
+        /* try to get it from remote registry */
+        if (password == null && remoteClient != null) {
+            checkPathsExist(remoteClient);
+            String encrypted = null;
+
+            if (remoteClient.entryExists(buildAliasEntryName(clusterName, 
alias))) {
+                encrypted = 
remoteClient.getEntryData(buildAliasEntryName(clusterName, alias));
+            }
+
+            if (encrypted == null) {
+                if (generate) { /* Generate a new password */
+                    generateAliasForCluster(clusterName, alias);
+                    password = getPasswordFromAliasForCluster(clusterName, 
alias);
+                }
+            } else {
+                try {
+                    password = decrypt(encrypted).toCharArray();
+                } catch (final Exception e) {
+                    throw new AliasServiceException(e);
+                }
+            }
+        }
 
-    /* try to get it from remote registry */
-    if (remoteClient != null) {
-      checkPathsExist(remoteClient);
-      String encrypted = null;
+        return password;
+    }
 
-      if(remoteClient.entryExists(buildAliasEntryName(clusterName, alias))) {
-        encrypted = remoteClient
-            .getEntryData(buildAliasEntryName(clusterName, alias));
-      }
+    @Override
+    public void generateAliasForCluster(final String clusterName, final String 
alias) throws AliasServiceException {
+        /* auto-generated password */
+        final String passwordString = PasswordUtils.generatePassword(16);
+        addAliasForCluster(clusterName, alias, passwordString);
+    }
 
-      /* Generate a new password */
-      if (encrypted == null) {
+    @Override
+    public char[] getPasswordFromAliasForGateway(String alias) throws 
AliasServiceException {
+        return getPasswordFromAliasForCluster(NO_CLUSTER_NAME, alias);
+    }
 
-        /* Generate a new password  */
-        if (generate) {
-          generateAliasForCluster(clusterName, alias);
-          password = getPasswordFromAliasForCluster(clusterName, alias);
-        }
+    @Override
+    public char[] getGatewayIdentityPassphrase() throws AliasServiceException {
+        return 
getPasswordFromAliasForGateway(config.getIdentityKeyPassphraseAlias());
+    }
 
-      } else {
-        try {
-          password = decrypt(encrypted).toCharArray();
-        } catch (final Exception e) {
-          throw new AliasServiceException(e);
-        }
-      }
+    @Override
+    public char[] getGatewayIdentityKeystorePassword() throws 
AliasServiceException {
+        return 
getPasswordFromAliasForGateway(config.getIdentityKeystorePasswordAlias());
     }
 
-    /* found nothing */
-    return password;
-  }
-
-  @Override
-  public void generateAliasForCluster(final String clusterName, final String 
alias)
-      throws AliasServiceException {
-    /* auto-generated password */
-    final String passwordString = PasswordUtils.generatePassword(16);
-    addAliasForCluster(clusterName, alias, passwordString);
-  }
-
-  @Override
-  public char[] getPasswordFromAliasForGateway(String alias)
-      throws AliasServiceException {
-    return getPasswordFromAliasForCluster(NO_CLUSTER_NAME, alias);
-  }
-
-  @Override
-  public char[] getGatewayIdentityPassphrase() throws AliasServiceException {
-    return 
getPasswordFromAliasForGateway(config.getIdentityKeyPassphraseAlias());
-  }
-
-  @Override
-  public char[] getGatewayIdentityKeystorePassword() throws 
AliasServiceException {
-    return 
getPasswordFromAliasForGateway(config.getIdentityKeystorePasswordAlias());
-  }
-
-  @Override
-  public char[] getSigningKeyPassphrase() throws AliasServiceException {
-    return 
getPasswordFromAliasForGateway(config.getSigningKeyPassphraseAlias());
-  }
-
-  @Override
-  public char[] getSigningKeystorePassword() throws AliasServiceException {
-    return 
getPasswordFromAliasForGateway(config.getSigningKeystorePasswordAlias());
-  }
-
-  @Override
-  public void generateAliasForGateway(final String alias) throws 
AliasServiceException {
-    generateAliasForCluster(NO_CLUSTER_NAME, alias);
-  }
-
-  @Override
-  public Certificate getCertificateForGateway(final String alias)
-      throws AliasServiceException {
-    throw new AliasServiceException(new UnsupportedOperationException());
-  }
-
-  @Override
-  public void init(final GatewayConfig config, final Map<String, String> 
options)
-      throws ServiceLifecycleException {
-    this.config = config;
-
-    /* If we have remote registry configured, query it */
-    final String clientName = config.getRemoteConfigurationMonitorClientName();
-    if (clientName != null && remoteConfigurationRegistryClientService != 
null) {
-      remoteClient = remoteConfigurationRegistryClientService.get(clientName);
-
-      /* ensure that nodes are properly setup */
-      ensureEntries(remoteClient);
-
-      /* Confirm access to the remote aliases directory */
-      final List<String> aliases = 
remoteClient.listChildEntries(PATH_KNOX_ALIAS_STORE_TOPOLOGY);
-      if (aliases == null) {
-        // Either the entry does not exist, or there is an authentication 
problem
-        throw new IllegalStateException(
-            "Unable to access remote path: " + PATH_KNOX_ALIAS_STORE_TOPOLOGY);
-      }
-
-      /* Register a listener for aliases entry additions/removals */
-      try {
-        remoteClient.addChildEntryListener(PATH_KNOX_ALIAS_STORE_TOPOLOGY,
-            new RemoteAliasChildListener(this));
-      } catch (final Exception e) {
-        throw new IllegalStateException(
-            "Unable to add listener for path " + 
PATH_KNOX_ALIAS_STORE_TOPOLOGY,
-            e);
-      }
-
-      encryptor = new ConfigurableEncryptor(new String(ms.getMasterSecret()));
-      encryptor.init(config);
-    } else {
-      LOG.missingClientConfigurationForRemoteMonitoring();
+    @Override
+    public char[] getSigningKeyPassphrase() throws AliasServiceException {
+        return 
getPasswordFromAliasForGateway(config.getSigningKeyPassphraseAlias());
     }
-  }
 
-  @Override
-  public void start() throws ServiceLifecycleException {
+    @Override
+    public char[] getSigningKeystorePassword() throws AliasServiceException {
+        return 
getPasswordFromAliasForGateway(config.getSigningKeystorePasswordAlias());
+    }
 
-  }
+    @Override
+    public void generateAliasForGateway(final String alias) throws 
AliasServiceException {
+        generateAliasForCluster(NO_CLUSTER_NAME, alias);
+    }
 
-  @Override
-  public void stop() throws ServiceLifecycleException {
-    if(remoteClient != null) {
-      try {
-        remoteClient.removeEntryListener(PATH_KNOX_ALIAS_STORE_TOPOLOGY);
-      } catch (final Exception e) {
-        LOG.errorRemovingRemoteListener(PATH_KNOX_ALIAS_STORE_TOPOLOGY, 
e.toString());
-      }
+    @Override
+    public Certificate getCertificateForGateway(final String alias) throws 
AliasServiceException {
+        throw new AliasServiceException(new UnsupportedOperationException());
     }
-  }
-
-  /**
-   * Encrypt the clear text with master password.
-   * @param clear clear text to be encrypted
-   * @return encrypted and base 64 encoded result.
-   * @throws Exception exception on failure
-   */
-  String encrypt(final String clear) throws Exception {
-    final EncryptionResult result = encryptor.encrypt(clear);
-
-    return Base64.encodeBase64String(
-        (Base64.encodeBase64String(result.salt) + "::" +
-             Base64.encodeBase64String(result.iv) + "::" +
-             
Base64.encodeBase64String(result.cipher)).getBytes(StandardCharsets.UTF_8));
-  }
-
-  /**
-   * Function to decrypt the encrypted text using master secret.
-   *
-   * @param encoded encoded and encrypted string.
-   * @return decrypted password.
-   * @throws Exception exception on failure
-   */
-  String decrypt(final String encoded) throws Exception {
-    final String line = new String(Base64.decodeBase64(encoded), 
StandardCharsets.UTF_8);
-    final String[] parts = line.split("::");
-    if(parts.length != 3) {
-      throw new IllegalArgumentException("Data should have 3 parts split by 
::");
+
+    @Override
+    public void init(final GatewayConfig config, final Map<String, String> 
options) throws ServiceLifecycleException {
+        this.config = config;
+
+        /* If we have remote registry configured, query it */
+        final String clientName = 
config.getRemoteConfigurationMonitorClientName();
+        if (clientName != null && remoteConfigurationRegistryClientService != 
null) {
+            remoteClient = 
remoteConfigurationRegistryClientService.get(clientName);
+
+            /* ensure that nodes are properly setup */
+            ensureEntries(remoteClient);
+
+            /* Confirm access to the remote aliases directory */
+            final List<String> aliases = 
remoteClient.listChildEntries(PATH_KNOX_ALIAS_STORE_TOPOLOGY);
+            if (aliases == null) {
+                // Either the entry does not exist, or there is an 
authentication problem
+                throw new IllegalStateException("Unable to access remote path: 
" + PATH_KNOX_ALIAS_STORE_TOPOLOGY);
+            }
+
+            /* Register a listener for aliases entry additions/removals */
+            try {
+                
remoteClient.addChildEntryListener(PATH_KNOX_ALIAS_STORE_TOPOLOGY, new 
RemoteAliasChildListener(this));
+            } catch (final Exception e) {
+                throw new IllegalStateException("Unable to add listener for 
path " + PATH_KNOX_ALIAS_STORE_TOPOLOGY, e);
+            }
+
+            encryptor = new ConfigurableEncryptor(new 
String(ms.getMasterSecret()));
+            encryptor.init(config);
+        } else {
+            LOG.missingClientConfigurationForRemoteMonitoring();
+        }
     }
-    return new String(encryptor.decrypt(
-        Base64.decodeBase64(parts[0]),
-        Base64.decodeBase64(parts[1]),
-        Base64.decodeBase64(parts[2])), StandardCharsets.UTF_8);
-  }
-
-  /**
-   * Ensure that the nodes are properly set up.
-   */
-  private void ensureEntries(
-      final RemoteConfigurationRegistryClient remoteClient) {
-    ensureEntry(PATH_KNOX, remoteClient);
-    ensureEntry(PATH_KNOX_SECURITY, remoteClient);
-    ensureEntry(PATH_KNOX_ALIAS_STORE_TOPOLOGY, remoteClient);
-    ensureEntry(
-        PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + NO_CLUSTER_NAME,
-        remoteClient);
-  }
-
-  /**
-   * A listener that listens for changes to the child nodes.
-   */
-  private class RemoteAliasChildListener
-      implements RemoteConfigurationRegistryClient.ChildEntryListener {
-
-    final ZookeeperRemoteAliasService remoteAliasService;
-
-    RemoteAliasChildListener (final ZookeeperRemoteAliasService 
remoteAliasService ) {
-      this.remoteAliasService = remoteAliasService;
+
+    @Override
+    public void start() throws ServiceLifecycleException {
+
     }
 
     @Override
-    public void childEvent(final RemoteConfigurationRegistryClient client,
-        final Type type, final String path) {
-
-      final String subPath = StringUtils.substringAfter(path,
-          PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR);
-      final String[] paths = StringUtils.split(subPath, '/');
-
-      switch (type) {
-      case REMOVED:
-        try {
-          /* remove listener */
-          client.removeEntryListener(path);
-          if (paths.length > 1) {
-            localAliasService.removeAliasForCluster(paths[0], paths[1]);
-          }
-        } catch (final Exception e) {
-          LOG.errorRemovingAliasLocally(paths[0], paths[1], e.toString());
+    public void stop() throws ServiceLifecycleException {
+        if (remoteClient != null) {
+            try {
+                
remoteClient.removeEntryListener(PATH_KNOX_ALIAS_STORE_TOPOLOGY);
+            } catch (final Exception e) {
+                
LOG.errorRemovingRemoteListener(PATH_KNOX_ALIAS_STORE_TOPOLOGY, e.toString());
+            }
         }
-        break;
-
-      case ADDED:
-        /* do not set listeners on cluster name but on respective aliases */
-        if (paths.length > 1) {
-          LOG.addAliasLocally(paths[0], paths[1]);
-          try {
-            client.addEntryListener(path,
-                new RemoteAliasEntryListener(paths[0], paths[1],
-                    remoteAliasService, localAliasService));
-          } catch (final Exception e) {
-            LOG.errorRemovingAliasLocally(paths[0], paths[1], e.toString());
-          }
-        } else if (subPath != null) {
-          /* Add a child listener for the cluster */
-          LOG.addRemoteListener(path);
-          try {
-            client.addChildEntryListener(path, new 
RemoteAliasChildListener(remoteAliasService));
-          } catch (Exception e) {
-            LOG.errorAddingRemoteListener(path, e.toString());
-          }
+    }
+
+    /**
+     * Encrypt the clear text with master password.
+     *
+     * @param clear
+     *            clear text to be encrypted
+     * @return encrypted and base 64 encoded result.
+     * @throws Exception
+     *             exception on failure
+     */
+    String encrypt(final String clear) throws Exception {
+        final EncryptionResult result = encryptor.encrypt(clear);
+
+        return 
Base64.encodeBase64String((Base64.encodeBase64String(result.salt) + "::" + 
Base64.encodeBase64String(result.iv) + "::" + 
Base64.encodeBase64String(result.cipher))
+                .getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * Function to decrypt the encrypted text using master secret.
+     *
+     * @param encoded
+     *            encoded and encrypted string.
+     * @return decrypted password.
+     * @throws Exception
+     *             exception on failure
+     */
+    String decrypt(final String encoded) throws Exception {
+        final String line = new String(Base64.decodeBase64(encoded), 
StandardCharsets.UTF_8);
+        final String[] parts = line.split("::");
+        if (parts.length != 3) {
+            throw new IllegalArgumentException("Data should have 3 parts split 
by ::");
         }
+        return new String(encryptor.decrypt(Base64.decodeBase64(parts[0]), 
Base64.decodeBase64(parts[1]), Base64.decodeBase64(parts[2])), 
StandardCharsets.UTF_8);
+    }
 
-        break;
-      }
+    /**
+     * Ensure that the nodes are properly set up.
+     */
+    private void ensureEntries(final RemoteConfigurationRegistryClient 
remoteClient) {
+        ensureEntry(PATH_KNOX, remoteClient);
+        ensureEntry(PATH_KNOX_SECURITY, remoteClient);
+        ensureEntry(PATH_KNOX_ALIAS_STORE_TOPOLOGY, remoteClient);
+        ensureEntry(PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + 
NO_CLUSTER_NAME, remoteClient);
     }
-  }
-
-  /**
-   * A listener that listens for changes to node value.
-   */
-  private class RemoteAliasEntryListener
-      implements RemoteConfigurationRegistryClient.EntryListener {
-    final String cluster;
-    final String alias;
-    final AliasService remoteAliasService;
-    final AliasService localAliasService;
-
-    RemoteAliasEntryListener(final String cluster, final String alias,
-                             final AliasService remoteAliasService,
-                             final AliasService localAliasService) {
-      this.cluster = cluster;
-      this.alias = alias;
-      this.remoteAliasService = remoteAliasService;
-      this.localAliasService = localAliasService;
+
+    /**
+     * A listener that listens for changes to the child nodes.
+     */
+    private class RemoteAliasChildListener implements 
RemoteConfigurationRegistryClient.ChildEntryListener {
+
+        final ZookeeperRemoteAliasService remoteAliasService;
+
+        RemoteAliasChildListener(final ZookeeperRemoteAliasService 
remoteAliasService) {
+            this.remoteAliasService = remoteAliasService;
+        }
+
+        @Override
+        public void childEvent(final RemoteConfigurationRegistryClient client, 
final Type type, final String path) {
+
+            final String subPath = StringUtils.substringAfter(path, 
PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR);
+            final String[] paths = StringUtils.split(subPath, '/');
+
+            switch (type) {
+            case REMOVED:
+                try {
+                    /* remove listener */
+                    client.removeEntryListener(path);
+                    if (paths.length > 1) {
+                        localAliasService.removeAliasForCluster(paths[0], 
paths[1]);
+                    }
+                } catch (final Exception e) {
+                    LOG.errorRemovingAliasLocally(paths[0], paths[1], 
e.toString());
+                }
+                break;
+
+            case ADDED:
+                /* do not set listeners on cluster name but on respective 
aliases */
+                if (paths.length > 1) {
+                    LOG.addAliasLocally(paths[0], paths[1]);
+                    try {
+                        client.addEntryListener(path, new 
RemoteAliasEntryListener(paths[0], paths[1], localAliasService));
+                    } catch (final Exception e) {
+                        LOG.errorRemovingAliasLocally(paths[0], paths[1], 
e.toString());
+                    }
+                } else if (subPath != null) {
+                    /* Add a child listener for the cluster */
+                    LOG.addRemoteListener(path);
+                    try {
+                        client.addChildEntryListener(path, new 
RemoteAliasChildListener(remoteAliasService));
+                    } catch (Exception e) {
+                        LOG.errorAddingRemoteListener(path, e.toString());
+                    }
+                }
+
+                break;
+            //TODO: UPDATED??
+            }
+        }
     }
 
-    @Override
-    public void entryChanged(final RemoteConfigurationRegistryClient client,
-        final String path, final byte[] data) {
-      try {
-        localAliasService.addAliasForCluster(cluster, alias,
-            decrypt(new String(data, StandardCharsets.UTF_8)));
-      } catch (final Exception e) {
-        /* log and move on */
-        LOG.errorAddingAliasLocally(cluster, alias, e.toString());
-      }
+    /**
+     * A listener that listens for changes to node value.
+     */
+    private class RemoteAliasEntryListener implements 
RemoteConfigurationRegistryClient.EntryListener {
+        final String cluster;
+        final String alias;
+        final AliasService localAliasService;
+
+        RemoteAliasEntryListener(final String cluster, final String alias, 
final AliasService localAliasService) {
+            this.cluster = cluster;
+            this.alias = alias;
+            this.localAliasService = localAliasService;
+        }
+
+        @Override
+        public void entryChanged(final RemoteConfigurationRegistryClient 
client, final String path, final byte[] data) {
+            try {
+                localAliasService.addAliasForCluster(cluster, alias, 
decrypt(new String(data, StandardCharsets.UTF_8)));
+            } catch (final Exception e) {
+                /* log and move on */
+                LOG.errorAddingAliasLocally(cluster, alias, e.toString());
+            }
+        }
     }
-  }
 }
diff --git 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasMonitorTest.java
 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasMonitorTest.java
index 7b190da..da1f059 100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasMonitorTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasMonitorTest.java
@@ -189,7 +189,7 @@ public class ZookeeperRemoteAliasMonitorTest {
         .andReturn(new ArrayList<>()).anyTimes();
 
     
EasyMock.expect(defaultAlias.getPasswordFromAliasForCluster(expectedClusterName,
 preferRemoteAlias))
-        .andReturn("thisiswrong".toCharArray()).anyTimes();
+        .andReturn(preferRemoteAliasClearPassword.toCharArray()).anyTimes();
 
     EasyMock.replay(defaultAlias);
 
diff --git 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasServiceTest.java
 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasServiceTest.java
index d49b0a1..7a2a36e 100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasServiceTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasServiceTest.java
@@ -24,14 +24,20 @@ import 
org.apache.knox.gateway.service.config.remote.zk.ZooKeeperClientService;
 import 
org.apache.knox.gateway.service.config.remote.zk.ZooKeeperClientServiceProvider;
 import 
org.apache.knox.gateway.services.config.client.RemoteConfigurationRegistryClientService;
 import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.MasterService;
 import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -45,6 +51,10 @@ import static org.easymock.EasyMock.capture;
  * Test for {@link ZookeeperRemoteAliasService} backed by Zookeeper.
  */
 public class ZookeeperRemoteAliasServiceTest {
+
+  @ClassRule
+  public static final TemporaryFolder testFolder = new TemporaryFolder();
+
   private static TestingCluster zkNodes;
   private static GatewayConfig gc;
 
@@ -76,6 +86,10 @@ public class ZookeeperRemoteAliasServiceTest {
     EasyMock.expect(gc.isRemoteAliasServiceEnabled())
         .andReturn(true).anyTimes();
 
+    final Path baseFolder = 
Paths.get(testFolder.newFolder().getAbsolutePath());
+    
EasyMock.expect(gc.getGatewayDataDir()).andReturn(Paths.get(baseFolder.toString(),
 "data").toString()).anyTimes();
+    
EasyMock.expect(gc.getGatewayKeystoreDir()).andReturn(Paths.get(baseFolder.toString(),
 "data", "keystores").toString()).anyTimes();
+
     EasyMock.replay(gc);
   }
 
@@ -250,4 +264,41 @@ public class ZookeeperRemoteAliasServiceTest {
       Assert.assertEquals("Data should have 3 parts split by ::", 
e.getMessage());
     }
   }
+
+    @Test
+    @Ignore("should be executed manually in case you'd like to measure how 
much time alias addition/fetch takes")
+    public void testPerformance() throws Exception {
+        final MasterService masterService = 
EasyMock.createNiceMock(MasterService.class);
+        
EasyMock.expect(masterService.getMasterSecret()).andReturn("ThisIsMyM4sterP4sW0r!d".toCharArray()).anyTimes();
+        EasyMock.replay(masterService);
+
+        final DefaultKeystoreService keystoreService = new 
DefaultKeystoreService();
+        keystoreService.init(gc, null);
+        keystoreService.setMasterService(masterService);
+
+        final int rounds = 11;
+        final int numOfAliases = 200;
+        final String cluster = "myTestCluster";
+        for (int round = 0; round < rounds; round++) {
+            //re-creating the alias service every time so that its cache is 
empty too
+            final DefaultAliasService aliasService = new DefaultAliasService();
+            aliasService.init(gc, null);
+            aliasService.setMasterService(masterService);
+            aliasService.setKeystoreService(keystoreService);
+
+            RemoteConfigurationRegistryClientService clientService = (new 
ZooKeeperClientServiceProvider()).newInstance();
+            clientService.setAliasService(aliasService);
+            clientService.init(gc, Collections.emptyMap());
+
+            final ZookeeperRemoteAliasService zkAlias = new 
ZookeeperRemoteAliasService(aliasService, masterService, clientService);
+            zkAlias.init(gc, Collections.emptyMap());
+            zkAlias.start();
+            final long start = System.currentTimeMillis();
+            for (int i = 0; i < numOfAliases; i++) {
+                zkAlias.addAliasForCluster(cluster, "alias" + i, "password" + 
i);
+                Assert.assertEquals("password" + i, new 
String(zkAlias.getPasswordFromAliasForCluster(cluster, "alias" + i)));
+            }
+            System.out.println(System.currentTimeMillis() - start);
+        }
+    }
 }

Reply via email to