org.apache.hadoop.hdfs.TestAclsEndToEnd.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hdfs.TestAclsEndToEnd.java

Source

/**
 * 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.
 */
package org.apache.hadoop.hdfs;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.crypto.key.kms.KMSClientProvider;
import org.apache.hadoop.crypto.key.kms.server.KMSConfiguration;
import org.apache.hadoop.crypto.key.kms.server.KeyAuthorizationKeyProvider;
import org.apache.hadoop.crypto.key.kms.server.MiniKMS;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystemTestHelper;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authorize.ProxyUsers;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * This class tests the ACLs system through the full code path.  It overlaps
 * slightly with the ACL tests in common, but the approach is more holistic.
 *
 * <b>NOTE:</b> Because of the mechanics of JAXP, when the KMS config files are
 * written to disk, a config param with a blank value ("") will be written in a
 * way that the KMS will read as unset, which is different from blank. For this
 * reason, when testing the effects of blank config params, this test class
 * sets the values of those config params to a space (" ").  A whitespace value
 * will be preserved by JAXP when writing out the config files and will be
 * interpreted by KMS as a blank value. (The KMS strips whitespace from ACL
 * values before interpreting them.)
 */
public class TestAclsEndToEnd {
    private static final Log LOG = LogFactory.getLog(TestAclsEndToEnd.class.getName());
    private static final String TEXT = "The blue zone is for loading and unloading only. "
            + "Please park in the red zone.";
    private static final Path ZONE1 = new Path("/tmp/BLUEZONE");
    private static final Path ZONE2 = new Path("/tmp/REDZONE");
    private static final Path ZONE3 = new Path("/tmp/LOADINGZONE");
    private static final Path ZONE4 = new Path("/tmp/UNLOADINGZONE");
    private static final Path FILE1 = new Path(ZONE1, "file1");
    private static final Path FILE1A = new Path(ZONE1, "file1a");
    private static final Path FILE2 = new Path(ZONE2, "file2");
    private static final Path FILE3 = new Path(ZONE3, "file3");
    private static final Path FILE4 = new Path(ZONE4, "file4");
    private static final String KEY1 = "key1";
    private static final String KEY2 = "key2";
    private static final String KEY3 = "key3";
    private static UserGroupInformation realUgi;
    private static String realUser;

    private MiniKMS miniKMS;
    private File kmsDir;
    private MiniDFSCluster cluster;
    private DistributedFileSystem fs;

    @BeforeClass
    public static void captureUser() throws IOException {
        realUgi = UserGroupInformation.getCurrentUser();
        realUser = System.getProperty("user.name");
    }

    /**
     * Extract the URI for the miniKMS.
     *
     * @return the URI for the miniKMS
     */
    private String getKeyProviderURI() {
        return KMSClientProvider.SCHEME_NAME + "://" + miniKMS.getKMSUrl().toExternalForm().replace("://", "@");
    }

    /**
     * Write out the config files needed by the miniKMS.  The miniKMS doesn't
     * provide a way to set the configs directly, so the only way to pass config
     * parameters is to write them out into config files.
     *
     * @param confDir the directory into which to write the configs
     * @param conf the config to write.
     * @throws IOException
     */
    private void writeConf(File confDir, Configuration conf) throws IOException {
        URI keystore = new Path(kmsDir.getAbsolutePath(), "kms.keystore").toUri();

        conf.set(KMSConfiguration.KEY_PROVIDER_URI, "jceks://file@" + keystore);
        conf.set("hadoop.kms.authentication.type", "simple");

        Writer writer = new FileWriter(new File(confDir, KMSConfiguration.KMS_SITE_XML));
        conf.writeXml(writer);
        writer.close();

        writer = new FileWriter(new File(confDir, KMSConfiguration.KMS_ACLS_XML));
        conf.writeXml(writer);
        writer.close();

        //create empty core-site.xml
        writer = new FileWriter(new File(confDir, "core-site.xml"));
        new Configuration(false).writeXml(writer);
        writer.close();
    }

    /**
     * Setup a fresh miniKMS and miniDFS.
     *
     * @param conf the configuration to use for both the miniKMS and miniDFS
     * @throws Exception thrown if setup fails
     */
    private void setup(Configuration conf) throws Exception {
        setup(conf, true, true);
    }

    /**
     * Setup a fresh miniDFS and a miniKMS.  The resetKms parameter controls
     * whether the miniKMS will start fresh or reuse the existing data.
     *
     * @param conf the configuration to use for both the miniKMS and miniDFS
     * @param resetKms whether to start a fresh miniKMS
     * @throws Exception thrown if setup fails
     */
    private void setup(Configuration conf, boolean resetKms) throws Exception {
        setup(conf, resetKms, true);
    }

    /**
     * Setup a miniDFS and miniKMS.  The resetKms and resetDfs parameters control
     * whether the services will start fresh or reuse the existing data.
     *
     * @param conf the configuration to use for both the miniKMS and miniDFS
     * @param resetKms whether to start a fresh miniKMS
     * @param resetDfs whether to start a fresh miniDFS
     * @throws Exception thrown if setup fails
     */
    private void setup(Configuration conf, boolean resetKms, boolean resetDfs) throws Exception {
        if (resetKms) {
            FileSystemTestHelper fsHelper = new FileSystemTestHelper();

            kmsDir = new File(fsHelper.getTestRootDir()).getAbsoluteFile();

            Assert.assertTrue(kmsDir.mkdirs());
        }

        writeConf(kmsDir, conf);

        MiniKMS.Builder miniKMSBuilder = new MiniKMS.Builder();

        miniKMS = miniKMSBuilder.setKmsConfDir(kmsDir).build();
        miniKMS.start();

        conf = new HdfsConfiguration();

        // Set up java key store
        conf.set(ProxyUsers.CONF_HADOOP_PROXYUSER + "." + realUser + ".users", "keyadmin,hdfs,user");
        conf.set(ProxyUsers.CONF_HADOOP_PROXYUSER + "." + realUser + ".hosts", "*");
        conf.set(DFSConfigKeys.DFS_ENCRYPTION_KEY_PROVIDER_URI, getKeyProviderURI());
        conf.setBoolean(DFSConfigKeys.DFS_NAMENODE_DELEGATION_TOKEN_ALWAYS_USE_KEY, true);

        MiniDFSCluster.Builder clusterBuilder = new MiniDFSCluster.Builder(conf);

        cluster = clusterBuilder.numDataNodes(1).format(resetDfs).build();
        fs = cluster.getFileSystem();
    }

    /**
     * Stop the miniKMS and miniDFS.
     */
    private void teardown() {
        // Restore login user
        UserGroupInformation.setLoginUser(realUgi);

        if (cluster != null) {
            cluster.shutdown();
        }

        miniKMS.stop();
    }

    /**
     * Return a new {@link Configuration} with KMS ACLs appropriate to pass the
     * full ACL test in {@link #doFullAclTest()} set.
     *
     * @param hdfsUgi the hdfs user
     * @param keyadminUgi the keyadmin user
     * @return the configuration
     */
    private static Configuration getBaseConf(UserGroupInformation hdfsUgi, UserGroupInformation keyadminUgi) {
        Configuration conf = new Configuration();

        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", keyadminUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DELETE", keyadminUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.ROLLOVER", keyadminUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET", " ");
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_KEYS", keyadminUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", hdfsUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.SET_KEY_MATERIAL", " ");
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", hdfsUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", "*");

        return conf;
    }

    /**
     * Set the recommended blacklists.
     *
     * @param hdfsUgi the hdfs user
     */
    private static void setBlacklistAcls(Configuration conf, UserGroupInformation hdfsUgi) {

        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.CREATE", hdfsUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.DELETE", hdfsUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.ROLLOVER", hdfsUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.GET", "*");
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.SET_KEY_MATERIAL", "*");
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.DECRYPT_EEK", hdfsUgi.getUserName());
    }

    /**
     * Set the key ACLs appropriate to pass the full ACL test in
     * {@link #doFullAclTest()} using the specified prefix.  The prefix should
     * either be "whitelist.key.acl." or "key.acl.key1.".
     *
     * @param conf the configuration
     * @param prefix the ACL prefix
     * @param hdfsUgi the hdfs user
     * @param keyadminUgi the keyadmin user
     * @param userUgi the normal user
     */
    private static void setKeyAcls(Configuration conf, String prefix, UserGroupInformation hdfsUgi,
            UserGroupInformation keyadminUgi, UserGroupInformation userUgi) {

        conf.set(prefix + "MANAGEMENT", keyadminUgi.getUserName());
        conf.set(prefix + "READ", hdfsUgi.getUserName());
        conf.set(prefix + "GENERATE_EEK", hdfsUgi.getUserName());
        conf.set(KeyAuthorizationKeyProvider.KEY_ACL + KEY1 + ".DECRYPT_EEK", userUgi.getUserName());
    }

    /**
     * Test the full life cycle of a key using a config with whitelist key ACLs.
     * The configuration used is the correct configuration to pass the full ACL
     * test in {@link #doFullAclTest()}.
     *
     * @throws Exception thrown on test failure
     */
    @Test
    public void testGoodWithWhitelist() throws Exception {
        UserGroupInformation hdfsUgi = UserGroupInformation.createProxyUserForTesting("hdfs", realUgi,
                new String[] { "supergroup" });
        UserGroupInformation keyadminUgi = UserGroupInformation.createProxyUserForTesting("keyadmin", realUgi,
                new String[] { "keyadmin" });
        UserGroupInformation userUgi = UserGroupInformation.createProxyUserForTesting("user", realUgi,
                new String[] { "staff" });

        Configuration conf = getBaseConf(hdfsUgi, keyadminUgi);

        setBlacklistAcls(conf, hdfsUgi);
        setKeyAcls(conf, KMSConfiguration.WHITELIST_KEY_ACL_PREFIX, hdfsUgi, keyadminUgi, userUgi);
        doFullAclTest(conf, hdfsUgi, keyadminUgi, userUgi);
    }

    /**
     * Test the full life cycle of a key using a config with key ACLs.
     * The configuration used is the correct configuration to pass the full ACL
     * test in {@link #doFullAclTest()}.
     *
     * @throws Exception thrown on test failure
     */
    @Test
    public void testGoodWithKeyAcls() throws Exception {
        UserGroupInformation hdfsUgi = UserGroupInformation.createProxyUserForTesting("hdfs", realUgi,
                new String[] { "supergroup" });
        UserGroupInformation keyadminUgi = UserGroupInformation.createProxyUserForTesting("keyadmin", realUgi,
                new String[] { "keyadmin" });
        UserGroupInformation userUgi = UserGroupInformation.createProxyUserForTesting("user", realUgi,
                new String[] { "staff" });
        Configuration conf = getBaseConf(hdfsUgi, keyadminUgi);

        setBlacklistAcls(conf, hdfsUgi);
        setKeyAcls(conf, KeyAuthorizationKeyProvider.KEY_ACL + KEY1 + ".", hdfsUgi, keyadminUgi, userUgi);
        doFullAclTest(conf, hdfsUgi, keyadminUgi, userUgi);
    }

    /**
     * Test the full life cycle of a key using a config with whitelist key ACLs
     * and without blacklist ACLs.  The configuration used is the correct
     * configuration to pass the full ACL test in {@link #doFullAclTest()}.
     *
     * @throws Exception thrown on test failure
     */
    @Test
    public void testGoodWithWhitelistWithoutBlacklist() throws Exception {
        UserGroupInformation hdfsUgi = UserGroupInformation.createProxyUserForTesting("hdfs", realUgi,
                new String[] { "supergroup" });
        UserGroupInformation keyadminUgi = UserGroupInformation.createProxyUserForTesting("keyadmin", realUgi,
                new String[] { "keyadmin" });
        UserGroupInformation userUgi = UserGroupInformation.createProxyUserForTesting("user", realUgi,
                new String[] { "staff" });
        Configuration conf = getBaseConf(hdfsUgi, keyadminUgi);

        setKeyAcls(conf, KMSConfiguration.WHITELIST_KEY_ACL_PREFIX, hdfsUgi, keyadminUgi, userUgi);
        doFullAclTest(conf, hdfsUgi, keyadminUgi, userUgi);
    }

    /**
     * Test the full life cycle of a key using a config with whitelist key ACLs
     * and without blacklist ACLs. The configuration used is the correct
     * configuration to pass the full ACL test in {@link #doFullAclTest()}.
     *
     * @throws Exception thrown on test failure
     */
    @Test
    public void testGoodWithKeyAclsWithoutBlacklist() throws Exception {
        UserGroupInformation hdfsUgi = UserGroupInformation.createProxyUserForTesting("hdfs", realUgi,
                new String[] { "supergroup" });
        UserGroupInformation keyadminUgi = UserGroupInformation.createProxyUserForTesting("keyadmin", realUgi,
                new String[] { "keyadmin" });
        UserGroupInformation userUgi = UserGroupInformation.createProxyUserForTesting("user", realUgi,
                new String[] { "staff" });
        Configuration conf = getBaseConf(hdfsUgi, keyadminUgi);

        setKeyAcls(conf, KeyAuthorizationKeyProvider.KEY_ACL + KEY1 + ".", hdfsUgi, keyadminUgi, userUgi);
        doFullAclTest(conf, hdfsUgi, keyadminUgi, userUgi);
    }

    /**
     * Run a full key life cycle test using the provided configuration and users.
     *
     * @param conf the configuration
     * @param hdfs the user to use as the hdfs user
     * @param keyadmin the user to use as the keyadmin user
     * @param user the user to use as the normal user
     * @throws Exception thrown if there is a test failure
     */
    private void doFullAclTest(final Configuration conf, final UserGroupInformation hdfsUgi,
            final UserGroupInformation keyadminUgi, final UserGroupInformation userUgi) throws Exception {

        try {
            setup(conf);

            // Create a test key
            assertTrue("Exception during creation of key " + KEY1 + " by " + keyadminUgi.getUserName(),
                    createKey(keyadminUgi, KEY1, conf));

            // Fail to create a test key
            assertFalse("Allowed creation of key " + KEY2 + " by " + hdfsUgi.getUserName(),
                    createKey(hdfsUgi, KEY2, conf));
            assertFalse("Allowed creation of key " + KEY2 + " by " + userUgi.getUserName(),
                    createKey(userUgi, KEY2, conf));

            // Create a directory and chown it to the normal user.
            fs.mkdirs(ZONE1);
            fs.setOwner(ZONE1, userUgi.getUserName(), userUgi.getPrimaryGroupName());

            // Create an EZ
            assertTrue("Exception during creation of EZ " + ZONE1 + " by " + hdfsUgi.getUserName() + " using key "
                    + KEY1, createEncryptionZone(hdfsUgi, KEY1, ZONE1));

            // Fail to create an EZ
            assertFalse(
                    "Allowed creation of EZ " + ZONE2 + " by " + keyadminUgi.getUserName() + " using key " + KEY1,
                    createEncryptionZone(keyadminUgi, KEY1, ZONE2));
            assertFalse("Allowed creation of EZ " + ZONE2 + " by " + userUgi.getUserName() + " using key " + KEY1,
                    createEncryptionZone(userUgi, KEY1, ZONE2));

            // Create a file in the zone
            assertTrue("Exception during creation of file " + FILE1 + " by " + userUgi.getUserName(),
                    createFile(userUgi, FILE1, TEXT));

            // Fail to create a file in the zone
            assertFalse("Allowed creation of file " + FILE1A + " by " + hdfsUgi.getUserName(),
                    createFile(hdfsUgi, FILE1A, TEXT));
            assertFalse("Allowed creation of file " + FILE1A + " by " + keyadminUgi.getUserName(),
                    createFile(keyadminUgi, FILE1A, TEXT));

            // Read a file in the zone
            assertTrue("Exception while reading file " + FILE1 + " by " + userUgi.getUserName(),
                    compareFile(userUgi, FILE1, TEXT));

            // Fail to read a file in the zone
            assertFalse("Allowed reading of file " + FILE1 + " by " + hdfsUgi.getUserName(),
                    compareFile(hdfsUgi, FILE1, TEXT));
            assertFalse("Allowed reading of file " + FILE1 + " by " + keyadminUgi.getUserName(),
                    compareFile(keyadminUgi, FILE1, TEXT));

            // Remove the zone
            fs.delete(ZONE1, true);

            // Fail to remove the key
            assertFalse("Allowed deletion of file " + FILE1 + " by " + hdfsUgi.getUserName(),
                    deleteKey(hdfsUgi, KEY1));
            assertFalse("Allowed deletion of file " + FILE1 + " by " + userUgi.getUserName(),
                    deleteKey(userUgi, KEY1));

            // Remove
            assertTrue("Exception during deletion of file " + FILE1 + " by " + keyadminUgi.getUserName(),
                    deleteKey(keyadminUgi, KEY1));
        } finally {
            fs.delete(ZONE1, true);
            fs.delete(ZONE2, true);
            teardown();
        }
    }

    /**
     * Test that key creation is correctly governed by ACLs.
     * @throws Exception thrown if setup fails
     */
    @Test
    public void testCreateKey() throws Exception {
        Configuration conf = new Configuration();

        // Correct config with whitelist ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf);

            assertTrue("Exception during key creation with correct config" + " using whitelist key ACLs",
                    createKey(realUgi, KEY1, conf));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Correct config with default ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf);

            assertTrue("Exception during key creation with correct config" + " using default key ACLs",
                    createKey(realUgi, KEY2, conf));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because of blacklist
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.CREATE", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf);

            assertFalse("Allowed key creation with blacklist for CREATE", createKey(realUgi, KEY3, conf));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Missing KMS ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", " ");
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf);

            assertFalse("Allowed key creation without CREATE KMS ACL", createKey(realUgi, KEY3, conf));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Missing key ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", realUgi.getUserName());

        try {
            setup(conf);

            assertFalse("Allowed key creation without MANAGMENT key ACL", createKey(realUgi, KEY3, conf));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because the key ACL set ignores the default ACL set for key3
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());
        conf.set(KeyAuthorizationKeyProvider.KEY_ACL + KEY3 + ".DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf);

            assertFalse("Allowed key creation when default key ACL should have been" + " overridden by key ACL",
                    createKey(realUgi, KEY3, conf));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Allowed because the default setting for KMS ACLs is fully permissive
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf);

            assertTrue("Exception during key creation with default KMS ACLs", createKey(realUgi, KEY3, conf));
        } finally {
            teardown();
        }
    }

    /**
     * Test that zone creation is correctly governed by ACLs.
     * @throws Exception thrown if setup fails
     */
    @Test
    public void testCreateEncryptionZone() throws Exception {
        Configuration conf = new Configuration();

        // Create a test key
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf);

            assertTrue("Exception during key creation", createKey(realUgi, KEY1, conf));
        } finally {
            teardown();
        }

        // We tear everything down and then restart it with the ACLs we want to
        // test so that there's no contamination from the ACLs needed for setup.
        // To make that work, we have to tell the setup() method not to create a
        // new KMS directory.
        conf = new Configuration();

        // Correct config with whitelist ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE1);

            assertTrue("Exception during zone creation with correct config using" + " whitelist key ACLs",
                    createEncryptionZone(realUgi, KEY1, ZONE1));
        } finally {
            fs.delete(ZONE1, true);
            teardown();
        }

        conf = new Configuration();

        // Correct config with default ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE2);

            assertTrue("Exception during zone creation with correct config using" + " default key ACLs",
                    createEncryptionZone(realUgi, KEY1, ZONE2));
        } finally {
            fs.delete(ZONE2, true);
            teardown();
        }

        conf = new Configuration();

        // Denied because the key ACL set ignores the default ACL set for key1
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KeyAuthorizationKeyProvider.KEY_ACL + KEY1 + ".DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE3);

            assertFalse(
                    "Allowed creation of zone when default key ACLs should have" + " been overridden by key ACL",
                    createEncryptionZone(realUgi, KEY1, ZONE3));
        } finally {
            fs.delete(ZONE3, true);
            teardown();
        }

        conf = new Configuration();

        // Correct config with blacklist
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE3);

            assertFalse("Allowed zone creation of zone with blacklisted GET_METADATA",
                    createEncryptionZone(realUgi, KEY1, ZONE3));
        } finally {
            fs.delete(ZONE3, true);
            teardown();
        }

        conf = new Configuration();

        // Correct config with blacklist
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE3);

            assertFalse("Allowed zone creation of zone with blacklisted GENERATE_EEK",
                    createEncryptionZone(realUgi, KEY1, ZONE3));
        } finally {
            fs.delete(ZONE3, true);
            teardown();
        }

        conf = new Configuration();

        // Missing KMS ACL but works because defaults for KMS ACLs are fully
        // permissive
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE3);

            assertTrue("Exception during zone creation with default KMS ACLs",
                    createEncryptionZone(realUgi, KEY1, ZONE3));
        } finally {
            fs.delete(ZONE3, true);
            teardown();
        }

        conf = new Configuration();

        // Missing GET_METADATA KMS ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", " ");
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE4);

            assertFalse("Allowed zone creation without GET_METADATA KMS ACL",
                    createEncryptionZone(realUgi, KEY1, ZONE4));
        } finally {
            fs.delete(ZONE4, true);
            teardown();
        }

        conf = new Configuration();

        // Missing GET_METADATA KMS ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", " ");
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE4);

            assertFalse("Allowed zone creation without GENERATE_EEK KMS ACL",
                    createEncryptionZone(realUgi, KEY1, ZONE4));
        } finally {
            fs.delete(ZONE4, true);
            teardown();
        }

        conf = new Configuration();

        // Missing READ key ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE4);

            assertFalse("Allowed zone creation without READ ACL", createEncryptionZone(realUgi, KEY1, ZONE4));
        } finally {
            fs.delete(ZONE4, true);
            teardown();
        }

        conf = new Configuration();

        // Missing GENERATE_EEK key ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "READ", realUgi.getUserName());

        try {
            setup(conf, false);

            fs.mkdirs(ZONE4);

            assertFalse("Allowed zone creation without GENERATE_EEK ACL",
                    createEncryptionZone(realUgi, KEY1, ZONE4));
        } finally {
            fs.delete(ZONE4, true);
            teardown();
        }
    }

    /**
     * Test that in-zone file creation is correctly governed by ACLs.
     * @throws Exception thrown if setup fails
     */
    @Test
    public void testCreateFileInEncryptionZone() throws Exception {
        Configuration conf = new Configuration();

        // Create a test key
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        assertTrue(new File(kmsDir, "kms.keystore").length() == 0);

        try {
            setup(conf);

            assertTrue("Exception during key creation", createKey(realUgi, KEY1, conf));
            fs.mkdirs(ZONE1);
            assertTrue("Exception during zone creation", createEncryptionZone(realUgi, KEY1, ZONE1));
            fs.mkdirs(ZONE2);
            assertTrue("Exception during zone creation", createEncryptionZone(realUgi, KEY1, ZONE2));
            fs.mkdirs(ZONE3);
            assertTrue("Exception during zone creation", createEncryptionZone(realUgi, KEY1, ZONE3));
            fs.mkdirs(ZONE4);
            assertTrue("Exception during zone creation", createEncryptionZone(realUgi, KEY1, ZONE4));
        } catch (Throwable ex) {
            fs.delete(ZONE1, true);
            fs.delete(ZONE2, true);
            fs.delete(ZONE3, true);
            fs.delete(ZONE4, true);

            throw ex;
        } finally {
            teardown();
        }

        // We tear everything down and then restart it with the ACLs we want to
        // test so that there's no contamination from the ACLs needed for setup.
        // To make that work, we have to tell the setup() method not to create a
        // new KMS directory or DFS dierctory.

        conf = new Configuration();

        // Correct config with whitelist ACLs
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertTrue("Exception during file creation with correct config" + " using whitelist ACL",
                    createFile(realUgi, FILE1, TEXT));
        } finally {
            fs.delete(ZONE1, true);
            teardown();
        }

        conf = new Configuration();

        // Correct config with default ACLs
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertTrue("Exception during file creation with correct config" + " using whitelist ACL",
                    createFile(realUgi, FILE2, TEXT));
        } finally {
            fs.delete(ZONE2, true);
            teardown();
        }

        conf = new Configuration();

        // Denied because the key ACL set ignores the default ACL set for key1
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());
        conf.set(KeyAuthorizationKeyProvider.KEY_ACL + KEY1 + ".READ", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file creation when default key ACLs should have been" + " overridden by key ACL",
                    createFile(realUgi, FILE3, TEXT));
        } catch (Exception ex) {
            fs.delete(ZONE3, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied by blacklist
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file creation with blacklist for GENERATE_EEK", createFile(realUgi, FILE3, TEXT));
        } catch (Exception ex) {
            fs.delete(ZONE3, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied by blacklist
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file creation with blacklist for DECRYPT_EEK", createFile(realUgi, FILE3, TEXT));
        } catch (Exception ex) {
            fs.delete(ZONE3, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Allowed because default KMS ACLs are fully permissive
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertTrue("Exception during file creation with default KMS ACLs", createFile(realUgi, FILE3, TEXT));
        } catch (Exception ex) {
            fs.delete(ZONE3, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because of missing GENERATE_EEK KMS ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", " ");
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file creation without GENERATE_EEK KMS ACL", createFile(realUgi, FILE4, TEXT));
        } catch (Exception ex) {
            fs.delete(ZONE3, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because of missing DECRYPT_EEK KMS ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", " ");
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file creation without DECRYPT_EEK KMS ACL", createFile(realUgi, FILE3, TEXT));
        } catch (Exception ex) {
            fs.delete(ZONE3, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because of missing GENERATE_EEK key ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file creation without GENERATE_EEK key ACL", createFile(realUgi, FILE3, TEXT));
        } catch (Exception ex) {
            fs.delete(ZONE3, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because of missing DECRYPT_EEK key ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file creation without DECRYPT_EEK key ACL", createFile(realUgi, FILE3, TEXT));
        } catch (Exception ex) {
            fs.delete(ZONE3, true);

            throw ex;
        } finally {
            teardown();
        }
    }

    /**
     * Test that in-zone file read is correctly governed by ACLs.
     * @throws Exception thrown if setup fails
     */
    @Test
    public void testReadFileInEncryptionZone() throws Exception {
        Configuration conf = new Configuration();

        // Create a test key
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GET_METADATA", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "READ", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "GENERATE_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        assertTrue(new File(kmsDir, "kms.keystore").length() == 0);

        try {
            setup(conf);

            assertTrue("Exception during key creation", createKey(realUgi, KEY1, conf));
            fs.mkdirs(ZONE1);
            assertTrue("Exception during zone creation", createEncryptionZone(realUgi, KEY1, ZONE1));
            assertTrue("Exception during file creation", createFile(realUgi, FILE1, TEXT));
        } catch (Throwable ex) {
            fs.delete(ZONE1, true);

            throw ex;
        } finally {
            teardown();
        }

        // We tear everything down and then restart it with the ACLs we want to
        // test so that there's no contamination from the ACLs needed for setup.
        // To make that work, we have to tell the setup() method not to create a
        // new KMS directory or DFS dierctory.

        conf = new Configuration();

        // Correct config with whitelist ACLs
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertTrue("Exception while reading file with correct config with" + " whitelist ACLs",
                    compareFile(realUgi, FILE1, TEXT));
        } catch (Throwable ex) {
            fs.delete(ZONE1, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Correct config with default ACLs
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertTrue("Exception while reading file with correct config" + " with default ACLs",
                    compareFile(realUgi, FILE1, TEXT));
        } catch (Throwable ex) {
            fs.delete(ZONE1, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because the key ACL set ignores the default ACL set for key1
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());
        conf.set(KeyAuthorizationKeyProvider.KEY_ACL + KEY1 + ".READ", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file read when default key ACLs should have been" + " overridden by key ACL",
                    compareFile(realUgi, FILE1, TEXT));
        } catch (Throwable ex) {
            fs.delete(ZONE1, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied by blacklist
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.DECRYPT_EEK", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file read with blacklist for DECRYPT_EEK", compareFile(realUgi, FILE1, TEXT));
        } catch (Throwable ex) {
            fs.delete(ZONE1, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Allowed because default KMS ACLs are fully permissive
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertTrue("Exception while reading file with default KMS ACLs", compareFile(realUgi, FILE1, TEXT));
        } catch (Throwable ex) {
            fs.delete(ZONE1, true);

            throw ex;
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because of missing DECRYPT_EEK KMS ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DECRYPT_EEK", " ");
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false, false);

            assertFalse("Allowed file read without DECRYPT_EEK KMS ACL", compareFile(realUgi, FILE1, TEXT));
        } catch (Throwable ex) {
            fs.delete(ZONE1, true);

            throw ex;
        } finally {
            teardown();
        }

        // Denied because of missing DECRYPT_EEK key ACL
        conf = new Configuration();

        try {
            setup(conf, false, false);

            assertFalse("Allowed file read without DECRYPT_EEK key ACL", compareFile(realUgi, FILE1, TEXT));
        } catch (Throwable ex) {
            fs.delete(ZONE1, true);

            throw ex;
        } finally {
            teardown();
        }
    }

    /**
     * Test that key deletion is correctly governed by ACLs.
     * @throws Exception thrown if setup fails
     */
    @Test
    public void testDeleteKey() throws Exception {
        Configuration conf = new Configuration();

        // Create a test key
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.CREATE", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf);

            assertTrue("Exception during key creation", createKey(realUgi, KEY1, conf));
            assertTrue("Exception during key creation", createKey(realUgi, KEY2, conf));
            assertTrue("Exception during key creation", createKey(realUgi, KEY3, conf));
        } finally {
            teardown();
        }

        // We tear everything down and then restart it with the ACLs we want to
        // test so that there's no contamination from the ACLs needed for setup.
        // To make that work, we have to tell the setup() method not to create a
        // new KMS directory.

        conf = new Configuration();

        // Correct config with whitelist ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DELETE", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf, false);

            assertTrue("Exception during key deletion with correct config" + " using whitelist key ACLs",
                    deleteKey(realUgi, KEY1));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Correct config with default ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DELETE", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf, false);

            assertTrue("Exception during key deletion with correct config" + " using default key ACLs",
                    deleteKey(realUgi, KEY2));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because of blacklist
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DELETE", realUgi.getUserName());
        conf.set(KMSConfiguration.CONFIG_PREFIX + "blacklist.DELETE", realUgi.getUserName());
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf, false);

            assertFalse("Allowed key deletion with blacklist for DELETE", deleteKey(realUgi, KEY3));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Missing KMS ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DELETE", " ");
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf, false);

            assertFalse("Allowed key deletion without DELETE KMS ACL", deleteKey(realUgi, KEY3));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Missing key ACL
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DELETE", realUgi.getUserName());

        try {
            setup(conf, false);

            assertFalse("Allowed key deletion without MANAGMENT key ACL", deleteKey(realUgi, KEY3));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Denied because the key ACL set ignores the default ACL set for key3
        conf.set(KMSConfiguration.CONFIG_PREFIX + "acl.DELETE", realUgi.getUserName());
        conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());
        conf.set(KeyAuthorizationKeyProvider.KEY_ACL + KEY3 + ".DECRYPT_EEK", realUgi.getUserName());

        try {
            setup(conf, false);

            assertFalse("Allowed key deletion when default key ACL should have been" + " overridden by key ACL",
                    deleteKey(realUgi, KEY3));
        } finally {
            teardown();
        }

        conf = new Configuration();

        // Allowed because the default setting for KMS ACLs is fully permissive
        conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", realUgi.getUserName());

        try {
            setup(conf, false);

            assertTrue("Exception during key deletion with default KMS ACLs", deleteKey(realUgi, KEY3));
        } finally {
            teardown();
        }
    }

    /**
     * Create a key as the specified user.
     *
     * @param ugi the target user
     * @param key the target key
     * @param conf the configuration
     * @return whether the key creation succeeded
     */
    private boolean createKey(UserGroupInformation ugi, final String key, final Configuration conf) {

        return doUserOp(ugi, new UserOp() {
            @Override
            public void execute() throws IOException {
                try {
                    DFSTestUtil.createKey(key, cluster, conf);
                } catch (NoSuchAlgorithmException ex) {
                    throw new IOException(ex);
                }
            }
        });
    }

    /**
     * Create a zone as the specified user.
     *
     * @param ugi the target user
     * @param key the target key
     * @param zone the target zone
     * @return whether the zone creation succeeded
     */
    private boolean createEncryptionZone(UserGroupInformation ugi, final String key, final Path zone) {

        return doUserOp(ugi, new UserOp() {
            @Override
            public void execute() throws IOException {
                cluster.getFileSystem().createEncryptionZone(zone, key);
            }
        });
    }

    /**
     * Create a file as the specified user.
     *
     * @param ugi the target user
     * @param file the target file
     * @param text the target file contents
     * @return whether the file creation succeeded
     */
    private boolean createFile(UserGroupInformation ugi, final Path file, final String text) {

        return doUserOp(ugi, new UserOp() {
            @Override
            public void execute() throws IOException {
                FSDataOutputStream dout = cluster.getFileSystem().create(file);
                PrintWriter out = new PrintWriter(new OutputStreamWriter(dout));

                out.println(text);
                out.close();
            }
        });
    }

    /**
     * Read a file as the specified user and compare the contents to expectations.
     *
     * @param ugi the target user
     * @param file the target file
     * @param text the expected file contents
     * @return true if the file read succeeded and the contents were as expected
     */
    private boolean compareFile(UserGroupInformation ugi, final Path file, final String text) {

        return doUserOp(ugi, new UserOp() {
            @Override
            public void execute() throws IOException {
                FSDataInputStream din = cluster.getFileSystem().open(file);
                BufferedReader in = new BufferedReader(new InputStreamReader(din));

                assertEquals("The text read does not match the text written", text, in.readLine());
            }
        });
    }

    /**
     * Delete a key as the specified user.
     *
     * @param ugi the target user
     * @param key the target key
     * @return whether the key deletion succeeded
     */
    private boolean deleteKey(UserGroupInformation ugi, final String key) throws IOException, InterruptedException {

        return doUserOp(ugi, new UserOp() {
            @Override
            public void execute() throws IOException {
                cluster.getNameNode().getNamesystem().getProvider().deleteKey(key);
            }
        });
    }

    /**
     * Perform an operation as the given user.  This method requires setting the
     * login user. This method does not restore the login user to the setting
     * from prior to the method call.
     *
     * @param ugi the target user
     * @param op the operation to perform
     * @return true if the operation succeeded without throwing an exception
     */
    private boolean doUserOp(UserGroupInformation ugi, final UserOp op) {
        UserGroupInformation.setLoginUser(ugi);

        // Create a test key
        return ugi.doAs(new PrivilegedAction<Boolean>() {
            @Override
            public Boolean run() {
                try {
                    op.execute();

                    return true;
                } catch (IOException ex) {
                    LOG.error("IOException thrown during doAs() operation", ex);

                    return false;
                }
            }
        });
    }

    /**
     * Simple interface that defines an operation to perform.
     */
    private static interface UserOp {
        public void execute() throws IOException;
    }
}