You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zookeeper.apache.org by an...@apache.org on 2018/09/25 11:25:35 UTC
[1/2] zookeeper git commit: ZOOKEEPER-3148: Add Kerberos tests for
modern JDKs
Repository: zookeeper
Updated Branches:
refs/heads/branch-3.4 29cc5c1a7 -> 7b25a7078
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java
index e3eddf7..7263925 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java
@@ -28,11 +28,17 @@ 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.server.quorum.auth.KerberosTestUtils;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuth;
+import static org.apache.zookeeper.server.quorum.auth.QuorumAuthTestBase.cleanupJaasConfig;
+import static org.apache.zookeeper.server.quorum.auth.QuorumAuthTestBase.setupJaasConfig;
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.assumeFalse;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
public class QuorumKerberosAuthTest extends KerberosSecurityTestcase {
@@ -46,9 +52,7 @@ public class QuorumKerberosAuthTest extends KerberosSecurityTestcase {
+ " keyTab=\"" + keytabFilePath + "\"\n"
+ " storeKey=true\n"
+ " useTicketCache=false\n"
- + " debug=true\n"
- + " doNotPrompt=true\n"
- + " refreshKrb5Config=true\n"
+ + " debug=false\n"
+ " principal=\"" + KerberosTestUtils.getServerPrincipal() + "\";\n" + "};\n"
+ "QuorumLearner {\n"
+ " com.sun.security.auth.module.Krb5LoginModule required\n"
@@ -56,13 +60,17 @@ public class QuorumKerberosAuthTest extends KerberosSecurityTestcase {
+ " keyTab=\"" + keytabFilePath + "\"\n"
+ " storeKey=true\n"
+ " useTicketCache=false\n"
- + " debug=true\n"
- + " doNotPrompt=true\n"
- + " refreshKrb5Config=true\n"
- + " isInitiator=true\n"
+ + " debug=false\n"
+ " principal=\"" + KerberosTestUtils.getLearnerPrincipal() + "\";\n" + "};\n");
setupJaasConfig(jaasEntries);
}
+
+ @BeforeClass
+ public static void notOnJdk6() throws Exception {
+ String specsVersion = System.getProperty("java.specification.version", "1.6");
+ System.out.println("java.specification.version="+specsVersion);
+ assumeFalse("Skipping test as Java Major version is "+specsVersion, "1.6".equals(specsVersion));
+ }
@Before
public void setUp() throws Exception {
@@ -103,7 +111,7 @@ public class QuorumKerberosAuthTest extends KerberosSecurityTestcase {
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);
+ String connectStr = startQuorum(3, authConfigs, 3, false);
CountdownWatcher watcher = new CountdownWatcher();
ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7b25a707/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java
index 55deefb..aab893e 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java
@@ -38,12 +38,17 @@ import org.junit.BeforeClass;
import org.junit.Test;
import junit.framework.Assert;
+import org.apache.zookeeper.server.quorum.auth.KerberosTestUtils;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuth;
+import static org.apache.zookeeper.server.quorum.auth.QuorumAuthTestBase.cleanupJaasConfig;
+import static org.apache.zookeeper.server.quorum.auth.QuorumAuthTestBase.setupJaasConfig;
+import static org.junit.Assume.assumeFalse;
public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase {
private static File keytabFile;
private static String hostServerPrincipal = KerberosTestUtils.getHostServerPrincipal();
private static String hostLearnerPrincipal = KerberosTestUtils.getHostLearnerPrincipal();
- private static String hostNamedLearnerPrincipal = KerberosTestUtils.getHostNamedLearnerPrincipal("myhost");
+ private static String hostNamedLearnerPrincipal = KerberosTestUtils.getHostNamedLearnerPrincipal("myHost");
static {
setupJaasConfigEntries(hostServerPrincipal, hostLearnerPrincipal, hostNamedLearnerPrincipal);
}
@@ -58,9 +63,7 @@ public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase {
+ " keyTab=\"" + keytabFilePath + "\"\n"
+ " storeKey=true\n"
+ " useTicketCache=false\n"
- + " debug=true\n"
- + " doNotPrompt=true\n"
- + " refreshKrb5Config=true\n"
+ + " debug=false\n"
+ " principal=\"" + KerberosTestUtils.replaceHostPattern(hostServerPrincipal) + "\";\n" + "};\n"
+ "QuorumLearner {\n"
+ " com.sun.security.auth.module.Krb5LoginModule required\n"
@@ -68,10 +71,7 @@ public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase {
+ " keyTab=\"" + keytabFilePath + "\"\n"
+ " storeKey=true\n"
+ " useTicketCache=false\n"
- + " debug=true\n"
- + " doNotPrompt=true\n"
- + " refreshKrb5Config=true\n"
- + " isInitiator=true\n"
+ + " debug=false\n"
+ " principal=\"" + KerberosTestUtils.replaceHostPattern(hostLearnerPrincipal) + "\";\n" + "};\n"
+ "QuorumLearnerMyHost {\n"
+ " com.sun.security.auth.module.Krb5LoginModule required\n"
@@ -79,13 +79,17 @@ public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase {
+ " keyTab=\"" + keytabFilePath + "\"\n"
+ " storeKey=true\n"
+ " useTicketCache=false\n"
- + " debug=true\n"
- + " doNotPrompt=true\n"
- + " refreshKrb5Config=true\n"
- + " isInitiator=true\n"
+ + " debug=false\n"
+ " principal=\"" + hostNamedLearnerPrincipal + "\";\n" + "};\n");
setupJaasConfig(jaasEntries);
}
+
+ @BeforeClass
+ public static void notOnJdk6() throws Exception {
+ String specsVersion = System.getProperty("java.specification.version", "1.6");
+ System.out.println("java.specification.version="+specsVersion);
+ assumeFalse("Skipping test as Java Major version is "+specsVersion, "1.6".equals(specsVersion));
+ }
@BeforeClass
public static void setUp() throws Exception {
@@ -130,7 +134,7 @@ public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase {
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);
+ String connectStr = startQuorum(3, authConfigs, 3, false);
CountdownWatcher watcher = new CountdownWatcher();
ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
@@ -151,7 +155,7 @@ public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase {
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);
+ String connectStr = startQuorum(3, authConfigs, 3, false);
CountdownWatcher watcher = new CountdownWatcher();
ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
[2/2] zookeeper git commit: ZOOKEEPER-3148: Add Kerberos tests for
modern JDKs
Posted by an...@apache.org.
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 <eo...@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 <eo...@apache.org>
Authored: Tue Sep 25 13:25:29 2018 +0200
Committer: Andor Molnar <an...@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);
}