You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by mo...@apache.org on 2018/01/11 17:39:08 UTC

[41/53] [abbrv] knox git commit: KNOX-998 - Merge from trunk 0.14.0 code

http://git-wip-us.apache.org/repos/asf/knox/blob/e766b3b7/gateway-test/src/test/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorTest.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorTest.java
deleted file mode 100644
index dd75028..0000000
--- a/gateway-test/src/test/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorTest.java
+++ /dev/null
@@ -1,603 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.hadoop.gateway.topology.monitor;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.framework.CuratorFrameworkFactory;
-import org.apache.curator.retry.ExponentialBackoffRetry;
-import org.apache.curator.test.InstanceSpec;
-import org.apache.curator.test.TestingCluster;
-import org.apache.hadoop.gateway.config.GatewayConfig;
-import org.apache.hadoop.gateway.service.config.remote.zk.ZooKeeperClientService;
-import org.apache.hadoop.gateway.service.config.remote.zk.ZooKeeperClientServiceProvider;
-import org.apache.hadoop.gateway.services.config.client.RemoteConfigurationRegistryClientService;
-import org.apache.hadoop.gateway.services.security.AliasService;
-import org.apache.hadoop.test.TestUtils;
-import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.ZooDefs;
-import org.apache.zookeeper.data.ACL;
-import org.apache.zookeeper.data.Id;
-import org.easymock.EasyMock;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-/**
- * Test the RemoteConfigurationMonitor functionality with SASL configured, and znode ACLs applied.
- *
- * The expected implementation is org.apache.hadoop.gateway.topology.monitor.zk.ZooKeeperConfigMonitor
- *
- * Digest-based SASL is used for this test, but since that is dictated solely by the JAAS config, Kerberos-based SASL
- * should work in exactly the same way, simply by modifying the SASL config.
- */
-public class RemoteConfigurationMonitorTest {
-
-    private static final String PATH_KNOX = "/knox";
-    private static final String PATH_KNOX_CONFIG = PATH_KNOX + "/config";
-    private static final String PATH_KNOX_PROVIDERS = PATH_KNOX_CONFIG + "/shared-providers";
-    private static final String PATH_KNOX_DESCRIPTORS = PATH_KNOX_CONFIG + "/descriptors";
-
-    private static final String PATH_AUTH_TEST = "/auth_test/child_node";
-
-
-    private static final String ALT_USERNAME = "notyou";
-    private static final String ZK_USERNAME = "testsasluser";
-    private static final String ZK_PASSWORD = "testsaslpwd";
-
-    private static final ACL ANY_AUTHENTICATED_USER_ALL = new ACL(ZooDefs.Perms.ALL, new Id("auth", ""));
-    private static final ACL SASL_TESTUSER_ALL = new ACL(ZooDefs.Perms.ALL, new Id("sasl", ZK_USERNAME));
-
-    private static File testTmp;
-    private static File providersDir;
-    private static File descriptorsDir;
-
-    private static TestingCluster zkCluster;
-
-    private static CuratorFramework client;
-
-    @BeforeClass
-    public static void setupSuite() throws Exception {
-        testTmp = TestUtils.createTempDir(RemoteConfigurationMonitorTest.class.getName());
-        File confDir = TestUtils.createTempDir(testTmp + "/conf");
-        providersDir = TestUtils.createTempDir(confDir + "/shared-providers");
-        descriptorsDir = TestUtils.createTempDir(confDir + "/descriptors");
-    }
-
-    @AfterClass
-    public static void tearDownSuite() throws Exception {
-        // Delete the working dir
-        testTmp.delete();
-    }
-
-    @Before
-    public void setupTest() throws Exception {
-        configureAndStartZKCluster();
-    }
-
-    @After
-    public void tearDownTest() throws Exception {
-        // Clean up the ZK nodes, and close the client
-        if (client != null) {
-            if (client.checkExists().forPath(PATH_KNOX) != null) {
-                client.delete().deletingChildrenIfNeeded().forPath(PATH_KNOX);
-            }
-            client.close();
-        }
-
-        // Shutdown the ZK cluster
-        zkCluster.close();
-    }
-
-    /**
-     * Create and persist a JAAS configuration file, defining the SASL config for both the ZooKeeper cluster instances
-     * and ZooKeeper clients.
-     *
-     * @param username The digest username
-     * @param password The digest password
-     *
-     * @return The JAAS configuration file
-     */
-    private static File setupDigestSaslConfig(String username, String password) throws Exception {
-        File saslConfigFile = new File(testTmp, "server-jaas.conf");
-        FileWriter fw = new FileWriter(saslConfigFile);
-        fw.write("Server {\n" +
-                "    org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
-                "    user_" + username + " =\"" + password + "\";\n" +
-                "};\n" +
-                "Client {\n" +
-                "    org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
-                "    username=\"" + username + "\"\n" +
-                "    password=\"" + password + "\";\n" +
-                "};\n");
-        fw.close();
-        return saslConfigFile;
-    }
-
-    /**
-     * Configure and start the ZooKeeper test cluster, and create the znodes monitored by the RemoteConfigurationMonitor.
-     */
-    private static void configureAndStartZKCluster() throws Exception {
-        // Configure security for the ZK cluster instances
-        Map<String, Object> customInstanceSpecProps = new HashMap<>();
-        customInstanceSpecProps.put("authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
-        customInstanceSpecProps.put("requireClientAuthScheme", "sasl");
-
-        // Define the test cluster
-        List<InstanceSpec> instanceSpecs = new ArrayList<>();
-        for (int i = 0 ; i < 3 ; i++) {
-            InstanceSpec is = new InstanceSpec(null, -1, -1, -1, false, (i+1), -1, -1, customInstanceSpecProps);
-            instanceSpecs.add(is);
-        }
-        zkCluster = new TestingCluster(instanceSpecs);
-
-        // Configure auth for the ZooKeeper servers and the clients
-        File saslConfigFile = setupDigestSaslConfig(ZK_USERNAME, ZK_PASSWORD);
-
-        // This system property is used by the ZooKeeper cluster instances, the test driver client, and the
-        // RemoteConfigurationMonitor implementation for SASL authentication/authorization
-        System.setProperty("java.security.auth.login.config", saslConfigFile.getAbsolutePath());
-
-        // Start the cluster
-        zkCluster.start();
-
-        // Create the client for the test cluster
-        client = CuratorFrameworkFactory.builder()
-                                        .connectString(zkCluster.getConnectString())
-                                        .retryPolicy(new ExponentialBackoffRetry(100, 3))
-                                        .build();
-        assertNotNull(client);
-        client.start();
-
-        // Create test config nodes with an ACL for a sasl user that is NOT configured for the test client
-        List<ACL> acls = Arrays.asList(new ACL(ZooDefs.Perms.ALL, new Id("sasl", ALT_USERNAME)),
-                                       new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE));
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_AUTH_TEST);
-        assertNotNull("Failed to create node:" + PATH_AUTH_TEST,
-                      client.checkExists().forPath(PATH_AUTH_TEST));
-    }
-
-
-    private static void validateKnoxConfigNodeACLs(List<ACL> expectedACLS, List<ACL> actualACLs) throws Exception {
-        assertEquals(expectedACLS.size(), actualACLs.size());
-        int matchedCount = 0;
-        for (ACL expected : expectedACLS) {
-            for (ACL actual : actualACLs) {
-                Id expectedId = expected.getId();
-                Id actualId = actual.getId();
-                if (actualId.getScheme().equals(expectedId.getScheme()) && actualId.getId().equals(expectedId.getId())) {
-                    matchedCount++;
-                    assertEquals(expected.getPerms(), actual.getPerms());
-                    break;
-                }
-            }
-        }
-        assertEquals("ACL mismatch despite being same quantity.", expectedACLS.size(), matchedCount);
-    }
-
-
-    @Test
-    public void testZooKeeperConfigMonitorSASLNodesExistWithUnacceptableACL() throws Exception {
-        final String configMonitorName = "zkConfigClient";
-        final String alias = "zkPass";
-
-        // Setup the base GatewayConfig mock
-        GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
-        EasyMock.expect(gc.getGatewayProvidersConfigDir()).andReturn(providersDir.getAbsolutePath()).anyTimes();
-        EasyMock.expect(gc.getGatewayDescriptorsDir()).andReturn(descriptorsDir.getAbsolutePath()).anyTimes();
-        EasyMock.expect(gc.getRemoteRegistryConfigurationNames())
-                .andReturn(Collections.singletonList(configMonitorName))
-                .anyTimes();
-        final String registryConfig =
-                GatewayConfig.REMOTE_CONFIG_REGISTRY_TYPE + "=" + ZooKeeperClientService.TYPE + ";" +
-                        GatewayConfig.REMOTE_CONFIG_REGISTRY_ADDRESS + "=" + zkCluster.getConnectString() + ";" +
-                        GatewayConfig.REMOTE_CONFIG_REGISTRY_PRINCIPAL + "=" + ZK_USERNAME + ";" +
-                        GatewayConfig.REMOTE_CONFIG_REGISTRY_AUTH_TYPE + "=Digest;" +
-                        GatewayConfig.REMOTE_CONFIG_REGISTRY_CREDENTIAL_ALIAS + "=" + alias;
-        EasyMock.expect(gc.getRemoteRegistryConfiguration(configMonitorName))
-                .andReturn(registryConfig).anyTimes();
-        EasyMock.expect(gc.getRemoteConfigurationMonitorClientName()).andReturn(configMonitorName).anyTimes();
-        EasyMock.replay(gc);
-
-        AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
-        EasyMock.expect(aliasService.getPasswordFromAliasForGateway(alias))
-                .andReturn(ZK_PASSWORD.toCharArray())
-                .anyTimes();
-        EasyMock.replay(aliasService);
-
-        RemoteConfigurationRegistryClientService clientService = (new ZooKeeperClientServiceProvider()).newInstance();
-        clientService.setAliasService(aliasService);
-        clientService.init(gc, Collections.emptyMap());
-        clientService.start();
-
-        RemoteConfigurationMonitorFactory.setClientService(clientService);
-
-        RemoteConfigurationMonitor cm = RemoteConfigurationMonitorFactory.get(gc);
-        assertNotNull("Failed to load RemoteConfigurationMonitor", cm);
-
-        final ACL ANY_AUTHENTICATED_USER_ALL = new ACL(ZooDefs.Perms.ALL, new Id("auth", ""));
-        List<ACL> acls = Arrays.asList(ANY_AUTHENTICATED_USER_ALL, new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.ANYONE_ID_UNSAFE));
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX);
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_CONFIG);
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_PROVIDERS);
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_DESCRIPTORS);
-
-        // Make sure both ACLs were applied
-        List<ACL> preACLs = client.getACL().forPath(PATH_KNOX);
-        assertEquals(2, preACLs.size());
-
-        // Check that the config nodes really do exist (the monitor will NOT create them if they're present)
-        assertNotNull(client.checkExists().forPath(PATH_KNOX));
-        assertNotNull(client.checkExists().forPath(PATH_KNOX_CONFIG));
-        assertNotNull(client.checkExists().forPath(PATH_KNOX_PROVIDERS));
-        assertNotNull(client.checkExists().forPath(PATH_KNOX_DESCRIPTORS));
-
-        try {
-            cm.start();
-        } catch (Exception e) {
-            fail("Failed to start monitor: " + e.getMessage());
-        }
-
-        // Validate the expected ACLs on the Knox config znodes (make sure the monitor removed the world:anyone ACL)
-        List<ACL> expectedACLs = Collections.singletonList(SASL_TESTUSER_ALL);
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX));
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_CONFIG));
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_PROVIDERS));
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_DESCRIPTORS));
-    }
-
-
-    @Test
-    public void testZooKeeperConfigMonitorSASLNodesExistWithAcceptableACL() throws Exception {
-        final String configMonitorName = "zkConfigClient";
-        final String alias = "zkPass";
-
-        // Setup the base GatewayConfig mock
-        GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
-        EasyMock.expect(gc.getGatewayProvidersConfigDir()).andReturn(providersDir.getAbsolutePath()).anyTimes();
-        EasyMock.expect(gc.getGatewayDescriptorsDir()).andReturn(descriptorsDir.getAbsolutePath()).anyTimes();
-        EasyMock.expect(gc.getRemoteRegistryConfigurationNames())
-                .andReturn(Collections.singletonList(configMonitorName))
-                .anyTimes();
-        final String registryConfig =
-                GatewayConfig.REMOTE_CONFIG_REGISTRY_TYPE + "=" + ZooKeeperClientService.TYPE + ";" +
-                        GatewayConfig.REMOTE_CONFIG_REGISTRY_ADDRESS + "=" + zkCluster.getConnectString() + ";" +
-                        GatewayConfig.REMOTE_CONFIG_REGISTRY_PRINCIPAL + "=" + ZK_USERNAME + ";" +
-                        GatewayConfig.REMOTE_CONFIG_REGISTRY_AUTH_TYPE + "=Digest;" +
-                        GatewayConfig.REMOTE_CONFIG_REGISTRY_CREDENTIAL_ALIAS + "=" + alias;
-        EasyMock.expect(gc.getRemoteRegistryConfiguration(configMonitorName))
-                .andReturn(registryConfig).anyTimes();
-        EasyMock.expect(gc.getRemoteConfigurationMonitorClientName()).andReturn(configMonitorName).anyTimes();
-        EasyMock.replay(gc);
-
-        AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
-        EasyMock.expect(aliasService.getPasswordFromAliasForGateway(alias))
-                .andReturn(ZK_PASSWORD.toCharArray())
-                .anyTimes();
-        EasyMock.replay(aliasService);
-
-        RemoteConfigurationRegistryClientService clientService = (new ZooKeeperClientServiceProvider()).newInstance();
-        clientService.setAliasService(aliasService);
-        clientService.init(gc, Collections.emptyMap());
-        clientService.start();
-
-        RemoteConfigurationMonitorFactory.setClientService(clientService);
-
-        RemoteConfigurationMonitor cm = RemoteConfigurationMonitorFactory.get(gc);
-        assertNotNull("Failed to load RemoteConfigurationMonitor", cm);
-
-        List<ACL> acls = Arrays.asList(ANY_AUTHENTICATED_USER_ALL);
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX);
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_CONFIG);
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_PROVIDERS);
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_DESCRIPTORS);
-
-        // Check that the config nodes really do exist (the monitor will NOT create them if they're present)
-        assertNotNull(client.checkExists().forPath(PATH_KNOX));
-        assertNotNull(client.checkExists().forPath(PATH_KNOX_CONFIG));
-        assertNotNull(client.checkExists().forPath(PATH_KNOX_PROVIDERS));
-        assertNotNull(client.checkExists().forPath(PATH_KNOX_DESCRIPTORS));
-
-        try {
-            cm.start();
-        } catch (Exception e) {
-            fail("Failed to start monitor: " + e.getMessage());
-        }
-
-        // Test auth violation
-        clientService.get(configMonitorName).createEntry("/auth_test/child_node/test1");
-        assertNull("Creation should have been prevented since write access is not granted to the test client.",
-                client.checkExists().forPath("/auth_test/child_node/test1"));
-        assertTrue("Creation should have been prevented since write access is not granted to the test client.",
-                client.getChildren().forPath("/auth_test/child_node").isEmpty());
-
-        // Validate the expected ACLs on the Knox config znodes (make sure the monitor didn't change them)
-        List<ACL> expectedACLs = Collections.singletonList(SASL_TESTUSER_ALL);
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX));
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_CONFIG));
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_PROVIDERS));
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_DESCRIPTORS));
-    }
-
-
-    @Test
-    public void testZooKeeperConfigMonitorSASLCreateNodes() throws Exception {
-        final String configMonitorName = "zkConfigClient";
-        final String alias = "zkPass";
-
-        // Setup the base GatewayConfig mock
-        GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
-        EasyMock.expect(gc.getGatewayProvidersConfigDir()).andReturn(providersDir.getAbsolutePath()).anyTimes();
-        EasyMock.expect(gc.getGatewayDescriptorsDir()).andReturn(descriptorsDir.getAbsolutePath()).anyTimes();
-        EasyMock.expect(gc.getRemoteRegistryConfigurationNames())
-                .andReturn(Collections.singletonList(configMonitorName))
-                .anyTimes();
-        final String registryConfig =
-                            GatewayConfig.REMOTE_CONFIG_REGISTRY_TYPE + "=" + ZooKeeperClientService.TYPE + ";" +
-                            GatewayConfig.REMOTE_CONFIG_REGISTRY_ADDRESS + "=" + zkCluster.getConnectString() + ";" +
-                            GatewayConfig.REMOTE_CONFIG_REGISTRY_PRINCIPAL + "=" + ZK_USERNAME + ";" +
-                            GatewayConfig.REMOTE_CONFIG_REGISTRY_AUTH_TYPE + "=Digest;" +
-                            GatewayConfig.REMOTE_CONFIG_REGISTRY_CREDENTIAL_ALIAS + "=" + alias;
-        EasyMock.expect(gc.getRemoteRegistryConfiguration(configMonitorName))
-                .andReturn(registryConfig).anyTimes();
-        EasyMock.expect(gc.getRemoteConfigurationMonitorClientName()).andReturn(configMonitorName).anyTimes();
-        EasyMock.replay(gc);
-
-        AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
-        EasyMock.expect(aliasService.getPasswordFromAliasForGateway(alias))
-                .andReturn(ZK_PASSWORD.toCharArray())
-                .anyTimes();
-        EasyMock.replay(aliasService);
-
-        RemoteConfigurationRegistryClientService clientService = (new ZooKeeperClientServiceProvider()).newInstance();
-        clientService.setAliasService(aliasService);
-        clientService.init(gc, Collections.emptyMap());
-        clientService.start();
-
-        RemoteConfigurationMonitorFactory.setClientService(clientService);
-
-        RemoteConfigurationMonitor cm = RemoteConfigurationMonitorFactory.get(gc);
-        assertNotNull("Failed to load RemoteConfigurationMonitor", cm);
-
-        // Check that the config nodes really don't yet exist (the monitor will create them if they're not present)
-        assertNull(client.checkExists().forPath(PATH_KNOX));
-        assertNull(client.checkExists().forPath(PATH_KNOX_CONFIG));
-        assertNull(client.checkExists().forPath(PATH_KNOX_PROVIDERS));
-        assertNull(client.checkExists().forPath(PATH_KNOX_DESCRIPTORS));
-
-        try {
-            cm.start();
-        } catch (Exception e) {
-            fail("Failed to start monitor: " + e.getMessage());
-        }
-
-        // Test auth violation
-        clientService.get(configMonitorName).createEntry("/auth_test/child_node/test1");
-        assertNull("Creation should have been prevented since write access is not granted to the test client.",
-                   client.checkExists().forPath("/auth_test/child_node/test1"));
-        assertTrue("Creation should have been prevented since write access is not granted to the test client.",
-                   client.getChildren().forPath("/auth_test/child_node").isEmpty());
-
-        // Validate the expected ACLs on the Knox config znodes (make sure the monitor created them correctly)
-        List<ACL> expectedACLs = Collections.singletonList(SASL_TESTUSER_ALL);
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX));
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_CONFIG));
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_PROVIDERS));
-        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_DESCRIPTORS));
-
-        // Test the Knox config nodes, for which authentication should be sufficient for access
-        try {
-            final String pc_one_znode = getProviderPath("providers-config1.xml");
-            final File pc_one         = new File(providersDir, "providers-config1.xml");
-            final String pc_two_znode = getProviderPath("providers-config2.xml");
-            final File pc_two         = new File(providersDir, "providers-config2.xml");
-
-            client.create().withMode(CreateMode.PERSISTENT).forPath(pc_one_znode, TEST_PROVIDERS_CONFIG_1.getBytes());
-            Thread.sleep(100);
-            assertTrue(pc_one.exists());
-            assertEquals(TEST_PROVIDERS_CONFIG_1, FileUtils.readFileToString(pc_one));
-
-            client.create().withMode(CreateMode.PERSISTENT).forPath(getProviderPath("providers-config2.xml"), TEST_PROVIDERS_CONFIG_2.getBytes());
-            Thread.sleep(100);
-            assertTrue(pc_two.exists());
-            assertEquals(TEST_PROVIDERS_CONFIG_2, FileUtils.readFileToString(pc_two));
-
-            client.setData().forPath(pc_two_znode, TEST_PROVIDERS_CONFIG_1.getBytes());
-            Thread.sleep(100);
-            assertTrue(pc_two.exists());
-            assertEquals(TEST_PROVIDERS_CONFIG_1, FileUtils.readFileToString(pc_two));
-
-            client.delete().forPath(pc_two_znode);
-            Thread.sleep(100);
-            assertFalse(pc_two.exists());
-
-            client.delete().forPath(pc_one_znode);
-            Thread.sleep(100);
-            assertFalse(pc_one.exists());
-
-            final String desc_one_znode   = getDescriptorPath("test1.json");
-            final String desc_two_znode   = getDescriptorPath("test2.json");
-            final String desc_three_znode = getDescriptorPath("test3.json");
-            final File desc_one           = new File(descriptorsDir, "test1.json");
-            final File desc_two           = new File(descriptorsDir, "test2.json");
-            final File desc_three         = new File(descriptorsDir, "test3.json");
-
-            client.create().withMode(CreateMode.PERSISTENT).forPath(desc_one_znode, TEST_DESCRIPTOR_1.getBytes());
-            Thread.sleep(100);
-            assertTrue(desc_one.exists());
-            assertEquals(TEST_DESCRIPTOR_1, FileUtils.readFileToString(desc_one));
-
-            client.create().withMode(CreateMode.PERSISTENT).forPath(desc_two_znode, TEST_DESCRIPTOR_1.getBytes());
-            Thread.sleep(100);
-            assertTrue(desc_two.exists());
-            assertEquals(TEST_DESCRIPTOR_1, FileUtils.readFileToString(desc_two));
-
-            client.setData().forPath(desc_two_znode, TEST_DESCRIPTOR_2.getBytes());
-            Thread.sleep(100);
-            assertTrue(desc_two.exists());
-            assertEquals(TEST_DESCRIPTOR_2, FileUtils.readFileToString(desc_two));
-
-            client.create().withMode(CreateMode.PERSISTENT).forPath(desc_three_znode, TEST_DESCRIPTOR_1.getBytes());
-            Thread.sleep(100);
-            assertTrue(desc_three.exists());
-            assertEquals(TEST_DESCRIPTOR_1, FileUtils.readFileToString(desc_three));
-
-            client.delete().forPath(desc_two_znode);
-            Thread.sleep(100);
-            assertFalse("Expected test2.json to have been deleted.", desc_two.exists());
-
-            client.delete().forPath(desc_three_znode);
-            Thread.sleep(100);
-            assertFalse(desc_three.exists());
-
-            client.delete().forPath(desc_one_znode);
-            Thread.sleep(100);
-            assertFalse(desc_one.exists());
-        } finally {
-            cm.stop();
-        }
-    }
-
-    private static String getDescriptorPath(String descriptorName) {
-        return PATH_KNOX_DESCRIPTORS + "/" + descriptorName;
-    }
-
-    private static String getProviderPath(String providerConfigName) {
-        return PATH_KNOX_PROVIDERS + "/" + providerConfigName;
-    }
-
-
-    private static final String TEST_PROVIDERS_CONFIG_1 =
-                    "<gateway>\n" +
-                    "    <provider>\n" +
-                    "        <role>identity-assertion</role>\n" +
-                    "        <name>Default</name>\n" +
-                    "        <enabled>true</enabled>\n" +
-                    "    </provider>\n" +
-                    "    <provider>\n" +
-                    "        <role>hostmap</role>\n" +
-                    "        <name>static</name>\n" +
-                    "        <enabled>true</enabled>\n" +
-                    "        <param><name>localhost</name><value>sandbox,sandbox.hortonworks.com</value></param>\n" +
-                    "    </provider>\n" +
-                    "</gateway>\n";
-
-    private static final String TEST_PROVIDERS_CONFIG_2 =
-                    "<gateway>\n" +
-                    "    <provider>\n" +
-                    "        <role>authentication</role>\n" +
-                    "        <name>ShiroProvider</name>\n" +
-                    "        <enabled>true</enabled>\n" +
-                    "        <param>\n" +
-                    "            <name>sessionTimeout</name>\n" +
-                    "            <value>30</value>\n" +
-                    "        </param>\n" +
-                    "        <param>\n" +
-                    "            <name>main.ldapRealm</name>\n" +
-                    "            <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>\n" +
-                    "        </param>\n" +
-                    "        <param>\n" +
-                    "            <name>main.ldapContextFactory</name>\n" +
-                    "            <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapContextFactory</value>\n" +
-                    "        </param>\n" +
-                    "        <param>\n" +
-                    "            <name>main.ldapRealm.contextFactory</name>\n" +
-                    "            <value>$ldapContextFactory</value>\n" +
-                    "        </param>\n" +
-                    "        <param>\n" +
-                    "            <name>main.ldapRealm.userDnTemplate</name>\n" +
-                    "            <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>\n" +
-                    "        </param>\n" +
-                    "        <param>\n" +
-                    "            <name>main.ldapRealm.contextFactory.url</name>\n" +
-                    "            <value>ldap://localhost:33389</value>\n" +
-                    "        </param>\n" +
-                    "        <param>\n" +
-                    "            <name>main.ldapRealm.contextFactory.authenticationMechanism</name>\n" +
-                    "            <value>simple</value>\n" +
-                    "        </param>\n" +
-                    "        <param>\n" +
-                    "            <name>urls./**</name>\n" +
-                    "            <value>authcBasic</value>\n" +
-                    "        </param>\n" +
-                    "    </provider>\n" +
-                    "</gateway>\n";
-
-    private static final String TEST_DESCRIPTOR_1 =
-                    "{\n" +
-                    "  \"discovery-type\":\"AMBARI\",\n" +
-                    "  \"discovery-address\":\"http://sandbox.hortonworks.com:8080\",\n" +
-                    "  \"discovery-user\":\"maria_dev\",\n" +
-                    "  \"discovery-pwd-alias\":\"sandbox.ambari.discovery.password\",\n" +
-                    "  \"provider-config-ref\":\"sandbox-providers.xml\",\n" +
-                    "  \"cluster\":\"Sandbox\",\n" +
-                    "  \"services\":[\n" +
-                    "    {\"name\":\"NODEUI\"},\n" +
-                    "    {\"name\":\"YARNUI\"},\n" +
-                    "    {\"name\":\"HDFSUI\"},\n" +
-                    "    {\"name\":\"OOZIEUI\"},\n" +
-                    "    {\"name\":\"HBASEUI\"},\n" +
-                    "    {\"name\":\"NAMENODE\"},\n" +
-                    "    {\"name\":\"JOBTRACKER\"},\n" +
-                    "    {\"name\":\"WEBHDFS\"},\n" +
-                    "    {\"name\":\"WEBHCAT\"},\n" +
-                    "    {\"name\":\"OOZIE\"},\n" +
-                    "    {\"name\":\"WEBHBASE\"},\n" +
-                    "    {\"name\":\"RESOURCEMANAGER\"},\n" +
-                    "    {\"name\":\"AMBARI\", \"urls\":[\"http://c6401.ambari.apache.org:8080\"]},\n" +
-                    "    {\"name\":\"AMBARIUI\", \"urls\":[\"http://c6401.ambari.apache.org:8080\"]}\n" +
-                    "  ]\n" +
-                    "}\n";
-
-    private static final String TEST_DESCRIPTOR_2 =
-                    "{\n" +
-                    "  \"discovery-type\":\"AMBARI\",\n" +
-                    "  \"discovery-address\":\"http://sandbox.hortonworks.com:8080\",\n" +
-                    "  \"discovery-user\":\"maria_dev\",\n" +
-                    "  \"discovery-pwd-alias\":\"sandbox.ambari.discovery.password\",\n" +
-                    "  \"provider-config-ref\":\"sandbox-providers.xml\",\n" +
-                    "  \"cluster\":\"Sandbox\",\n" +
-                    "  \"services\":[\n" +
-                    "    {\"name\":\"NAMENODE\"},\n" +
-                    "    {\"name\":\"JOBTRACKER\"},\n" +
-                    "    {\"name\":\"WEBHDFS\"},\n" +
-                    "    {\"name\":\"WEBHCAT\"},\n" +
-                    "    {\"name\":\"OOZIE\"},\n" +
-                    "    {\"name\":\"WEBHBASE\"},\n" +
-                    "    {\"name\":\"RESOURCEMANAGER\"}\n" +
-                    "  ]\n" +
-                    "}\n";
-
-}

http://git-wip-us.apache.org/repos/asf/knox/blob/e766b3b7/gateway-test/src/test/java/org/apache/knox/gateway/SimpleDescriptorHandlerFuncTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/knox/gateway/SimpleDescriptorHandlerFuncTest.java b/gateway-test/src/test/java/org/apache/knox/gateway/SimpleDescriptorHandlerFuncTest.java
new file mode 100644
index 0000000..5b29e19
--- /dev/null
+++ b/gateway-test/src/test/java/org/apache/knox/gateway/SimpleDescriptorHandlerFuncTest.java
@@ -0,0 +1,275 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.knox.gateway;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.GatewayServices;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.KeystoreService;
+import org.apache.knox.gateway.services.security.MasterService;
+import org.apache.knox.gateway.services.topology.TopologyService;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryConfig;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryType;
+import org.apache.knox.gateway.topology.simple.SimpleDescriptor;
+import org.apache.knox.gateway.topology.simple.SimpleDescriptorHandler;
+import org.apache.knox.test.TestUtils;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import java.io.File;
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.capture;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class SimpleDescriptorHandlerFuncTest {
+
+
+  private static final String TEST_PROVIDER_CONFIG =
+      "    <gateway>\n" +
+          "        <provider>\n" +
+          "            <role>authentication</role>\n" +
+          "            <name>ShiroProvider</name>\n" +
+          "            <enabled>true</enabled>\n" +
+          "            <param>\n" +
+          "                <name>sessionTimeout</name>\n" +
+          "                <value>30</value>\n" +
+          "            </param>\n" +
+          "            <param>\n" +
+          "                <name>main.ldapRealm</name>\n" +
+          "                <value>org.apache.knox.gateway.shirorealm.KnoxLdapRealm</value>\n" +
+          "            </param>\n" +
+          "            <param>\n" +
+          "                <name>main.ldapContextFactory</name>\n" +
+          "                <value>org.apache.knox.gateway.shirorealm.KnoxLdapContextFactory</value>\n" +
+          "            </param>\n" +
+          "            <param>\n" +
+          "                <name>main.ldapRealm.contextFactory</name>\n" +
+          "                <value>$ldapContextFactory</value>\n" +
+          "            </param>\n" +
+          "            <param>\n" +
+          "                <name>main.ldapRealm.userDnTemplate</name>\n" +
+          "                <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>\n" +
+          "            </param>\n" +
+          "            <param>\n" +
+          "                <name>main.ldapRealm.contextFactory.url</name>\n" +
+          "                <value>ldap://localhost:33389</value>\n" +
+          "            </param>\n" +
+          "            <param>\n" +
+          "                <name>main.ldapRealm.contextFactory.authenticationMechanism</name>\n" +
+          "                <value>simple</value>\n" +
+          "            </param>\n" +
+          "            <param>\n" +
+          "                <name>urls./**</name>\n" +
+          "                <value>authcBasic</value>\n" +
+          "            </param>\n" +
+          "        </provider>\n" +
+          "\n" +
+          "        <provider>\n" +
+          "            <role>identity-assertion</role>\n" +
+          "            <name>Default</name>\n" +
+          "            <enabled>true</enabled>\n" +
+          "        </provider>\n" +
+          "\n" +
+          "        <provider>\n" +
+          "            <role>hostmap</role>\n" +
+          "            <name>static</name>\n" +
+          "            <enabled>true</enabled>\n" +
+          "            <param><name>localhost</name><value>sandbox,sandbox.hortonworks.com</value></param>\n" +
+          "        </provider>\n" +
+          "    </gateway>\n";
+
+
+  /**
+   * KNOX-1136
+   * <p>
+   * Test that a credential store is created, and a encryptQueryString alias is defined, with a password that is not
+   * random (but is derived from the master secret and the topology name).
+   * <p>
+   * N.B. This test depends on the NoOpServiceDiscovery extension being configured in META-INF/services
+   */
+  @Test
+  public void testSimpleDescriptorHandlerQueryStringCredentialAliasCreation() throws Exception {
+
+    final String testMasterSecret = "mysecret";
+    final String discoveryType = "NO_OP";
+    final String clusterName = "dummy";
+
+    final Map<String, List<String>> serviceURLs = new HashMap<>();
+    serviceURLs.put("RESOURCEMANAGER", Collections.singletonList("http://myhost:1234/resource"));
+
+    File testRootDir = TestUtils.createTempDir(getClass().getSimpleName());
+    File testConfDir = new File(testRootDir, "conf");
+    File testProvDir = new File(testConfDir, "shared-providers");
+    File testTopoDir = new File(testConfDir, "topologies");
+    File testDeployDir = new File(testConfDir, "deployments");
+
+    // Write the externalized provider config to a temp file
+    File providerConfig = new File(testProvDir, "ambari-cluster-policy.xml");
+    FileUtils.write(providerConfig, TEST_PROVIDER_CONFIG);
+
+    File topologyFile = null;
+    try {
+      File destDir = new File(System.getProperty("java.io.tmpdir")).getCanonicalFile();
+
+      // Mock out the simple descriptor
+      SimpleDescriptor testDescriptor = EasyMock.createNiceMock(SimpleDescriptor.class);
+      EasyMock.expect(testDescriptor.getName()).andReturn("mysimpledescriptor").anyTimes();
+      EasyMock.expect(testDescriptor.getDiscoveryAddress()).andReturn(null).anyTimes();
+      EasyMock.expect(testDescriptor.getDiscoveryType()).andReturn(discoveryType).anyTimes();
+      EasyMock.expect(testDescriptor.getDiscoveryUser()).andReturn(null).anyTimes();
+      EasyMock.expect(testDescriptor.getProviderConfig()).andReturn(providerConfig.getAbsolutePath()).anyTimes();
+      EasyMock.expect(testDescriptor.getClusterName()).andReturn(clusterName).anyTimes();
+      List<SimpleDescriptor.Service> serviceMocks = new ArrayList<>();
+      for (String serviceName : serviceURLs.keySet()) {
+        SimpleDescriptor.Service svc = EasyMock.createNiceMock(SimpleDescriptor.Service.class);
+        EasyMock.expect(svc.getName()).andReturn(serviceName).anyTimes();
+        EasyMock.expect(svc.getURLs()).andReturn(serviceURLs.get(serviceName)).anyTimes();
+        EasyMock.expect(svc.getParams()).andReturn(Collections.emptyMap()).anyTimes();
+        EasyMock.replay(svc);
+        serviceMocks.add(svc);
+      }
+      EasyMock.expect(testDescriptor.getServices()).andReturn(serviceMocks).anyTimes();
+      EasyMock.replay(testDescriptor);
+
+      // Try setting up enough of the GatewayServer to support the test...
+      GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class);
+      InetSocketAddress gatewayAddress = new InetSocketAddress(0);
+      EasyMock.expect(config.getGatewayTopologyDir()).andReturn(testTopoDir.getAbsolutePath()).anyTimes();
+      EasyMock.expect(config.getGatewayDeploymentDir()).andReturn(testDeployDir.getAbsolutePath()).anyTimes();
+      EasyMock.expect(config.getGatewayAddress()).andReturn(gatewayAddress).anyTimes();
+      EasyMock.expect(config.getGatewayPortMappings()).andReturn(Collections.emptyMap()).anyTimes();
+      EasyMock.replay(config);
+
+      // Setup the Gateway Services
+      GatewayServices gatewayServices = EasyMock.createNiceMock(GatewayServices.class);
+
+      // Master Service
+      MasterService ms = EasyMock.createNiceMock(MasterService.class);
+      EasyMock.expect(ms.getMasterSecret()).andReturn(testMasterSecret.toCharArray()).anyTimes();
+      EasyMock.replay(ms);
+      EasyMock.expect(gatewayServices.getService("MasterService")).andReturn(ms).anyTimes();
+
+      // Keystore Service
+      KeystoreService ks = EasyMock.createNiceMock(KeystoreService.class);
+      EasyMock.expect(ks.isCredentialStoreForClusterAvailable(testDescriptor.getName())).andReturn(false).once();
+      ks.createCredentialStoreForCluster(testDescriptor.getName());
+      EasyMock.expectLastCall().once();
+      KeyStore credStore = EasyMock.createNiceMock(KeyStore.class);
+      EasyMock.expect(ks.getCredentialStoreForCluster(testDescriptor.getName())).andReturn(credStore).anyTimes();
+      EasyMock.replay(ks);
+      EasyMock.expect(gatewayServices.getService(GatewayServices.KEYSTORE_SERVICE)).andReturn(ks).anyTimes();
+
+      // Alias Service
+      AliasService as = EasyMock.createNiceMock(AliasService.class);
+      // Captures for validating the alias creation for a generated topology
+      Capture<String> capturedCluster = EasyMock.newCapture();
+      Capture<String> capturedAlias = EasyMock.newCapture();
+      Capture<String> capturedPwd = EasyMock.newCapture();
+      as.addAliasForCluster(capture(capturedCluster), capture(capturedAlias), capture(capturedPwd));
+      EasyMock.expectLastCall().anyTimes();
+      EasyMock.replay(as);
+      EasyMock.expect(gatewayServices.getService(GatewayServices.ALIAS_SERVICE)).andReturn(as).anyTimes();
+
+      // Topology Service
+      TopologyService ts = EasyMock.createNiceMock(TopologyService.class);
+      ts.addTopologyChangeListener(anyObject());
+      EasyMock.expectLastCall().anyTimes();
+      ts.reloadTopologies();
+      EasyMock.expectLastCall().anyTimes();
+      EasyMock.expect(ts.getTopologies()).andReturn(Collections.emptyList()).anyTimes();
+      EasyMock.replay(ts);
+      EasyMock.expect(gatewayServices.getService(GatewayServices.TOPOLOGY_SERVICE)).andReturn(ts).anyTimes();
+
+      EasyMock.replay(gatewayServices);
+
+      // Start a GatewayService with the GatewayServices mock
+      GatewayServer server = GatewayServer.startGateway(config, gatewayServices);
+
+      // Invoke the simple descriptor handler, which will also create the credential store
+      // (because it doesn't exist) and the encryptQueryString alias
+      Map<String, File> files = SimpleDescriptorHandler.handle(testDescriptor,
+                                                               providerConfig.getParentFile(),
+                                                               destDir);
+      topologyFile = files.get("topology");
+
+      // Validate the AliasService interaction
+      assertEquals("Unexpected cluster name for the alias (should be the topology name).",
+                   testDescriptor.getName(), capturedCluster.getValue());
+      assertEquals("Unexpected alias name.", "encryptQueryString", capturedAlias.getValue());
+      assertEquals("Unexpected alias value (should be master secret + topology name.",
+                   testMasterSecret + testDescriptor.getName(), capturedPwd.getValue());
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    } finally {
+      FileUtils.forceDelete(testRootDir);
+      if (topologyFile != null) {
+        topologyFile.delete();
+      }
+    }
+  }
+
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Test classes for effectively "skipping" service discovery for this test.
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public static final class NoOpServiceDiscoveryType implements ServiceDiscoveryType {
+    @Override
+    public String getType() {
+      return NoOpServiceDiscovery.TYPE;
+    }
+
+    @Override
+    public ServiceDiscovery newInstance() {
+      return new NoOpServiceDiscovery();
+    }
+  }
+
+  private static final class NoOpServiceDiscovery implements ServiceDiscovery {
+    static final String TYPE = "NO_OP";
+
+    @Override
+    public String getType() {
+      return TYPE;
+    }
+
+    @Override
+    public Map<String, Cluster> discover(ServiceDiscoveryConfig config) {
+      return Collections.emptyMap();
+    }
+
+    @Override
+    public Cluster discover(ServiceDiscoveryConfig config, String clusterName) {
+      return null;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/e766b3b7/gateway-test/src/test/java/org/apache/knox/gateway/topology/monitor/RemoteConfigurationMonitorTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/knox/gateway/topology/monitor/RemoteConfigurationMonitorTest.java b/gateway-test/src/test/java/org/apache/knox/gateway/topology/monitor/RemoteConfigurationMonitorTest.java
new file mode 100644
index 0000000..37668a8
--- /dev/null
+++ b/gateway-test/src/test/java/org/apache/knox/gateway/topology/monitor/RemoteConfigurationMonitorTest.java
@@ -0,0 +1,603 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.knox.gateway.topology.monitor;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.test.InstanceSpec;
+import org.apache.curator.test.TestingCluster;
+import org.apache.knox.gateway.config.GatewayConfig;
+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.test.TestUtils;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test the RemoteConfigurationMonitor functionality with SASL configured, and znode ACLs applied.
+ *
+ * The expected implementation is org.apache.knox.gateway.topology.monitor.zk.ZooKeeperConfigMonitor
+ *
+ * Digest-based SASL is used for this test, but since that is dictated solely by the JAAS config, Kerberos-based SASL
+ * should work in exactly the same way, simply by modifying the SASL config.
+ */
+public class RemoteConfigurationMonitorTest {
+
+    private static final String PATH_KNOX = "/knox";
+    private static final String PATH_KNOX_CONFIG = PATH_KNOX + "/config";
+    private static final String PATH_KNOX_PROVIDERS = PATH_KNOX_CONFIG + "/shared-providers";
+    private static final String PATH_KNOX_DESCRIPTORS = PATH_KNOX_CONFIG + "/descriptors";
+
+    private static final String PATH_AUTH_TEST = "/auth_test/child_node";
+
+
+    private static final String ALT_USERNAME = "notyou";
+    private static final String ZK_USERNAME = "testsasluser";
+    private static final String ZK_PASSWORD = "testsaslpwd";
+
+    private static final ACL ANY_AUTHENTICATED_USER_ALL = new ACL(ZooDefs.Perms.ALL, new Id("auth", ""));
+    private static final ACL SASL_TESTUSER_ALL = new ACL(ZooDefs.Perms.ALL, new Id("sasl", ZK_USERNAME));
+
+    private static File testTmp;
+    private static File providersDir;
+    private static File descriptorsDir;
+
+    private static TestingCluster zkCluster;
+
+    private static CuratorFramework client;
+
+    @BeforeClass
+    public static void setupSuite() throws Exception {
+        testTmp = TestUtils.createTempDir(RemoteConfigurationMonitorTest.class.getName());
+        File confDir = TestUtils.createTempDir(testTmp + "/conf");
+        providersDir = TestUtils.createTempDir(confDir + "/shared-providers");
+        descriptorsDir = TestUtils.createTempDir(confDir + "/descriptors");
+    }
+
+    @AfterClass
+    public static void tearDownSuite() throws Exception {
+        // Delete the working dir
+        testTmp.delete();
+    }
+
+    @Before
+    public void setupTest() throws Exception {
+        configureAndStartZKCluster();
+    }
+
+    @After
+    public void tearDownTest() throws Exception {
+        // Clean up the ZK nodes, and close the client
+        if (client != null) {
+            if (client.checkExists().forPath(PATH_KNOX) != null) {
+                client.delete().deletingChildrenIfNeeded().forPath(PATH_KNOX);
+            }
+            client.close();
+        }
+
+        // Shutdown the ZK cluster
+        zkCluster.close();
+    }
+
+    /**
+     * Create and persist a JAAS configuration file, defining the SASL config for both the ZooKeeper cluster instances
+     * and ZooKeeper clients.
+     *
+     * @param username The digest username
+     * @param password The digest password
+     *
+     * @return The JAAS configuration file
+     */
+    private static File setupDigestSaslConfig(String username, String password) throws Exception {
+        File saslConfigFile = new File(testTmp, "server-jaas.conf");
+        FileWriter fw = new FileWriter(saslConfigFile);
+        fw.write("Server {\n" +
+                "    org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
+                "    user_" + username + " =\"" + password + "\";\n" +
+                "};\n" +
+                "Client {\n" +
+                "    org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
+                "    username=\"" + username + "\"\n" +
+                "    password=\"" + password + "\";\n" +
+                "};\n");
+        fw.close();
+        return saslConfigFile;
+    }
+
+    /**
+     * Configure and start the ZooKeeper test cluster, and create the znodes monitored by the RemoteConfigurationMonitor.
+     */
+    private static void configureAndStartZKCluster() throws Exception {
+        // Configure security for the ZK cluster instances
+        Map<String, Object> customInstanceSpecProps = new HashMap<>();
+        customInstanceSpecProps.put("authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
+        customInstanceSpecProps.put("requireClientAuthScheme", "sasl");
+
+        // Define the test cluster
+        List<InstanceSpec> instanceSpecs = new ArrayList<>();
+        for (int i = 0 ; i < 3 ; i++) {
+            InstanceSpec is = new InstanceSpec(null, -1, -1, -1, false, (i+1), -1, -1, customInstanceSpecProps);
+            instanceSpecs.add(is);
+        }
+        zkCluster = new TestingCluster(instanceSpecs);
+
+        // Configure auth for the ZooKeeper servers and the clients
+        File saslConfigFile = setupDigestSaslConfig(ZK_USERNAME, ZK_PASSWORD);
+
+        // This system property is used by the ZooKeeper cluster instances, the test driver client, and the
+        // RemoteConfigurationMonitor implementation for SASL authentication/authorization
+        System.setProperty("java.security.auth.login.config", saslConfigFile.getAbsolutePath());
+
+        // Start the cluster
+        zkCluster.start();
+
+        // Create the client for the test cluster
+        client = CuratorFrameworkFactory.builder()
+                                        .connectString(zkCluster.getConnectString())
+                                        .retryPolicy(new ExponentialBackoffRetry(100, 3))
+                                        .build();
+        assertNotNull(client);
+        client.start();
+
+        // Create test config nodes with an ACL for a sasl user that is NOT configured for the test client
+        List<ACL> acls = Arrays.asList(new ACL(ZooDefs.Perms.ALL, new Id("sasl", ALT_USERNAME)),
+                                       new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE));
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_AUTH_TEST);
+        assertNotNull("Failed to create node:" + PATH_AUTH_TEST,
+                      client.checkExists().forPath(PATH_AUTH_TEST));
+    }
+
+
+    private static void validateKnoxConfigNodeACLs(List<ACL> expectedACLS, List<ACL> actualACLs) throws Exception {
+        assertEquals(expectedACLS.size(), actualACLs.size());
+        int matchedCount = 0;
+        for (ACL expected : expectedACLS) {
+            for (ACL actual : actualACLs) {
+                Id expectedId = expected.getId();
+                Id actualId = actual.getId();
+                if (actualId.getScheme().equals(expectedId.getScheme()) && actualId.getId().equals(expectedId.getId())) {
+                    matchedCount++;
+                    assertEquals(expected.getPerms(), actual.getPerms());
+                    break;
+                }
+            }
+        }
+        assertEquals("ACL mismatch despite being same quantity.", expectedACLS.size(), matchedCount);
+    }
+
+
+    @Test
+    public void testZooKeeperConfigMonitorSASLNodesExistWithUnacceptableACL() throws Exception {
+        final String configMonitorName = "zkConfigClient";
+        final String alias = "zkPass";
+
+        // Setup the base GatewayConfig mock
+        GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
+        EasyMock.expect(gc.getGatewayProvidersConfigDir()).andReturn(providersDir.getAbsolutePath()).anyTimes();
+        EasyMock.expect(gc.getGatewayDescriptorsDir()).andReturn(descriptorsDir.getAbsolutePath()).anyTimes();
+        EasyMock.expect(gc.getRemoteRegistryConfigurationNames())
+                .andReturn(Collections.singletonList(configMonitorName))
+                .anyTimes();
+        final String registryConfig =
+                GatewayConfig.REMOTE_CONFIG_REGISTRY_TYPE + "=" + ZooKeeperClientService.TYPE + ";" +
+                        GatewayConfig.REMOTE_CONFIG_REGISTRY_ADDRESS + "=" + zkCluster.getConnectString() + ";" +
+                        GatewayConfig.REMOTE_CONFIG_REGISTRY_PRINCIPAL + "=" + ZK_USERNAME + ";" +
+                        GatewayConfig.REMOTE_CONFIG_REGISTRY_AUTH_TYPE + "=Digest;" +
+                        GatewayConfig.REMOTE_CONFIG_REGISTRY_CREDENTIAL_ALIAS + "=" + alias;
+        EasyMock.expect(gc.getRemoteRegistryConfiguration(configMonitorName))
+                .andReturn(registryConfig).anyTimes();
+        EasyMock.expect(gc.getRemoteConfigurationMonitorClientName()).andReturn(configMonitorName).anyTimes();
+        EasyMock.replay(gc);
+
+        AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
+        EasyMock.expect(aliasService.getPasswordFromAliasForGateway(alias))
+                .andReturn(ZK_PASSWORD.toCharArray())
+                .anyTimes();
+        EasyMock.replay(aliasService);
+
+        RemoteConfigurationRegistryClientService clientService = (new ZooKeeperClientServiceProvider()).newInstance();
+        clientService.setAliasService(aliasService);
+        clientService.init(gc, Collections.emptyMap());
+        clientService.start();
+
+        RemoteConfigurationMonitorFactory.setClientService(clientService);
+
+        RemoteConfigurationMonitor cm = RemoteConfigurationMonitorFactory.get(gc);
+        assertNotNull("Failed to load RemoteConfigurationMonitor", cm);
+
+        final ACL ANY_AUTHENTICATED_USER_ALL = new ACL(ZooDefs.Perms.ALL, new Id("auth", ""));
+        List<ACL> acls = Arrays.asList(ANY_AUTHENTICATED_USER_ALL, new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.ANYONE_ID_UNSAFE));
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX);
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_CONFIG);
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_PROVIDERS);
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_DESCRIPTORS);
+
+        // Make sure both ACLs were applied
+        List<ACL> preACLs = client.getACL().forPath(PATH_KNOX);
+        assertEquals(2, preACLs.size());
+
+        // Check that the config nodes really do exist (the monitor will NOT create them if they're present)
+        assertNotNull(client.checkExists().forPath(PATH_KNOX));
+        assertNotNull(client.checkExists().forPath(PATH_KNOX_CONFIG));
+        assertNotNull(client.checkExists().forPath(PATH_KNOX_PROVIDERS));
+        assertNotNull(client.checkExists().forPath(PATH_KNOX_DESCRIPTORS));
+
+        try {
+            cm.start();
+        } catch (Exception e) {
+            fail("Failed to start monitor: " + e.getMessage());
+        }
+
+        // Validate the expected ACLs on the Knox config znodes (make sure the monitor removed the world:anyone ACL)
+        List<ACL> expectedACLs = Collections.singletonList(SASL_TESTUSER_ALL);
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX));
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_CONFIG));
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_PROVIDERS));
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_DESCRIPTORS));
+    }
+
+
+    @Test
+    public void testZooKeeperConfigMonitorSASLNodesExistWithAcceptableACL() throws Exception {
+        final String configMonitorName = "zkConfigClient";
+        final String alias = "zkPass";
+
+        // Setup the base GatewayConfig mock
+        GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
+        EasyMock.expect(gc.getGatewayProvidersConfigDir()).andReturn(providersDir.getAbsolutePath()).anyTimes();
+        EasyMock.expect(gc.getGatewayDescriptorsDir()).andReturn(descriptorsDir.getAbsolutePath()).anyTimes();
+        EasyMock.expect(gc.getRemoteRegistryConfigurationNames())
+                .andReturn(Collections.singletonList(configMonitorName))
+                .anyTimes();
+        final String registryConfig =
+                GatewayConfig.REMOTE_CONFIG_REGISTRY_TYPE + "=" + ZooKeeperClientService.TYPE + ";" +
+                        GatewayConfig.REMOTE_CONFIG_REGISTRY_ADDRESS + "=" + zkCluster.getConnectString() + ";" +
+                        GatewayConfig.REMOTE_CONFIG_REGISTRY_PRINCIPAL + "=" + ZK_USERNAME + ";" +
+                        GatewayConfig.REMOTE_CONFIG_REGISTRY_AUTH_TYPE + "=Digest;" +
+                        GatewayConfig.REMOTE_CONFIG_REGISTRY_CREDENTIAL_ALIAS + "=" + alias;
+        EasyMock.expect(gc.getRemoteRegistryConfiguration(configMonitorName))
+                .andReturn(registryConfig).anyTimes();
+        EasyMock.expect(gc.getRemoteConfigurationMonitorClientName()).andReturn(configMonitorName).anyTimes();
+        EasyMock.replay(gc);
+
+        AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
+        EasyMock.expect(aliasService.getPasswordFromAliasForGateway(alias))
+                .andReturn(ZK_PASSWORD.toCharArray())
+                .anyTimes();
+        EasyMock.replay(aliasService);
+
+        RemoteConfigurationRegistryClientService clientService = (new ZooKeeperClientServiceProvider()).newInstance();
+        clientService.setAliasService(aliasService);
+        clientService.init(gc, Collections.emptyMap());
+        clientService.start();
+
+        RemoteConfigurationMonitorFactory.setClientService(clientService);
+
+        RemoteConfigurationMonitor cm = RemoteConfigurationMonitorFactory.get(gc);
+        assertNotNull("Failed to load RemoteConfigurationMonitor", cm);
+
+        List<ACL> acls = Arrays.asList(ANY_AUTHENTICATED_USER_ALL);
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX);
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_CONFIG);
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_PROVIDERS);
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_DESCRIPTORS);
+
+        // Check that the config nodes really do exist (the monitor will NOT create them if they're present)
+        assertNotNull(client.checkExists().forPath(PATH_KNOX));
+        assertNotNull(client.checkExists().forPath(PATH_KNOX_CONFIG));
+        assertNotNull(client.checkExists().forPath(PATH_KNOX_PROVIDERS));
+        assertNotNull(client.checkExists().forPath(PATH_KNOX_DESCRIPTORS));
+
+        try {
+            cm.start();
+        } catch (Exception e) {
+            fail("Failed to start monitor: " + e.getMessage());
+        }
+
+        // Test auth violation
+        clientService.get(configMonitorName).createEntry("/auth_test/child_node/test1");
+        assertNull("Creation should have been prevented since write access is not granted to the test client.",
+                client.checkExists().forPath("/auth_test/child_node/test1"));
+        assertTrue("Creation should have been prevented since write access is not granted to the test client.",
+                client.getChildren().forPath("/auth_test/child_node").isEmpty());
+
+        // Validate the expected ACLs on the Knox config znodes (make sure the monitor didn't change them)
+        List<ACL> expectedACLs = Collections.singletonList(SASL_TESTUSER_ALL);
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX));
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_CONFIG));
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_PROVIDERS));
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_DESCRIPTORS));
+    }
+
+
+    @Test
+    public void testZooKeeperConfigMonitorSASLCreateNodes() throws Exception {
+        final String configMonitorName = "zkConfigClient";
+        final String alias = "zkPass";
+
+        // Setup the base GatewayConfig mock
+        GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
+        EasyMock.expect(gc.getGatewayProvidersConfigDir()).andReturn(providersDir.getAbsolutePath()).anyTimes();
+        EasyMock.expect(gc.getGatewayDescriptorsDir()).andReturn(descriptorsDir.getAbsolutePath()).anyTimes();
+        EasyMock.expect(gc.getRemoteRegistryConfigurationNames())
+                .andReturn(Collections.singletonList(configMonitorName))
+                .anyTimes();
+        final String registryConfig =
+                            GatewayConfig.REMOTE_CONFIG_REGISTRY_TYPE + "=" + ZooKeeperClientService.TYPE + ";" +
+                            GatewayConfig.REMOTE_CONFIG_REGISTRY_ADDRESS + "=" + zkCluster.getConnectString() + ";" +
+                            GatewayConfig.REMOTE_CONFIG_REGISTRY_PRINCIPAL + "=" + ZK_USERNAME + ";" +
+                            GatewayConfig.REMOTE_CONFIG_REGISTRY_AUTH_TYPE + "=Digest;" +
+                            GatewayConfig.REMOTE_CONFIG_REGISTRY_CREDENTIAL_ALIAS + "=" + alias;
+        EasyMock.expect(gc.getRemoteRegistryConfiguration(configMonitorName))
+                .andReturn(registryConfig).anyTimes();
+        EasyMock.expect(gc.getRemoteConfigurationMonitorClientName()).andReturn(configMonitorName).anyTimes();
+        EasyMock.replay(gc);
+
+        AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
+        EasyMock.expect(aliasService.getPasswordFromAliasForGateway(alias))
+                .andReturn(ZK_PASSWORD.toCharArray())
+                .anyTimes();
+        EasyMock.replay(aliasService);
+
+        RemoteConfigurationRegistryClientService clientService = (new ZooKeeperClientServiceProvider()).newInstance();
+        clientService.setAliasService(aliasService);
+        clientService.init(gc, Collections.emptyMap());
+        clientService.start();
+
+        RemoteConfigurationMonitorFactory.setClientService(clientService);
+
+        RemoteConfigurationMonitor cm = RemoteConfigurationMonitorFactory.get(gc);
+        assertNotNull("Failed to load RemoteConfigurationMonitor", cm);
+
+        // Check that the config nodes really don't yet exist (the monitor will create them if they're not present)
+        assertNull(client.checkExists().forPath(PATH_KNOX));
+        assertNull(client.checkExists().forPath(PATH_KNOX_CONFIG));
+        assertNull(client.checkExists().forPath(PATH_KNOX_PROVIDERS));
+        assertNull(client.checkExists().forPath(PATH_KNOX_DESCRIPTORS));
+
+        try {
+            cm.start();
+        } catch (Exception e) {
+            fail("Failed to start monitor: " + e.getMessage());
+        }
+
+        // Test auth violation
+        clientService.get(configMonitorName).createEntry("/auth_test/child_node/test1");
+        assertNull("Creation should have been prevented since write access is not granted to the test client.",
+                   client.checkExists().forPath("/auth_test/child_node/test1"));
+        assertTrue("Creation should have been prevented since write access is not granted to the test client.",
+                   client.getChildren().forPath("/auth_test/child_node").isEmpty());
+
+        // Validate the expected ACLs on the Knox config znodes (make sure the monitor created them correctly)
+        List<ACL> expectedACLs = Collections.singletonList(SASL_TESTUSER_ALL);
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX));
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_CONFIG));
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_PROVIDERS));
+        validateKnoxConfigNodeACLs(expectedACLs, client.getACL().forPath(PATH_KNOX_DESCRIPTORS));
+
+        // Test the Knox config nodes, for which authentication should be sufficient for access
+        try {
+            final String pc_one_znode = getProviderPath("providers-config1.xml");
+            final File pc_one         = new File(providersDir, "providers-config1.xml");
+            final String pc_two_znode = getProviderPath("providers-config2.xml");
+            final File pc_two         = new File(providersDir, "providers-config2.xml");
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(pc_one_znode, TEST_PROVIDERS_CONFIG_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(pc_one.exists());
+            assertEquals(TEST_PROVIDERS_CONFIG_1, FileUtils.readFileToString(pc_one));
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(getProviderPath("providers-config2.xml"), TEST_PROVIDERS_CONFIG_2.getBytes());
+            Thread.sleep(100);
+            assertTrue(pc_two.exists());
+            assertEquals(TEST_PROVIDERS_CONFIG_2, FileUtils.readFileToString(pc_two));
+
+            client.setData().forPath(pc_two_znode, TEST_PROVIDERS_CONFIG_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(pc_two.exists());
+            assertEquals(TEST_PROVIDERS_CONFIG_1, FileUtils.readFileToString(pc_two));
+
+            client.delete().forPath(pc_two_znode);
+            Thread.sleep(100);
+            assertFalse(pc_two.exists());
+
+            client.delete().forPath(pc_one_znode);
+            Thread.sleep(100);
+            assertFalse(pc_one.exists());
+
+            final String desc_one_znode   = getDescriptorPath("test1.json");
+            final String desc_two_znode   = getDescriptorPath("test2.json");
+            final String desc_three_znode = getDescriptorPath("test3.json");
+            final File desc_one           = new File(descriptorsDir, "test1.json");
+            final File desc_two           = new File(descriptorsDir, "test2.json");
+            final File desc_three         = new File(descriptorsDir, "test3.json");
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(desc_one_znode, TEST_DESCRIPTOR_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(desc_one.exists());
+            assertEquals(TEST_DESCRIPTOR_1, FileUtils.readFileToString(desc_one));
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(desc_two_znode, TEST_DESCRIPTOR_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(desc_two.exists());
+            assertEquals(TEST_DESCRIPTOR_1, FileUtils.readFileToString(desc_two));
+
+            client.setData().forPath(desc_two_znode, TEST_DESCRIPTOR_2.getBytes());
+            Thread.sleep(100);
+            assertTrue(desc_two.exists());
+            assertEquals(TEST_DESCRIPTOR_2, FileUtils.readFileToString(desc_two));
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(desc_three_znode, TEST_DESCRIPTOR_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(desc_three.exists());
+            assertEquals(TEST_DESCRIPTOR_1, FileUtils.readFileToString(desc_three));
+
+            client.delete().forPath(desc_two_znode);
+            Thread.sleep(100);
+            assertFalse("Expected test2.json to have been deleted.", desc_two.exists());
+
+            client.delete().forPath(desc_three_znode);
+            Thread.sleep(100);
+            assertFalse(desc_three.exists());
+
+            client.delete().forPath(desc_one_znode);
+            Thread.sleep(100);
+            assertFalse(desc_one.exists());
+        } finally {
+            cm.stop();
+        }
+    }
+
+    private static String getDescriptorPath(String descriptorName) {
+        return PATH_KNOX_DESCRIPTORS + "/" + descriptorName;
+    }
+
+    private static String getProviderPath(String providerConfigName) {
+        return PATH_KNOX_PROVIDERS + "/" + providerConfigName;
+    }
+
+
+    private static final String TEST_PROVIDERS_CONFIG_1 =
+                    "<gateway>\n" +
+                    "    <provider>\n" +
+                    "        <role>identity-assertion</role>\n" +
+                    "        <name>Default</name>\n" +
+                    "        <enabled>true</enabled>\n" +
+                    "    </provider>\n" +
+                    "    <provider>\n" +
+                    "        <role>hostmap</role>\n" +
+                    "        <name>static</name>\n" +
+                    "        <enabled>true</enabled>\n" +
+                    "        <param><name>localhost</name><value>sandbox,sandbox.hortonworks.com</value></param>\n" +
+                    "    </provider>\n" +
+                    "</gateway>\n";
+
+    private static final String TEST_PROVIDERS_CONFIG_2 =
+                    "<gateway>\n" +
+                    "    <provider>\n" +
+                    "        <role>authentication</role>\n" +
+                    "        <name>ShiroProvider</name>\n" +
+                    "        <enabled>true</enabled>\n" +
+                    "        <param>\n" +
+                    "            <name>sessionTimeout</name>\n" +
+                    "            <value>30</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm</name>\n" +
+                    "            <value>org.apache.knox.gateway.shirorealm.KnoxLdapRealm</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapContextFactory</name>\n" +
+                    "            <value>org.apache.knox.gateway.shirorealm.KnoxLdapContextFactory</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm.contextFactory</name>\n" +
+                    "            <value>$ldapContextFactory</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm.userDnTemplate</name>\n" +
+                    "            <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm.contextFactory.url</name>\n" +
+                    "            <value>ldap://localhost:33389</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm.contextFactory.authenticationMechanism</name>\n" +
+                    "            <value>simple</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>urls./**</name>\n" +
+                    "            <value>authcBasic</value>\n" +
+                    "        </param>\n" +
+                    "    </provider>\n" +
+                    "</gateway>\n";
+
+    private static final String TEST_DESCRIPTOR_1 =
+                    "{\n" +
+                    "  \"discovery-type\":\"AMBARI\",\n" +
+                    "  \"discovery-address\":\"http://sandbox.hortonworks.com:8080\",\n" +
+                    "  \"discovery-user\":\"maria_dev\",\n" +
+                    "  \"discovery-pwd-alias\":\"sandbox.ambari.discovery.password\",\n" +
+                    "  \"provider-config-ref\":\"sandbox-providers.xml\",\n" +
+                    "  \"cluster\":\"Sandbox\",\n" +
+                    "  \"services\":[\n" +
+                    "    {\"name\":\"NODEUI\"},\n" +
+                    "    {\"name\":\"YARNUI\"},\n" +
+                    "    {\"name\":\"HDFSUI\"},\n" +
+                    "    {\"name\":\"OOZIEUI\"},\n" +
+                    "    {\"name\":\"HBASEUI\"},\n" +
+                    "    {\"name\":\"NAMENODE\"},\n" +
+                    "    {\"name\":\"JOBTRACKER\"},\n" +
+                    "    {\"name\":\"WEBHDFS\"},\n" +
+                    "    {\"name\":\"WEBHCAT\"},\n" +
+                    "    {\"name\":\"OOZIE\"},\n" +
+                    "    {\"name\":\"WEBHBASE\"},\n" +
+                    "    {\"name\":\"RESOURCEMANAGER\"},\n" +
+                    "    {\"name\":\"AMBARI\", \"urls\":[\"http://c6401.ambari.apache.org:8080\"]},\n" +
+                    "    {\"name\":\"AMBARIUI\", \"urls\":[\"http://c6401.ambari.apache.org:8080\"]}\n" +
+                    "  ]\n" +
+                    "}\n";
+
+    private static final String TEST_DESCRIPTOR_2 =
+                    "{\n" +
+                    "  \"discovery-type\":\"AMBARI\",\n" +
+                    "  \"discovery-address\":\"http://sandbox.hortonworks.com:8080\",\n" +
+                    "  \"discovery-user\":\"maria_dev\",\n" +
+                    "  \"discovery-pwd-alias\":\"sandbox.ambari.discovery.password\",\n" +
+                    "  \"provider-config-ref\":\"sandbox-providers.xml\",\n" +
+                    "  \"cluster\":\"Sandbox\",\n" +
+                    "  \"services\":[\n" +
+                    "    {\"name\":\"NAMENODE\"},\n" +
+                    "    {\"name\":\"JOBTRACKER\"},\n" +
+                    "    {\"name\":\"WEBHDFS\"},\n" +
+                    "    {\"name\":\"WEBHCAT\"},\n" +
+                    "    {\"name\":\"OOZIE\"},\n" +
+                    "    {\"name\":\"WEBHBASE\"},\n" +
+                    "    {\"name\":\"RESOURCEMANAGER\"}\n" +
+                    "  ]\n" +
+                    "}\n";
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/e766b3b7/gateway-test/src/test/resources/META-INF/services/org.apache.hadoop.gateway.topology.discovery.ServiceDiscoveryType
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/META-INF/services/org.apache.hadoop.gateway.topology.discovery.ServiceDiscoveryType b/gateway-test/src/test/resources/META-INF/services/org.apache.hadoop.gateway.topology.discovery.ServiceDiscoveryType
deleted file mode 100644
index 0c5fe09..0000000
--- a/gateway-test/src/test/resources/META-INF/services/org.apache.hadoop.gateway.topology.discovery.ServiceDiscoveryType
+++ /dev/null
@@ -1,19 +0,0 @@
-##########################################################################
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##########################################################################
-
-org.apache.hadoop.gateway.SimpleDescriptorHandlerFuncTest$NoOpServiceDiscoveryType

http://git-wip-us.apache.org/repos/asf/knox/blob/e766b3b7/gateway-test/src/test/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.ServiceDiscoveryType
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.ServiceDiscoveryType b/gateway-test/src/test/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.ServiceDiscoveryType
new file mode 100644
index 0000000..8d72813
--- /dev/null
+++ b/gateway-test/src/test/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.ServiceDiscoveryType
@@ -0,0 +1,19 @@
+##########################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##########################################################################
+
+org.apache.knox.gateway.SimpleDescriptorHandlerFuncTest$NoOpServiceDiscoveryType