zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From an...@apache.org
Subject [2/2] zookeeper git commit: ZOOKEEPER-3148: Add Kerberos tests for modern JDKs
Date Tue, 25 Sep 2018 11:25:36 GMT
ZOOKEEPER-3148: Add Kerberos tests for modern JDKs

- This change is only for 3.4 branch, which MUST be compatible with JDK6
- Add Kerby and upgrade commons-io to 2.6, as needed by Kerby
- Copy Kerberos tests from branch-3.5 in a new package o.a.zookeeper.server.quorum.authnew
- Mark Legacy Kerberos tests to be executed only on JDK6 VMs
- Mark New Kerberos tests not to be executed on JDK6 VMs
- Java version is done using system property, this is the safer way without introducing external libs

Author: Enrico Olivelli <eolivelli@apache.org>

Reviewers: hanm@apache.org, andor@apache.org

Closes #626 from eolivelli/fix/34-kerby-jdk11


Project: http://git-wip-us.apache.org/repos/asf/zookeeper/repo
Commit: http://git-wip-us.apache.org/repos/asf/zookeeper/commit/7b25a707
Tree: http://git-wip-us.apache.org/repos/asf/zookeeper/tree/7b25a707
Diff: http://git-wip-us.apache.org/repos/asf/zookeeper/diff/7b25a707

Branch: refs/heads/branch-3.4
Commit: 7b25a70787d1ffb6e6c4bf0389cfcb3734be0bb1
Parents: 29cc5c1
Author: Enrico Olivelli <eolivelli@apache.org>
Authored: Tue Sep 25 13:25:29 2018 +0200
Committer: Andor Molnar <andor@apache.org>
Committed: Tue Sep 25 13:25:29 2018 +0200

----------------------------------------------------------------------
 build.xml                                       |   3 +-
 ivy.xml                                         |  38 ++
 .../auth/ApacheDSKerberosSecurityTestcase.java  | 120 ++++
 .../server/quorum/auth/ApacheDSMiniKdc.java     | 574 +++++++++++++++++++
 .../server/quorum/auth/ApacheDSMiniKdcTest.java | 185 ++++++
 .../auth/ApacheDSQuorumKerberosAuthTest.java    | 124 ++++
 ...ApacheDSQuorumKerberosHostBasedAuthTest.java | 200 +++++++
 .../quorum/auth/KerberosSecurityTestcase.java   |   3 +-
 .../zookeeper/server/quorum/auth/MiniKdc.java   | 346 +++--------
 .../server/quorum/auth/MiniKdcTest.java         |  18 +-
 .../quorum/auth/QuorumKerberosAuthTest.java     |  24 +-
 .../auth/QuorumKerberosHostBasedAuthTest.java   |  32 +-
 12 files changed, 1382 insertions(+), 285 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/build.xml
----------------------------------------------------------------------
diff --git a/build.xml b/build.xml
index 4618f5c..4b945c8 100644
--- a/build.xml
+++ b/build.xml
@@ -43,7 +43,8 @@ xmlns:cs="antlib:com.puppycrawl.tools.checkstyle">
     <property name="mockito.version" value="1.8.5"/>
     <property name="checkstyle.version" value="6.1.1"/>
     <property name="commons-collections.version" value="3.2.2"/>
-    <property name="commons-io.version" value="2.4"/>
+    <property name="commons-io.version" value="2.6"/>
+    <property name="kerby.version" value="1.1.0"/>
 
     <property name="apache-directory-server.version" value="2.0.0-M15"/>
     <property name="apache-directory-api.version" value="1.0.0-M20"/>

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/ivy.xml
----------------------------------------------------------------------
diff --git a/ivy.xml b/ivy.xml
index 8e5f198..dbc7f81 100644
--- a/ivy.xml
+++ b/ivy.xml
@@ -98,6 +98,7 @@
         <exclude org="org.apache.directory.api" module="api-ldap-schema-data"/>
         <exclude org="org.slf4j" module="slf4j-api"/>
         <exclude org="commons-collections" module="commons-collections"/>
+        <exclude org="commons-io" module="commons-io"/>
     </dependency>
     <dependency org="org.apache.directory.server" name="apacheds-interceptor-kerberos" rev="${apache-directory-server.version}" conf="test->default">
         <exclude org="org.apache.directory.api" module="api-ldap-schema-data"/>
@@ -108,16 +109,19 @@
         <exclude org="org.apache.directory.api" module="api-ldap-schema-data"/>
         <exclude org="commons-collections" module="commons-collections"/>
         <exclude org="org.slf4j" module="slf4j-api"/>
+        <exclude org="commons-io" module="commons-io"/>
     </dependency>
     <dependency org="org.apache.directory.server" name="apacheds-protocol-kerberos" rev="${apache-directory-server.version}" conf="test->default">
         <exclude org="org.apache.directory.api" module="api-ldap-schema-data"/>
         <exclude org="commons-collections" module="commons-collections"/>
         <exclude org="org.slf4j" module="slf4j-api"/>
+        <exclude org="commons-io" module="commons-io"/>
     </dependency>
     <dependency org="org.apache.directory.server" name="apacheds-ldif-partition" rev="${apache-directory-server.version}" conf="test->default">
         <exclude org="org.apache.directory.api" module="api-ldap-schema-data"/>
         <exclude org="commons-collections" module="commons-collections"/>
         <exclude org="org.slf4j" module="slf4j-api"/>
+        <exclude org="commons-io" module="commons-io"/>
     </dependency>
     <dependency org="org.apache.directory.server" name="apacheds-mavibot-partition" rev="${apache-directory-server.version}" conf="test->default">
         <exclude org="org.apache.directory.api" module="api-ldap-schema-data"/>
@@ -125,6 +129,7 @@
         <exclude org="org.slf4j" module="slf4j-log4j12"/>
         <exclude org="log4j" module="log4j"/>
         <exclude org="org.slf4j" module="slf4j-api"/>
+        <exclude org="commons-io" module="commons-io"/>
     </dependency>
     <dependency org="org.apache.directory.api" name="api-all" rev="${apache-directory-api.version}" conf="test->default">
         <exclude org="xml-apis" module="xml-apis"/>
@@ -132,18 +137,51 @@
         <exclude org="dom4j" module="dom4j"/>
         <exclude org="commons-collections" module="commons-collections"/>
         <exclude org="org.slf4j" module="slf4j-api"/>
+        <exclude org="commons-io" module="commons-io"/>
     </dependency>
     <dependency org="org.apache.directory.server" name="apacheds-jdbm-partition" rev="${apache-directory-server.version}" conf="test->default">
         <exclude org="org.apache.directory.api" module="api-ldap-schema-data"/>
         <exclude org="commons-collections" module="commons-collections"/>
         <exclude org="org.slf4j" module="slf4j-api"/>
+        <exclude org="commons-io" module="commons-io"/>
     </dependency>
     <dependency org="org.apache.directory.server" name="apacheds-protocol-ldap" rev="${apache-directory-server.version}" conf="test->default">
         <exclude org="org.apache.directory.api" module="api-ldap-schema-data"/>
         <exclude org="commons-collections" module="commons-collections"/>
         <exclude org="org.slf4j" module="slf4j-api"/>
+        <exclude org="commons-io" module="commons-io"/>
     </dependency>
 
+    <dependency org="org.apache.kerby" name="kerby-config" rev="${kerby.version}" conf="test->default">
+          <exclude org="org.slf4j" module="slf4j-api"/>
+          <exclude org="org.slf4j" module="slf4j-log4j12"/>
+    </dependency>
+    <dependency org="org.apache.kerby" name="kerb-simplekdc" rev="${kerby.version}" conf="test->default">
+        <exclude org="org.slf4j" module="slf4j-api"/>
+    </dependency>
+    <dependency org="org.apache.kerby" name="kerb-core"
+                rev="${kerby.version}" conf="test->default">
+        <exclude org="org.slf4j" module="slf4j-api"/>
+    </dependency>
+    <dependency org="org.apache.kerby" name="kerb-server"
+                rev="${kerby.version}" conf="test->default"/>
+    <dependency org="org.apache.kerby" name="kerb-common"
+                rev="${kerby.version}" conf="test->default"/>
+    <dependency org="org.apache.kerby" name="kerb-admin"
+                rev="${kerby.version}" conf="test->default"/>
+    <dependency org="org.apache.kerby" name="kerb-identity"
+                rev="${kerby.version}" conf="test->default"/>
+    <dependency org="org.apache.kerby" name="kerb-client"
+                rev="${kerby.version}" conf="test->default"/>
+    <dependency org="org.apache.kerby" name="kerb-util"
+                rev="${kerby.version}" conf="test->default"/>
+    <dependency org="org.apache.kerby" name="kerb-crypto"
+                rev="${kerby.version}" conf="test->default"/>
+    <dependency org="org.apache.kerby" name="kerby-util"
+                rev="${kerby.version}" conf="test->default"/>
+    <dependency org="org.apache.kerby" name="kerby-asn1"
+                rev="${kerby.version}" conf="test->default"/>
+
     <dependency org="org.hamcrest" name="hamcrest-all" rev="${hamcrest.version}" conf="test->default" />
 
     <dependency org="org.openclover" name="clover" rev="${clover.version}" conf="clover->default"/>

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSKerberosSecurityTestcase.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSKerberosSecurityTestcase.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSKerberosSecurityTestcase.java
new file mode 100644
index 0000000..ff24728
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSKerberosSecurityTestcase.java
@@ -0,0 +1,120 @@
+/**
+ * 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.zookeeper.server.quorum.auth;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+
+/*
+ * This code is originally from HDFS, see the similarly named file there
+ * in case of bug fixing, history, etc.
+ *
+ * Branch : trunk
+ * Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33
+ */
+
+/**
+ * KerberosSecurityTestcase provides a base class for using MiniKdc with other
+ * test cases. KerberosSecurityTestcase starts the MiniKdc (@Before) before
+ * running tests, and stop the MiniKdc (@After) after the testcases, using
+ * default settings (working dir and kdc configurations).
+ * <p>
+ * Users can directly inherit this class and implement their own test functions
+ * using the default settings, or override functions getTestDir() and
+ * createMiniKdcConf() to provide new settings.
+ */
+public class ApacheDSKerberosSecurityTestcase extends QuorumAuthTestBase {
+    private static ApacheDSMiniKdc kdc;
+    private static File workDir;
+    private static Properties conf;
+
+    @BeforeClass
+    public static void setUpSasl() throws Exception {
+        startMiniKdc();
+    }
+
+    @AfterClass
+    public static void tearDownSasl() throws Exception {
+        stopMiniKdc();
+        FileUtils.deleteQuietly(workDir);
+    }
+
+    public static void startMiniKdc() throws Exception {
+        createTestDir();
+        createMiniKdcConf();
+
+        kdc = new ApacheDSMiniKdc(conf, workDir);
+        kdc.start();
+    }
+
+    /**
+     * Create a working directory, it should be the build directory. Under this
+     * directory an ApacheDS working directory will be created, this directory
+     * will be deleted when the MiniKdc stops.
+     *
+     * @throws IOException
+     */
+    public static void createTestDir() throws IOException {
+        workDir = createTmpDir(
+                new File(System.getProperty("build.test.dir", "build")));
+    }
+
+    static File createTmpDir(File parentDir) throws IOException {
+        File tmpFile = File.createTempFile("test", ".junit", parentDir);
+        // don't delete tmpFile - this ensures we don't attempt to create
+        // a tmpDir with a duplicate name
+        File tmpDir = new File(tmpFile + ".dir");
+        // never true if tmpfile does it's job
+        Assert.assertFalse(tmpDir.exists());
+        Assert.assertTrue(tmpDir.mkdirs());
+        return tmpDir;
+    }
+
+    /**
+     * Create a Kdc configuration
+     */
+    public static void createMiniKdcConf() {
+        conf = ApacheDSMiniKdc.createConf();
+    }
+
+    public static void stopMiniKdc() {
+        if (kdc != null) {
+            kdc.stop();
+        }
+    }
+
+    public static ApacheDSMiniKdc getKdc() {
+        return kdc;
+    }
+
+    public static File getWorkDir() {
+        return workDir;
+    }
+
+    public static Properties getConf() {
+        return conf;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSMiniKdc.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSMiniKdc.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSMiniKdc.java
new file mode 100644
index 0000000..49a71e5
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSMiniKdc.java
@@ -0,0 +1,574 @@
+/**
+ * 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.zookeeper.server.quorum.auth;
+
+import org.apache.commons.io.Charsets;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.text.StrSubstitutor;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.schemaextractor.SchemaLdifExtractor;
+import org.apache.directory.api.ldap.schemaextractor.impl.DefaultSchemaLdifExtractor;
+import org.apache.directory.api.ldap.schemaloader.LdifSchemaLoader;
+import org.apache.directory.api.ldap.schemamanager.impl.DefaultSchemaManager;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.api.CacheService;
+import org.apache.directory.server.core.api.DirectoryService;
+import org.apache.directory.server.core.api.InstanceLayout;
+import org.apache.directory.server.core.api.schema.SchemaPartition;
+import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.core.partition.ldif.LdifPartition;
+import org.apache.directory.server.kerberos.KerberosConfig;
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
+import org.apache.directory.server.kerberos.shared.keytab.Keytab;
+import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
+import org.apache.directory.server.protocol.shared.transport.TcpTransport;
+import org.apache.directory.server.protocol.shared.transport.UdpTransport;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.shared.kerberos.KerberosTime;
+import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
+import org.apache.directory.shared.kerberos.components.EncryptionKey;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.ldif.LdifEntry;
+import org.apache.directory.api.ldap.model.ldif.LdifReader;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Mini KDC based on Apache Directory Server that can be embedded in testcases
+ * or used from command line as a standalone KDC.
+ * <p>
+ * <b>From within testcases:</b>
+ * <p>
+ ApacheDSMiniKdc sets 2 System properties when started and un-sets them when stopped:
+ <ul>
+ * <li>java.security.krb5.conf: set to the MiniKDC real/host/port</li>
+ * <li>sun.security.krb5.debug: set to the debug value provided in the
+ * configuration</li>
+ * </ul>
+ Because of this, multiple ApacheDSMiniKdc instances cannot be started in parallel.
+ For example, running testcases in parallel that start a KDC each. To
+ accomplish this a single ApacheDSMiniKdc should be used for all testcases running
+ in parallel.
+ <p>
+ ApacheDSMiniKdc default configuration values are:
+ <ul>
+ *   <li>org.name=EXAMPLE (used to create the REALM)</li>
+ *   <li>org.domain=COM (used to create the REALM)</li>
+ *   <li>kdc.bind.address=localhost</li>
+ *   <li>kdc.port=0 (ephemeral port)</li>
+ *   <li>instance=DefaultKrbServer</li>
+ *   <li>max.ticket.lifetime=86400000 (1 day)</li>
+ *   <li>max.renewable.lifetime=604800000 (7 days)</li>
+ *   <li>transport=TCP</li>
+ *   <li>debug=false</li>
+ * </ul>
+ * The generated krb5.conf forces TCP connections.
+ */
+/*
+ * This code is originally from HDFS, see the file name ApacheDSMiniKdc there
+ * in case of bug fixing, history, etc.
+ *
+ * Branch : trunk
+ * Github Revision: 42e3a805117ff7cb054c2442f7b0e0cc54be63ad
+ */
+public class ApacheDSMiniKdc {
+
+    public static final String JAVA_SECURITY_KRB5_CONF =
+            "java.security.krb5.conf";
+    public static final String SUN_SECURITY_KRB5_DEBUG =
+            "sun.security.krb5.debug";
+    private static final File testData = new File(
+            System.getProperty("test.data.dir", "build/test/data"));
+
+    public static void main(String[] args) throws Exception {
+        if (args.length < 4) {
+            System.out.println("Arguments: <WORKDIR> <MINIKDCPROPERTIES> " +
+                    "<KEYTABFILE> [<PRINCIPALS>]+");
+            System.exit(1);
+        }
+        File workDir = new File(args[0]);
+        if (!workDir.exists()) {
+            throw new RuntimeException("Specified work directory does not exists: "
+                    + workDir.getAbsolutePath());
+        }
+        Properties conf = createConf();
+        File file = new File(args[1]);
+        if (!file.exists()) {
+            throw new RuntimeException("Specified configuration does not exists: "
+                    + file.getAbsolutePath());
+        }
+        Properties userConf = new Properties();
+        InputStreamReader r = null;
+        try {
+            r = new InputStreamReader(new FileInputStream(file), Charsets.UTF_8);
+            userConf.load(r);
+        } finally {
+            if (r != null) {
+                r.close();
+            }
+        }
+        for (Map.Entry<?, ?> entry : userConf.entrySet()) {
+            conf.put(entry.getKey(), entry.getValue());
+        }
+        final ApacheDSMiniKdc miniKdc = new ApacheDSMiniKdc(conf, workDir);
+        miniKdc.start();
+        File krb5conf = new File(workDir, "krb5.conf");
+        if (miniKdc.getKrb5conf().renameTo(krb5conf)) {
+            File keytabFile = new File(args[2]).getAbsoluteFile();
+            String[] principals = new String[args.length - 3];
+            System.arraycopy(args, 3, principals, 0, args.length - 3);
+            miniKdc.createPrincipal(keytabFile, principals);
+            System.out.println();
+            System.out.println("Standalone MiniKdc Running");
+            System.out.println("---------------------------------------------------");
+            System.out.println("  Realm           : " + miniKdc.getRealm());
+            System.out.println("  Running at      : " + miniKdc.getHost() + ":" +
+                    miniKdc.getHost());
+            System.out.println("  krb5conf        : " + krb5conf);
+            System.out.println();
+            System.out.println("  created keytab  : " + keytabFile);
+            System.out.println("  with principals : " + Arrays.asList(principals));
+            System.out.println();
+            System.out.println(" Do <CTRL-C> or kill <PID> to stop it");
+            System.out.println("---------------------------------------------------");
+            System.out.println();
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                @Override
+                public void run() {
+                    miniKdc.stop();
+                }
+            });
+        } else {
+            throw new RuntimeException("Cannot rename KDC's krb5conf to "
+                    + krb5conf.getAbsolutePath());
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(ApacheDSMiniKdc.class);
+
+    public static final String ORG_NAME = "org.name";
+    public static final String ORG_DOMAIN = "org.domain";
+    public static final String KDC_BIND_ADDRESS = "kdc.bind.address";
+    public static final String KDC_PORT = "kdc.port";
+    public static final String INSTANCE = "instance";
+    public static final String MAX_TICKET_LIFETIME = "max.ticket.lifetime";
+    public static final String MAX_RENEWABLE_LIFETIME = "max.renewable.lifetime";
+    public static final String TRANSPORT = "transport";
+    public static final String DEBUG = "debug";
+
+    private static final Set<String> PROPERTIES = new HashSet<String>();
+    private static final Properties DEFAULT_CONFIG = new Properties();
+
+    static {
+        PROPERTIES.add(ORG_NAME);
+        PROPERTIES.add(ORG_DOMAIN);
+        PROPERTIES.add(KDC_BIND_ADDRESS);
+        PROPERTIES.add(KDC_BIND_ADDRESS);
+        PROPERTIES.add(KDC_PORT);
+        PROPERTIES.add(INSTANCE);
+        PROPERTIES.add(TRANSPORT);
+        PROPERTIES.add(MAX_TICKET_LIFETIME);
+        PROPERTIES.add(MAX_RENEWABLE_LIFETIME);
+
+        DEFAULT_CONFIG.setProperty(KDC_BIND_ADDRESS, "localhost");
+        DEFAULT_CONFIG.setProperty(KDC_PORT, "0");
+        DEFAULT_CONFIG.setProperty(INSTANCE, "DefaultKrbServer");
+        DEFAULT_CONFIG.setProperty(ORG_NAME, "EXAMPLE");
+        DEFAULT_CONFIG.setProperty(ORG_DOMAIN, "COM");
+        DEFAULT_CONFIG.setProperty(TRANSPORT, "TCP");
+        DEFAULT_CONFIG.setProperty(MAX_TICKET_LIFETIME, "86400000");
+        DEFAULT_CONFIG.setProperty(MAX_RENEWABLE_LIFETIME, "604800000");
+        DEFAULT_CONFIG.setProperty(DEBUG, "true");
+    }
+
+    /**
+     * Convenience method that returns MiniKdc default configuration.
+     * <p>
+ The returned configuration is a copy, it can be customized before using
+ it to create a ApacheDSMiniKdc.
+     * @return a ApacheDSMiniKdc default configuration.
+     */
+    public static Properties createConf() {
+        return (Properties) DEFAULT_CONFIG.clone();
+    }
+
+    private Properties conf;
+    private DirectoryService ds;
+    private KdcServer kdc;
+    private int port;
+    private String realm;
+    private File workDir;
+    private File krb5conf;
+
+    /**
+     * Creates a MiniKdc.
+     *
+     * @param conf MiniKdc configuration.
+     * @param workDir working directory, it should be the build directory. Under
+     * this directory an ApacheDS working directory will be created, this
+     * directory will be deleted when the MiniKdc stops.
+     * @throws Exception thrown if the MiniKdc could not be created.
+     */
+    public ApacheDSMiniKdc(Properties conf, File workDir) throws Exception {
+        if (!conf.keySet().containsAll(PROPERTIES)) {
+            Set<String> missingProperties = new HashSet<String>(PROPERTIES);
+            missingProperties.removeAll(conf.keySet());
+            throw new IllegalArgumentException("Missing configuration properties: "
+                    + missingProperties);
+        }
+        this.workDir = new File(workDir, Long.toString(System.currentTimeMillis()));
+        if (!this.workDir.exists()
+                && !this.workDir.mkdirs()) {
+            throw new RuntimeException("Cannot create directory " + this.workDir);
+        }
+        LOG.info("Configuration:");
+        LOG.info("---------------------------------------------------------------");
+        for (Map.Entry<?, ?> entry : conf.entrySet()) {
+            LOG.info("  {}: {}", entry.getKey(), entry.getValue());
+        }
+        LOG.info("---------------------------------------------------------------");
+        this.conf = conf;
+        port = Integer.parseInt(conf.getProperty(KDC_PORT));
+        if (port == 0) {
+            ServerSocket ss = new ServerSocket(0, 1, InetAddress.getByName(conf.getProperty(KDC_BIND_ADDRESS)));
+            port = ss.getLocalPort();
+            ss.close();
+        }
+        String orgName = conf.getProperty(ORG_NAME);
+        String orgDomain = conf.getProperty(ORG_DOMAIN);
+        realm = orgName.toUpperCase(Locale.ENGLISH) + "."
+                + orgDomain.toUpperCase(Locale.ENGLISH);
+    }
+
+    /**
+     * Returns the port of the ApacheDSMiniKdc.
+     *
+     * @return the port of the ApacheDSMiniKdc.
+     */
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * Returns the host of the ApacheDSMiniKdc.
+     *
+     * @return the host of the ApacheDSMiniKdc.
+     */
+    public String getHost() {
+        return conf.getProperty(KDC_BIND_ADDRESS);
+    }
+
+    /**
+     * Returns the realm of the ApacheDSMiniKdc.
+     *
+     * @return the realm of the ApacheDSMiniKdc.
+     */
+    public String getRealm() {
+        return realm;
+    }
+
+    public File getKrb5conf() {
+        return krb5conf;
+    }
+
+    /**
+     * Starts the ApacheDSMiniKdc.
+     *
+     * @throws Exception thrown if the ApacheDSMiniKdc could not be started.
+     */
+    public synchronized void start() throws Exception {
+        if (kdc != null) {
+            throw new RuntimeException("Already started");
+        }
+        initDirectoryService();
+        initKDCServer();
+    }
+
+    private void initDirectoryService() throws Exception {
+        ds = new DefaultDirectoryService();
+        ds.setInstanceLayout(new InstanceLayout(workDir));
+
+        CacheService cacheService = new CacheService();
+        ds.setCacheService(cacheService);
+
+        // first load the schema
+        InstanceLayout instanceLayout = ds.getInstanceLayout();
+        File schemaPartitionDirectory = new File(instanceLayout.getPartitionsDirectory(), "schema");
+        SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor(instanceLayout.getPartitionsDirectory());
+        extractor.extractOrCopy();
+
+        SchemaLoader loader = new LdifSchemaLoader(schemaPartitionDirectory);
+        SchemaManager schemaManager = new DefaultSchemaManager(loader);
+        schemaManager.loadAllEnabled();
+        ds.setSchemaManager(schemaManager);
+        // Init the LdifPartition with schema
+        LdifPartition schemaLdifPartition = new LdifPartition(schemaManager);
+        schemaLdifPartition.setPartitionPath(schemaPartitionDirectory.toURI());
+
+        // The schema partition
+        SchemaPartition schemaPartition = new SchemaPartition(schemaManager);
+        schemaPartition.setWrappedPartition(schemaLdifPartition);
+        ds.setSchemaPartition(schemaPartition);
+
+        JdbmPartition systemPartition = new JdbmPartition(ds.getSchemaManager());
+        systemPartition.setId("system");
+        systemPartition.setPartitionPath(
+                new File(ds.getInstanceLayout().getPartitionsDirectory(), systemPartition.getId()).toURI());
+        systemPartition.setSuffixDn(new Dn(ServerDNConstants.SYSTEM_DN));
+        systemPartition.setSchemaManager(ds.getSchemaManager());
+        ds.setSystemPartition(systemPartition);
+
+        ds.getChangeLog().setEnabled(false);
+        ds.setDenormalizeOpAttrsEnabled(true);
+        ds.addLast(new KeyDerivationInterceptor());
+
+        // create one partition
+        String orgName = conf.getProperty(ORG_NAME).toLowerCase(Locale.ENGLISH);
+        String orgDomain = conf.getProperty(ORG_DOMAIN).toLowerCase(Locale.ENGLISH);
+
+        JdbmPartition partition = new JdbmPartition(ds.getSchemaManager());
+        partition.setId(orgName);
+        partition.setPartitionPath(new File(ds.getInstanceLayout().getPartitionsDirectory(), orgName).toURI());
+        partition.setSuffixDn(new Dn("dc=" + orgName + ",dc=" + orgDomain));
+        ds.addPartition(partition);
+        // indexes
+        Set<Index<?, ?, String>> indexedAttributes = new HashSet<Index<?, ?, String>>();
+        indexedAttributes.add(new JdbmIndex<String, Entry>("objectClass", false));
+        indexedAttributes.add(new JdbmIndex<String, Entry>("dc", false));
+        indexedAttributes.add(new JdbmIndex<String, Entry>("ou", false));
+        partition.setIndexedAttributes(indexedAttributes);
+
+        // And start the ds
+        ds.setInstanceId(conf.getProperty(INSTANCE));
+        ds.startup();
+        // context entry, after ds.startup()
+        Dn dn = new Dn("dc=" + orgName + ",dc=" + orgDomain);
+        Entry entry = ds.newEntry(dn);
+        entry.add("objectClass", "top", "domain");
+        entry.add("dc", orgName);
+        ds.getAdminSession().add(entry);
+    }
+
+    private void initKDCServer() throws Exception {
+        String orgName = conf.getProperty(ORG_NAME);
+        String orgDomain = conf.getProperty(ORG_DOMAIN);
+        String bindAddress = conf.getProperty(KDC_BIND_ADDRESS);
+        final Map<String, String> map = new HashMap<String, String>();
+        map.put("0", orgName.toLowerCase(Locale.ENGLISH));
+        map.put("1", orgDomain.toLowerCase(Locale.ENGLISH));
+        map.put("2", orgName.toUpperCase(Locale.ENGLISH));
+        map.put("3", orgDomain.toUpperCase(Locale.ENGLISH));
+        map.put("4", bindAddress);
+
+        InputStream is1 = getMinikdcResourceAsStream("minikdc.ldiff");
+
+        SchemaManager schemaManager = ds.getSchemaManager();
+        LdifReader reader = null;
+
+        try {
+            final String content = StrSubstitutor.replace(IOUtils.toString(is1), map);
+            reader = new LdifReader(new StringReader(content));
+
+            for (LdifEntry ldifEntry : reader) {
+                ds.getAdminSession().add(new DefaultEntry(schemaManager, ldifEntry.getEntry()));
+            }
+        } finally {
+            IOUtils.closeQuietly(reader);
+            IOUtils.closeQuietly(is1);
+        }
+
+        KerberosConfig kerberosConfig = new KerberosConfig();
+        kerberosConfig.setMaximumRenewableLifetime(Long.parseLong(conf.getProperty(MAX_RENEWABLE_LIFETIME)));
+        kerberosConfig.setMaximumTicketLifetime(Long.parseLong(conf.getProperty(MAX_TICKET_LIFETIME)));
+        kerberosConfig.setSearchBaseDn(String.format("dc=%s,dc=%s", orgName, orgDomain));
+        kerberosConfig.setPaEncTimestampRequired(false);
+        kdc = new KdcServer(kerberosConfig);
+        kdc.setDirectoryService(ds);
+
+        // transport
+        String transport = conf.getProperty(TRANSPORT);
+        if (transport.trim().equals("TCP")) {
+            kdc.addTransports(new TcpTransport(bindAddress, port, 3, 50));
+        } else if (transport.trim().equals("UDP")) {
+            kdc.addTransports(new UdpTransport(port));
+        } else {
+            throw new IllegalArgumentException("Invalid transport: " + transport);
+        }
+        kdc.setServiceName(conf.getProperty(INSTANCE));
+        kdc.start();
+
+        StringBuilder sb = new StringBuilder();
+        InputStream is2 = getMinikdcResourceAsStream("minikdc-krb5.conf");
+
+        BufferedReader r = null;
+
+        try {
+            r = new BufferedReader(new InputStreamReader(is2, Charsets.UTF_8));
+            String line = r.readLine();
+
+            while (line != null) {
+                sb.append(line).append("{3}");
+                line = r.readLine();
+            }
+        } finally {
+            IOUtils.closeQuietly(r);
+            IOUtils.closeQuietly(is2);
+        }
+
+        krb5conf = new File(workDir, "krb5.conf").getAbsoluteFile();
+        FileUtils.writeStringToFile(krb5conf, MessageFormat.format(sb.toString(), getRealm(), getHost(),
+                Integer.toString(getPort()), System.getProperty("line.separator")));
+        System.setProperty(JAVA_SECURITY_KRB5_CONF, krb5conf.getAbsolutePath());
+
+        System.setProperty(SUN_SECURITY_KRB5_DEBUG, conf.getProperty(DEBUG, "false"));
+
+        // refresh the config
+        Class<?> classRef;
+        if (System.getProperty("java.vendor").contains("IBM")) {
+            classRef = Class.forName("com.ibm.security.krb5.internal.Config");
+        } else {
+            classRef = Class.forName("sun.security.krb5.Config");
+        }
+        Method refreshMethod = classRef.getMethod("refresh", new Class[0]);
+        refreshMethod.invoke(classRef, new Object[0]);
+
+        LOG.info("MiniKdc listening at port: {}", getPort());
+        LOG.info("MiniKdc setting JVM krb5.conf to: {}", krb5conf.getAbsolutePath());
+    }
+
+    private InputStream getMinikdcResourceAsStream(String resourceName)
+            throws FileNotFoundException {
+        File kdcResourceFile = new File(testData, "/kerberos/" + resourceName);
+        return new FileInputStream(kdcResourceFile);
+    }
+
+    /**
+     * Stops the ApacheDSMiniKdc
+     */
+    public synchronized void stop() {
+        if (kdc != null) {
+            System.getProperties().remove(JAVA_SECURITY_KRB5_CONF);
+            System.getProperties().remove(SUN_SECURITY_KRB5_DEBUG);
+            kdc.stop();
+            try {
+                ds.shutdown();
+            } catch (Exception ex) {
+                LOG.error("Could not shutdown ApacheDS properly: {}", ex.toString(), ex);
+            }
+        }
+        delete(workDir);
+    }
+
+    private void delete(File f) {
+        if (f.isFile()) {
+            if (! f.delete()) {
+                LOG.warn("WARNING: cannot delete file " + f.getAbsolutePath());
+            }
+        } else {
+            for (File c: f.listFiles()) {
+                delete(c);
+            }
+            if (! f.delete()) {
+                LOG.warn("WARNING: cannot delete directory " + f.getAbsolutePath());
+            }
+        }
+    }
+
+    /**
+     * Creates a principal in the KDC with the specified user and password.
+     *
+     * @param principal principal name, do not include the domain.
+     * @param password password.
+     * @throws Exception thrown if the principal could not be created.
+     */
+    public synchronized void createPrincipal(String principal, String password) throws Exception {
+        String orgName = conf.getProperty(ORG_NAME);
+        String orgDomain = conf.getProperty(ORG_DOMAIN);
+        String baseDn = "ou=users,dc=" + orgName.toLowerCase(Locale.ENGLISH) + ",dc="
+                + orgDomain.toLowerCase(Locale.ENGLISH);
+        String content = "dn: uid=" + principal + "," + baseDn + "\n" + "objectClass: top\n" + "objectClass: person\n"
+                + "objectClass: inetOrgPerson\n" + "objectClass: krb5principal\n" + "objectClass: krb5kdcentry\n"
+                + "cn: " + principal + "\n" + "sn: " + principal + "\n" + "uid: " + principal + "\n" + "userPassword: "
+                + password + "\n" + "krb5PrincipalName: " + principal + "@" + getRealm() + "\n"
+                + "krb5KeyVersionNumber: 0";
+
+        for (LdifEntry ldifEntry : new LdifReader(new StringReader(content))) {
+            ds.getAdminSession().add(new DefaultEntry(ds.getSchemaManager(), ldifEntry.getEntry()));
+        }
+    }
+
+    /**
+     * Creates multiple principals in the KDC and adds them to a keytab file.
+     *
+     * @param keytabFile keytab file to add the created principals.
+     * @param principals principals to add to the KDC, do not include the domain.
+     * @throws Exception thrown if the principals or the keytab file could not be
+     * created.
+     */
+    public synchronized void createPrincipal(File keytabFile,
+                                             String ... principals)
+            throws Exception {
+        String generatedPassword = UUID.randomUUID().toString();
+        Keytab keytab = new Keytab();
+        List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
+        for (String principal : principals) {
+            createPrincipal(principal, generatedPassword);
+            principal = principal + "@" + getRealm();
+            KerberosTime timestamp = new KerberosTime();
+            for (Map.Entry<EncryptionType, EncryptionKey> entry : KerberosKeyFactory
+                    .getKerberosKeys(principal, generatedPassword).entrySet()) {
+                EncryptionKey ekey = entry.getValue();
+                byte keyVersion = (byte) ekey.getKeyVersion();
+                entries.add(new KeytabEntry(principal, 1L, timestamp, keyVersion, ekey));
+            }
+        }
+        keytab.setEntries(entries);
+        keytab.write(keytabFile);
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSMiniKdcTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSMiniKdcTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSMiniKdcTest.java
new file mode 100644
index 0000000..bcad892
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSMiniKdcTest.java
@@ -0,0 +1,185 @@
+/**
+ * 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.zookeeper.server.quorum.auth;
+
+import org.apache.directory.server.kerberos.shared.keytab.Keytab;
+import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import java.io.File;
+import java.security.Principal;
+import java.util.Set;
+import java.util.Map;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Arrays;
+
+/*
+ * This code is originally from HDFS, see the file name TestMiniKdc there
+ * in case of bug fixing, history, etc.
+ *
+ * Branch : trunk
+ * Github Revision: 916140604ffef59466ba30832478311d3e6249bd
+ */
+public class ApacheDSMiniKdcTest extends ApacheDSKerberosSecurityTestcase {
+    private static final boolean IBM_JAVA = System.getProperty("java.vendor")
+            .contains("IBM");
+
+    @Test(timeout = 60000)
+    public void testMiniKdcStart() {
+        ApacheDSMiniKdc kdc = getKdc();
+        Assert.assertNotSame(0, kdc.getPort());
+    }
+
+    @Test(timeout = 60000)
+    public void testKeytabGen() throws Exception {
+        ApacheDSMiniKdc kdc = getKdc();
+        File workDir = getWorkDir();
+
+        kdc.createPrincipal(new File(workDir, "keytab"), "foo/bar", "bar/foo");
+        Keytab kt = Keytab.read(new File(workDir, "keytab"));
+
+        Set<String> principals = new HashSet<String>();
+        for (KeytabEntry entry : kt.getEntries()) {
+            principals.add(entry.getPrincipalName());
+        }
+        //here principals use \ instead of /
+        //because org.apache.directory.server.kerberos.shared.keytab.KeytabDecoder
+        // .getPrincipalName(IoBuffer buffer) use \\ when generates principal
+        Assert.assertEquals(new HashSet<String>(Arrays.asList(
+                "foo\\bar@" + kdc.getRealm(), "bar\\foo@" + kdc.getRealm())),
+                principals);
+      }
+
+    private static class KerberosConfiguration extends Configuration {
+        private String principal;
+        private String keytab;
+        private boolean isInitiator;
+
+        private KerberosConfiguration(String principal, File keytab,
+                boolean client) {
+            this.principal = principal;
+            this.keytab = keytab.getAbsolutePath();
+            this.isInitiator = client;
+        }
+
+        public static Configuration createClientConfig(String principal,
+                File keytab) {
+            return new KerberosConfiguration(principal, keytab, true);
+        }
+
+        public static Configuration createServerConfig(String principal,
+                File keytab) {
+            return new KerberosConfiguration(principal, keytab, false);
+        }
+
+        private static String getKrb5LoginModuleName() {
+            return System.getProperty("java.vendor").contains("IBM")
+                    ? "com.ibm.security.auth.module.Krb5LoginModule"
+                    : "com.sun.security.auth.module.Krb5LoginModule";
+        }
+
+        @Override
+        public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+            Map<String, String> options = new HashMap<String, String>();
+            options.put("principal", principal);
+            options.put("refreshKrb5Config", "true");
+            if (IBM_JAVA) {
+                options.put("useKeytab", keytab);
+                options.put("credsType", "both");
+            } else {
+                options.put("keyTab", keytab);
+                options.put("useKeyTab", "true");
+                options.put("storeKey", "true");
+                options.put("doNotPrompt", "true");
+                options.put("useTicketCache", "true");
+                options.put("renewTGT", "true");
+                options.put("isInitiator", Boolean.toString(isInitiator));
+            }
+            String ticketCache = System.getenv("KRB5CCNAME");
+            if (ticketCache != null) {
+                options.put("ticketCache", ticketCache);
+            }
+            options.put("debug", "true");
+
+            return new AppConfigurationEntry[] {
+                    new AppConfigurationEntry(getKrb5LoginModuleName(),
+                            AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                            options) };
+        }
+    }
+
+    @Test(timeout = 60000)
+    public void testKerberosLogin() throws Exception {
+        ApacheDSMiniKdc kdc = getKdc();
+        File workDir = getWorkDir();
+        LoginContext loginContext = null;
+        try {
+            String principal = "foo";
+            File keytab = new File(workDir, "foo.keytab");
+            kdc.createPrincipal(keytab, principal);
+
+            Set<Principal> principals = new HashSet<Principal>();
+            principals.add(new KerberosPrincipal(principal));
+
+            // client login
+            Subject subject = new Subject(false, principals,
+                    new HashSet<Object>(), new HashSet<Object>());
+            loginContext = new LoginContext("", subject, null,
+                    KerberosConfiguration.createClientConfig(principal,
+                            keytab));
+            loginContext.login();
+            subject = loginContext.getSubject();
+            Assert.assertEquals(1, subject.getPrincipals().size());
+            Assert.assertEquals(KerberosPrincipal.class,
+                    subject.getPrincipals().iterator().next().getClass());
+            Assert.assertEquals(principal + "@" + kdc.getRealm(),
+                    subject.getPrincipals().iterator().next().getName());
+            loginContext.logout();
+
+            // server login
+            subject = new Subject(false, principals, new HashSet<Object>(),
+                    new HashSet<Object>());
+            loginContext = new LoginContext("", subject, null,
+                    KerberosConfiguration.createServerConfig(principal,
+                            keytab));
+            loginContext.login();
+            subject = loginContext.getSubject();
+            Assert.assertEquals(1, subject.getPrincipals().size());
+            Assert.assertEquals(KerberosPrincipal.class,
+                    subject.getPrincipals().iterator().next().getClass());
+            Assert.assertEquals(principal + "@" + kdc.getRealm(),
+                    subject.getPrincipals().iterator().next().getName());
+            loginContext.logout();
+
+        } finally {
+            if (loginContext != null && loginContext.getSubject() != null
+                    && !loginContext.getSubject().getPrincipals().isEmpty()) {
+                loginContext.logout();
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSQuorumKerberosAuthTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSQuorumKerberosAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSQuorumKerberosAuthTest.java
new file mode 100644
index 0000000..a689a79
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSQuorumKerberosAuthTest.java
@@ -0,0 +1,124 @@
+/**
+ * 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.zookeeper.server.quorum.auth;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread;
+import org.apache.zookeeper.test.ClientBase;
+import org.apache.zookeeper.test.ClientBase.CountdownWatcher;
+import org.junit.After;
+import org.junit.AfterClass;
+import static org.junit.Assume.assumeTrue;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class ApacheDSQuorumKerberosAuthTest extends ApacheDSKerberosSecurityTestcase {
+    private static File keytabFile;
+    static {
+        String keytabFilePath = FilenameUtils.normalize(KerberosTestUtils.getKeytabFile(), true);
+        String jaasEntries = new String(""
+                + "QuorumServer {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       principal=\"" + KerberosTestUtils.getServerPrincipal() + "\";\n" + "};\n"
+                + "QuorumLearner {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       isInitiator=true\n"
+                + "       principal=\"" + KerberosTestUtils.getLearnerPrincipal() + "\";\n" + "};\n");
+        setupJaasConfig(jaasEntries);
+    }
+
+    @BeforeClass
+    public static void onlyJdk6() throws Exception {
+        String specsVersion = System.getProperty("java.specification.version", "1.6");
+        System.out.println("java.specification.version="+specsVersion);
+        assumeTrue("Skipping test as Java Major version is "+specsVersion, "1.6".equals(specsVersion));
+    }
+    
+    @Before
+    public void setUp() throws Exception {
+        // create keytab
+        keytabFile = new File(KerberosTestUtils.getKeytabFile());
+        String learnerPrincipal = KerberosTestUtils.getLearnerPrincipal();
+        String serverPrincipal = KerberosTestUtils.getServerPrincipal();
+        learnerPrincipal = learnerPrincipal.substring(0, learnerPrincipal.lastIndexOf("@"));
+        serverPrincipal = serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@"));
+        getKdc().createPrincipal(keytabFile, learnerPrincipal, serverPrincipal);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        for (MainThread mainThread : mt) {
+            mainThread.shutdown();
+            mainThread.deleteBaseDir();
+        }
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        if(keytabFile != null){
+            FileUtils.deleteQuietly(keytabFile);
+        }
+        cleanupJaasConfig();
+    }
+
+    /**
+     * Test to verify that server is able to start with valid credentials
+     */
+    @Test(timeout = 120000)
+    public void testValidCredentials() throws Exception {
+        String serverPrincipal = KerberosTestUtils.getServerPrincipal();
+        serverPrincipal = serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@"));
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal);
+        String connectStr = startQuorum(3, authConfigs, 3, true);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        }
+        zk.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSQuorumKerberosHostBasedAuthTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSQuorumKerberosHostBasedAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSQuorumKerberosHostBasedAuthTest.java
new file mode 100644
index 0000000..dc1dba9
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/ApacheDSQuorumKerberosHostBasedAuthTest.java
@@ -0,0 +1,200 @@
+/**
+ * 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.zookeeper.server.quorum.auth;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.PortAssignment;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread;
+import org.apache.zookeeper.test.ClientBase;
+import org.apache.zookeeper.test.ClientBase.CountdownWatcher;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import junit.framework.Assert;
+import static org.junit.Assume.assumeTrue;
+
+public class ApacheDSQuorumKerberosHostBasedAuthTest extends ApacheDSKerberosSecurityTestcase {
+    private static File keytabFile;
+    private static String hostServerPrincipal = KerberosTestUtils.getHostServerPrincipal();
+    private static String hostLearnerPrincipal = KerberosTestUtils.getHostLearnerPrincipal();
+    private static String hostNamedLearnerPrincipal = KerberosTestUtils.getHostNamedLearnerPrincipal("myhost");
+    static {
+        setupJaasConfigEntries(hostServerPrincipal, hostLearnerPrincipal, hostNamedLearnerPrincipal);
+    }
+
+    private static void setupJaasConfigEntries(String hostServerPrincipal,
+            String hostLearnerPrincipal, String hostNamedLearnerPrincipal) {
+        String keytabFilePath = FilenameUtils.normalize(KerberosTestUtils.getKeytabFile(), true);
+        String jaasEntries = new String(""
+                + "QuorumServer {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       principal=\"" + KerberosTestUtils.replaceHostPattern(hostServerPrincipal) + "\";\n" + "};\n"
+                + "QuorumLearner {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       isInitiator=true\n"
+                + "       principal=\"" + KerberosTestUtils.replaceHostPattern(hostLearnerPrincipal) + "\";\n" + "};\n"
+                + "QuorumLearnerMyHost {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       isInitiator=true\n"
+                + "       principal=\"" + hostNamedLearnerPrincipal + "\";\n" + "};\n");
+        setupJaasConfig(jaasEntries);
+    }
+    
+    @BeforeClass
+    public static void onlyJdk6() throws Exception {
+        String specsVersion = System.getProperty("java.specification.version", "1.6");
+        System.out.println("java.specification.version="+specsVersion);
+        assumeTrue("Skipping test as Java Major version is "+specsVersion, "1.6".equals(specsVersion));
+    }
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        // create keytab
+        keytabFile = new File(KerberosTestUtils.getKeytabFile());
+
+        // Creates principals in the KDC and adds them to a keytab file.
+        String learnerPrincipal = hostLearnerPrincipal.substring(0, hostLearnerPrincipal.lastIndexOf("@"));
+        learnerPrincipal = KerberosTestUtils.replaceHostPattern(learnerPrincipal);
+        String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@"));
+        serverPrincipal = KerberosTestUtils.replaceHostPattern(serverPrincipal);
+
+        // learner with ipaddress in principal
+        String learnerPrincipal2 = hostNamedLearnerPrincipal.substring(0, hostNamedLearnerPrincipal.lastIndexOf("@"));
+        getKdc().createPrincipal(keytabFile, learnerPrincipal, learnerPrincipal2, serverPrincipal);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        for (MainThread mainThread : mt) {
+            mainThread.shutdown();
+            mainThread.deleteBaseDir();
+        }
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        if(keytabFile != null){
+            FileUtils.deleteQuietly(keytabFile);
+        }
+        cleanupJaasConfig();
+    }
+
+    /**
+     * Test to verify that server is able to start with valid credentials
+     */
+    @Test(timeout = 120000)
+    public void testValidCredentials() throws Exception {
+        String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@"));
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal);
+        String connectStr = startQuorum(3, authConfigs, 3, true);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        }
+        zk.close();
+    }
+
+    /**
+     * Test to verify that the bad server connection to the quorum should be rejected.
+     */
+    @Test(timeout = 120000)
+    public void testConnectBadServer() throws Exception {
+        String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@"));
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal);
+        String connectStr = startQuorum(3, authConfigs, 3, true);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        }
+        zk.close();
+
+        String quorumCfgSection = mt.get(0).getQuorumCfgSection();
+        StringBuilder sb = new StringBuilder();
+        sb.append(quorumCfgSection);
+
+        int myid = mt.size() + 1;
+        final int clientPort = PortAssignment.unique();
+        String server = String.format("server.%d=localhost:%d:%d:participant",
+                myid, PortAssignment.unique(), PortAssignment.unique());
+        sb.append(server + "\n");
+        quorumCfgSection = sb.toString();
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT,
+                "QuorumLearnerMyHost");
+        MainThread badServer = new MainThread(myid, clientPort, quorumCfgSection,
+                authConfigs);
+        badServer.start();
+        watcher = new CountdownWatcher();
+        connectStr = "127.0.0.1:" + clientPort;
+        zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        try{
+            watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT/3);
+            Assert.fail("Must throw exception as the myHost is not an authorized one!");
+        } catch (TimeoutException e){
+            // expected
+        } finally {
+            zk.close();
+            badServer.shutdown();
+            badServer.deleteBaseDir();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java
index 9617c70..03579cb 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java
@@ -18,15 +18,14 @@
 package org.apache.zookeeper.server.quorum.auth;
 
 import org.apache.commons.io.FileUtils;
-import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.BeforeClass;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.Properties;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuthTestBase;
 
 /*
  * This code is originally from HDFS, see the similarly named file there

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java
index ebe541d..4afef41 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java
@@ -17,67 +17,26 @@
  */
 
 package org.apache.zookeeper.server.quorum.auth;
-
 import org.apache.commons.io.Charsets;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.text.StrSubstitutor;
-import org.apache.directory.api.ldap.model.schema.SchemaManager;
-import org.apache.directory.api.ldap.schemaextractor.SchemaLdifExtractor;
-import org.apache.directory.api.ldap.schemaextractor.impl.DefaultSchemaLdifExtractor;
-import org.apache.directory.api.ldap.schemaloader.LdifSchemaLoader;
-import org.apache.directory.api.ldap.schemamanager.impl.DefaultSchemaManager;
-import org.apache.directory.server.constants.ServerDNConstants;
-import org.apache.directory.server.core.DefaultDirectoryService;
-import org.apache.directory.server.core.api.CacheService;
-import org.apache.directory.server.core.api.DirectoryService;
-import org.apache.directory.server.core.api.InstanceLayout;
-import org.apache.directory.server.core.api.schema.SchemaPartition;
-import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
-import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
-import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
-import org.apache.directory.server.core.partition.ldif.LdifPartition;
-import org.apache.directory.server.kerberos.KerberosConfig;
-import org.apache.directory.server.kerberos.kdc.KdcServer;
-import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
-import org.apache.directory.server.kerberos.shared.keytab.Keytab;
-import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
-import org.apache.directory.server.protocol.shared.transport.TcpTransport;
-import org.apache.directory.server.protocol.shared.transport.UdpTransport;
-import org.apache.directory.server.xdbm.Index;
-import org.apache.directory.shared.kerberos.KerberosTime;
-import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
-import org.apache.directory.shared.kerberos.components.EncryptionKey;
-import org.apache.directory.api.ldap.model.entry.DefaultEntry;
-import org.apache.directory.api.ldap.model.entry.Entry;
-import org.apache.directory.api.ldap.model.ldif.LdifEntry;
-import org.apache.directory.api.ldap.model.ldif.LdifReader;
-import org.apache.directory.api.ldap.model.name.Dn;
-import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.server.KdcConfigKey;
+import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer;
+import org.apache.kerby.util.IOUtil;
+import org.apache.kerby.util.NetworkUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.StringReader;
-import java.lang.reflect.Method;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.text.MessageFormat;
-import java.util.ArrayList;
+import java.io.IOException;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
-import java.util.UUID;
 
 /**
  * Mini KDC based on Apache Directory Server that can be embedded in testcases
@@ -85,11 +44,10 @@ import java.util.UUID;
  * <p>
  * <b>From within testcases:</b>
  * <p>
- * MiniKdc sets 2 System properties when started and un-sets them when stopped:
+ * MiniKdc sets one System property when started and un-set when stopped:
  * <ul>
- * <li>java.security.krb5.conf: set to the MiniKDC real/host/port</li>
- * <li>sun.security.krb5.debug: set to the debug value provided in the
- * configuration</li>
+ *   <li>sun.security.krb5.debug: set to the debug value provided in the
+ *   configuration</li>
  * </ul>
  * Because of this, multiple MiniKdc instances cannot be started in parallel.
  * For example, running testcases in parallel that start a KDC each. To
@@ -115,7 +73,7 @@ import java.util.UUID;
  * in case of bug fixing, history, etc.
  *
  * Branch : trunk
- * Github Revision: 42e3a805117ff7cb054c2442f7b0e0cc54be63ad
+ * Github Revision: 916140604ffef59466ba30832478311d3e6249bd
  */
 public class MiniKdc {
 
@@ -123,8 +81,6 @@ public class MiniKdc {
             "java.security.krb5.conf";
     public static final String SUN_SECURITY_KRB5_DEBUG =
             "sun.security.krb5.debug";
-    private static final File testData = new File(
-            System.getProperty("test.data.dir", "build/test/data"));
 
     public static void main(String[] args) throws Exception {
         if (args.length < 4) {
@@ -224,7 +180,7 @@ public class MiniKdc {
         DEFAULT_CONFIG.setProperty(TRANSPORT, "TCP");
         DEFAULT_CONFIG.setProperty(MAX_TICKET_LIFETIME, "86400000");
         DEFAULT_CONFIG.setProperty(MAX_RENEWABLE_LIFETIME, "604800000");
-        DEFAULT_CONFIG.setProperty(DEBUG, "true");
+        DEFAULT_CONFIG.setProperty(DEBUG, "false");
     }
 
     /**
@@ -239,13 +195,17 @@ public class MiniKdc {
     }
 
     private Properties conf;
-    private DirectoryService ds;
-    private KdcServer kdc;
+    private SimpleKdcServer simpleKdc;
     private int port;
     private String realm;
     private File workDir;
     private File krb5conf;
+    private String transport;
+    private boolean krb5Debug;
 
+    public void setTransport(String transport) {
+        this.transport = transport;
+    }
     /**
      * Creates a MiniKdc.
      *
@@ -275,12 +235,7 @@ public class MiniKdc {
         LOG.info("---------------------------------------------------------------");
         this.conf = conf;
         port = Integer.parseInt(conf.getProperty(KDC_PORT));
-        if (port == 0) {
-            ServerSocket ss = new ServerSocket(0, 1, InetAddress.getByName(conf.getProperty(KDC_BIND_ADDRESS)));
-            port = ss.getLocalPort();
-            ss.close();
-        }
-        String orgName = conf.getProperty(ORG_NAME);
+        String orgName= conf.getProperty(ORG_NAME);
         String orgDomain = conf.getProperty(ORG_DOMAIN);
         realm = orgName.toUpperCase(Locale.ENGLISH) + "."
                 + orgDomain.toUpperCase(Locale.ENGLISH);
@@ -314,6 +269,7 @@ public class MiniKdc {
     }
 
     public File getKrb5conf() {
+        krb5conf = new File(System.getProperty(JAVA_SECURITY_KRB5_CONF));
         return krb5conf;
     }
 
@@ -323,186 +279,81 @@ public class MiniKdc {
      * @throws Exception thrown if the MiniKdc could not be started.
      */
     public synchronized void start() throws Exception {
-        if (kdc != null) {
+        if (simpleKdc != null) {
             throw new RuntimeException("Already started");
         }
-        initDirectoryService();
-        initKDCServer();
+        simpleKdc = new SimpleKdcServer();
+        prepareKdcServer();
+        simpleKdc.init();
+        resetDefaultRealm();
+        simpleKdc.start();
+        LOG.info("MiniKdc stated.");
     }
 
-    private void initDirectoryService() throws Exception {
-        ds = new DefaultDirectoryService();
-        ds.setInstanceLayout(new InstanceLayout(workDir));
-
-        CacheService cacheService = new CacheService();
-        ds.setCacheService(cacheService);
-
-        // first load the schema
-        InstanceLayout instanceLayout = ds.getInstanceLayout();
-        File schemaPartitionDirectory = new File(instanceLayout.getPartitionsDirectory(), "schema");
-        SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor(instanceLayout.getPartitionsDirectory());
-        extractor.extractOrCopy();
-
-        SchemaLoader loader = new LdifSchemaLoader(schemaPartitionDirectory);
-        SchemaManager schemaManager = new DefaultSchemaManager(loader);
-        schemaManager.loadAllEnabled();
-        ds.setSchemaManager(schemaManager);
-        // Init the LdifPartition with schema
-        LdifPartition schemaLdifPartition = new LdifPartition(schemaManager);
-        schemaLdifPartition.setPartitionPath(schemaPartitionDirectory.toURI());
-
-        // The schema partition
-        SchemaPartition schemaPartition = new SchemaPartition(schemaManager);
-        schemaPartition.setWrappedPartition(schemaLdifPartition);
-        ds.setSchemaPartition(schemaPartition);
-
-        JdbmPartition systemPartition = new JdbmPartition(ds.getSchemaManager());
-        systemPartition.setId("system");
-        systemPartition.setPartitionPath(
-                new File(ds.getInstanceLayout().getPartitionsDirectory(), systemPartition.getId()).toURI());
-        systemPartition.setSuffixDn(new Dn(ServerDNConstants.SYSTEM_DN));
-        systemPartition.setSchemaManager(ds.getSchemaManager());
-        ds.setSystemPartition(systemPartition);
-
-        ds.getChangeLog().setEnabled(false);
-        ds.setDenormalizeOpAttrsEnabled(true);
-        ds.addLast(new KeyDerivationInterceptor());
-
-        // create one partition
-        String orgName = conf.getProperty(ORG_NAME).toLowerCase(Locale.ENGLISH);
-        String orgDomain = conf.getProperty(ORG_DOMAIN).toLowerCase(Locale.ENGLISH);
-
-        JdbmPartition partition = new JdbmPartition(ds.getSchemaManager());
-        partition.setId(orgName);
-        partition.setPartitionPath(new File(ds.getInstanceLayout().getPartitionsDirectory(), orgName).toURI());
-        partition.setSuffixDn(new Dn("dc=" + orgName + ",dc=" + orgDomain));
-        ds.addPartition(partition);
-        // indexes
-        Set<Index<?, ?, String>> indexedAttributes = new HashSet<Index<?, ?, String>>();
-        indexedAttributes.add(new JdbmIndex<String, Entry>("objectClass", false));
-        indexedAttributes.add(new JdbmIndex<String, Entry>("dc", false));
-        indexedAttributes.add(new JdbmIndex<String, Entry>("ou", false));
-        partition.setIndexedAttributes(indexedAttributes);
-
-        // And start the ds
-        ds.setInstanceId(conf.getProperty(INSTANCE));
-        ds.startup();
-        // context entry, after ds.startup()
-        Dn dn = new Dn("dc=" + orgName + ",dc=" + orgDomain);
-        Entry entry = ds.newEntry(dn);
-        entry.add("objectClass", "top", "domain");
-        entry.add("dc", orgName);
-        ds.getAdminSession().add(entry);
+    private void resetDefaultRealm() throws IOException {
+        InputStream templateResource = new FileInputStream(
+                getKrb5conf().getAbsolutePath());
+        String content = IOUtil.readInput(templateResource);
+        content = content.replaceAll("default_realm = .*\n",
+                "default_realm = " + getRealm() + "\n");
+        IOUtil.writeFile(content, getKrb5conf());
     }
 
-    private void initKDCServer() throws Exception {
-        String orgName = conf.getProperty(ORG_NAME);
-        String orgDomain = conf.getProperty(ORG_DOMAIN);
-        String bindAddress = conf.getProperty(KDC_BIND_ADDRESS);
-        final Map<String, String> map = new HashMap<String, String>();
-        map.put("0", orgName.toLowerCase(Locale.ENGLISH));
-        map.put("1", orgDomain.toLowerCase(Locale.ENGLISH));
-        map.put("2", orgName.toUpperCase(Locale.ENGLISH));
-        map.put("3", orgDomain.toUpperCase(Locale.ENGLISH));
-        map.put("4", bindAddress);
-
-        InputStream is1 = getMinikdcResourceAsStream("minikdc.ldiff");
-
-        SchemaManager schemaManager = ds.getSchemaManager();
-        LdifReader reader = null;
-
-        try {
-            final String content = StrSubstitutor.replace(IOUtils.toString(is1), map);
-            reader = new LdifReader(new StringReader(content));
-
-            for (LdifEntry ldifEntry : reader) {
-                ds.getAdminSession().add(new DefaultEntry(schemaManager, ldifEntry.getEntry()));
-            }
-        } finally {
-            IOUtils.closeQuietly(reader);
-            IOUtils.closeQuietly(is1);
-        }
-
-        KerberosConfig kerberosConfig = new KerberosConfig();
-        kerberosConfig.setMaximumRenewableLifetime(Long.parseLong(conf.getProperty(MAX_RENEWABLE_LIFETIME)));
-        kerberosConfig.setMaximumTicketLifetime(Long.parseLong(conf.getProperty(MAX_TICKET_LIFETIME)));
-        kerberosConfig.setSearchBaseDn(String.format("dc=%s,dc=%s", orgName, orgDomain));
-        kerberosConfig.setPaEncTimestampRequired(false);
-        kdc = new KdcServer(kerberosConfig);
-        kdc.setDirectoryService(ds);
-
+    private void prepareKdcServer() throws Exception {
         // transport
-        String transport = conf.getProperty(TRANSPORT);
-        if (transport.trim().equals("TCP")) {
-            kdc.addTransports(new TcpTransport(bindAddress, port, 3, 50));
-        } else if (transport.trim().equals("UDP")) {
-            kdc.addTransports(new UdpTransport(port));
-        } else {
-            throw new IllegalArgumentException("Invalid transport: " + transport);
+        simpleKdc.setWorkDir(workDir);
+        simpleKdc.setKdcHost(getHost());
+        simpleKdc.setKdcRealm(realm);
+        if (transport == null) {
+            transport = conf.getProperty(TRANSPORT);
         }
-        kdc.setServiceName(conf.getProperty(INSTANCE));
-        kdc.start();
-
-        StringBuilder sb = new StringBuilder();
-        InputStream is2 = getMinikdcResourceAsStream("minikdc-krb5.conf");
-
-        BufferedReader r = null;
-
-        try {
-            r = new BufferedReader(new InputStreamReader(is2, Charsets.UTF_8));
-            String line = r.readLine();
-
-            while (line != null) {
-                sb.append(line).append("{3}");
-                line = r.readLine();
-            }
-        } finally {
-            IOUtils.closeQuietly(r);
-            IOUtils.closeQuietly(is2);
+        if (port == 0) {
+            port = NetworkUtil.getServerPort();
         }
-
-        krb5conf = new File(workDir, "krb5.conf").getAbsoluteFile();
-        FileUtils.writeStringToFile(krb5conf, MessageFormat.format(sb.toString(), getRealm(), getHost(),
-                Integer.toString(getPort()), System.getProperty("line.separator")));
-        System.setProperty(JAVA_SECURITY_KRB5_CONF, krb5conf.getAbsolutePath());
-
-        System.setProperty(SUN_SECURITY_KRB5_DEBUG, conf.getProperty(DEBUG, "false"));
-
-        // refresh the config
-        Class<?> classRef;
-        if (System.getProperty("java.vendor").contains("IBM")) {
-            classRef = Class.forName("com.ibm.security.krb5.internal.Config");
+        if (transport != null) {
+            if (transport.trim().equals("TCP")) {
+                simpleKdc.setKdcTcpPort(port);
+                simpleKdc.setAllowUdp(false);
+            } else if (transport.trim().equals("UDP")) {
+                simpleKdc.setKdcUdpPort(port);
+                simpleKdc.setAllowTcp(false);
+            } else {
+                throw new IllegalArgumentException("Invalid transport: " + transport);
+            }
         } else {
-            classRef = Class.forName("sun.security.krb5.Config");
+            throw new IllegalArgumentException("Need to set transport!");
+        }
+        simpleKdc.getKdcConfig().setString(KdcConfigKey.KDC_SERVICE_NAME,
+                conf.getProperty(INSTANCE));
+        if (conf.getProperty(DEBUG) != null) {
+            krb5Debug = getAndSet(SUN_SECURITY_KRB5_DEBUG, conf.getProperty(DEBUG));
         }
-        Method refreshMethod = classRef.getMethod("refresh", new Class[0]);
-        refreshMethod.invoke(classRef, new Object[0]);
-
-        LOG.info("MiniKdc listening at port: {}", getPort());
-        LOG.info("MiniKdc setting JVM krb5.conf to: {}", krb5conf.getAbsolutePath());
-    }
-
-    private InputStream getMinikdcResourceAsStream(String resourceName)
-            throws FileNotFoundException {
-        File kdcResourceFile = new File(testData, "/kerberos/" + resourceName);
-        return new FileInputStream(kdcResourceFile);
     }
 
     /**
      * Stops the MiniKdc
      */
     public synchronized void stop() {
-        if (kdc != null) {
-            System.getProperties().remove(JAVA_SECURITY_KRB5_CONF);
-            System.getProperties().remove(SUN_SECURITY_KRB5_DEBUG);
-            kdc.stop();
+        if (simpleKdc != null) {
             try {
-                ds.shutdown();
-            } catch (Exception ex) {
-                LOG.error("Could not shutdown ApacheDS properly: {}", ex.toString(), ex);
+                simpleKdc.stop();
+            } catch (KrbException e) {
+                e.printStackTrace();
+            } finally {
+                if(conf.getProperty(DEBUG) != null) {
+                    System.setProperty(SUN_SECURITY_KRB5_DEBUG,
+                            Boolean.toString(krb5Debug));
+                }
             }
         }
         delete(workDir);
+        try {
+            // Will be fixed in next Kerby version.
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        LOG.info("MiniKdc stopped.");
     }
 
     private void delete(File f) {
@@ -527,20 +378,9 @@ public class MiniKdc {
      * @param password password.
      * @throws Exception thrown if the principal could not be created.
      */
-    public synchronized void createPrincipal(String principal, String password) throws Exception {
-        String orgName = conf.getProperty(ORG_NAME);
-        String orgDomain = conf.getProperty(ORG_DOMAIN);
-        String baseDn = "ou=users,dc=" + orgName.toLowerCase(Locale.ENGLISH) + ",dc="
-                + orgDomain.toLowerCase(Locale.ENGLISH);
-        String content = "dn: uid=" + principal + "," + baseDn + "\n" + "objectClass: top\n" + "objectClass: person\n"
-                + "objectClass: inetOrgPerson\n" + "objectClass: krb5principal\n" + "objectClass: krb5kdcentry\n"
-                + "cn: " + principal + "\n" + "sn: " + principal + "\n" + "uid: " + principal + "\n" + "userPassword: "
-                + password + "\n" + "krb5PrincipalName: " + principal + "@" + getRealm() + "\n"
-                + "krb5KeyVersionNumber: 0";
-
-        for (LdifEntry ldifEntry : new LdifReader(new StringReader(content))) {
-            ds.getAdminSession().add(new DefaultEntry(ds.getSchemaManager(), ldifEntry.getEntry()));
-        }
+    public synchronized void createPrincipal(String principal, String password)
+            throws Exception {
+        simpleKdc.createPrincipal(principal, password);
     }
 
     /**
@@ -554,21 +394,25 @@ public class MiniKdc {
     public synchronized void createPrincipal(File keytabFile,
                                              String ... principals)
             throws Exception {
-        String generatedPassword = UUID.randomUUID().toString();
-        Keytab keytab = new Keytab();
-        List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
+        simpleKdc.createPrincipals(principals);
+        if (keytabFile.exists() && !keytabFile.delete()) {
+            LOG.error("Failed to delete keytab file: " + keytabFile);
+        }
         for (String principal : principals) {
-            createPrincipal(principal, generatedPassword);
-            principal = principal + "@" + getRealm();
-            KerberosTime timestamp = new KerberosTime();
-            for (Map.Entry<EncryptionType, EncryptionKey> entry : KerberosKeyFactory
-                    .getKerberosKeys(principal, generatedPassword).entrySet()) {
-                EncryptionKey ekey = entry.getValue();
-                byte keyVersion = (byte) ekey.getKeyVersion();
-                entries.add(new KeytabEntry(principal, 1L, timestamp, keyVersion, ekey));
-            }
+            simpleKdc.getKadmin().exportKeytab(keytabFile, principal);
         }
-        keytab.setEntries(entries);
-        keytab.write(keytabFile);
     }
-}
+
+    /**
+     * Set the System property; return the old value for caching.
+     *
+     * @param sysprop property
+     * @param debug true or false
+     * @return the previous value
+     */
+    private boolean getAndSet(String sysprop, String debug) {
+        boolean old = Boolean.getBoolean(sysprop);
+        System.setProperty(sysprop, debug);
+        return old;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java
index f5317f7..69dbcd1 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java
@@ -18,8 +18,8 @@
 
 package org.apache.zookeeper.server.quorum.auth;
 
-import org.apache.directory.server.kerberos.shared.keytab.Keytab;
-import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
+import org.apache.kerby.kerberos.kerb.keytab.Keytab;
+import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -30,6 +30,7 @@ import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import java.io.File;
 import java.security.Principal;
+import java.util.List;
 import java.util.Set;
 import java.util.Map;
 import java.util.HashSet;
@@ -59,17 +60,16 @@ public class MiniKdcTest extends KerberosSecurityTestcase {
         File workDir = getWorkDir();
 
         kdc.createPrincipal(new File(workDir, "keytab"), "foo/bar", "bar/foo");
-        Keytab kt = Keytab.read(new File(workDir, "keytab"));
+        List<PrincipalName> principalNameList =
+                Keytab.loadKeytab(new File(workDir, "keytab")).getPrincipals();
 
         Set<String> principals = new HashSet<String>();
-        for (KeytabEntry entry : kt.getEntries()) {
-            principals.add(entry.getPrincipalName());
+        for (PrincipalName principalName : principalNameList) {
+          principals.add(principalName.getName());
         }
-        //here principals use \ instead of /
-        //because org.apache.directory.server.kerberos.shared.keytab.KeytabDecoder
-        // .getPrincipalName(IoBuffer buffer) use \\ when generates principal
+
         Assert.assertEquals(new HashSet<String>(Arrays.asList(
-                "foo\\bar@" + kdc.getRealm(), "bar\\foo@" + kdc.getRealm())),
+                "foo/bar@" + kdc.getRealm(), "bar/foo@" + kdc.getRealm())),
                 principals);
       }
 


Mime
View raw message