You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ze...@apache.org on 2018/07/06 07:39:49 UTC

directory-kerby git commit: Add unit tests of HAS REST API.

Repository: directory-kerby
Updated Branches:
  refs/heads/trunk 9a02da485 -> b40f88a74


Add unit tests of HAS REST API.


Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/commit/b40f88a7
Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/b40f88a7
Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/b40f88a7

Branch: refs/heads/trunk
Commit: b40f88a748fbb74ceff1781575e5cd040023cb33
Parents: 9a02da4
Author: zenglinx <fr...@intel.com>
Authored: Fri Jul 6 15:39:18 2018 +0800
Committer: zenglinx <fr...@intel.com>
Committed: Fri Jul 6 15:39:18 2018 +0800

----------------------------------------------------------------------
 has-project/has-server/pom.xml                  |  27 +-
 .../kerby/has/server/web/rest/KadminApi.java    |   2 +-
 .../apache/kerby/has/server/TestConfApi.java    |  81 +++++
 .../apache/kerby/has/server/TestHadminApi.java  |  60 ++++
 .../apache/kerby/has/server/TestInitApi.java    |  44 +++
 .../apache/kerby/has/server/TestKadminApi.java  |  62 ++++
 .../kerby/has/server/TestRestApiBase.java       | 339 +++++++++++++++++++
 .../src/test/resources/conf/backend.conf        |  20 ++
 .../src/test/resources/conf/has-server.conf     |  25 ++
 .../has-server/src/test/resources/conf/kdc.conf |  23 ++
 .../src/test/resources/conf/krb5.conf           |  29 ++
 11 files changed, 710 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/pom.xml
----------------------------------------------------------------------
diff --git a/has-project/has-server/pom.xml b/has-project/has-server/pom.xml
index bc08328..a336d2d 100644
--- a/has-project/has-server/pom.xml
+++ b/has-project/has-server/pom.xml
@@ -40,6 +40,22 @@
     </dependency>
     <dependency>
       <groupId>org.apache.kerby</groupId>
+      <artifactId>json-backend</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>mysql-backend</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <version>1.4.196</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
       <artifactId>token-provider</artifactId>
       <version>${project.version}</version>
     </dependency>
@@ -73,6 +89,15 @@
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.sun.jersey</groupId>
+      <artifactId>jersey-client</artifactId>
+      <version>1.19</version>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.core</groupId>
+      <artifactId>jersey-common</artifactId>
+      <version>RELEASE</version>
+    </dependency>
   </dependencies>
-
 </project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/KadminApi.java
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/KadminApi.java b/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/KadminApi.java
index 24ba8b2..040bf8a 100644
--- a/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/KadminApi.java
+++ b/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/KadminApi.java
@@ -186,7 +186,7 @@ public class KadminApi {
      * Add principal by name and password.
      *
      * @param principal principal name.
-     * @param newPassword principal password
+     * @param password principal password
      * @return Response
      */
     @POST

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestConfApi.java
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestConfApi.java b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestConfApi.java
new file mode 100644
index 0000000..ee204a7
--- /dev/null
+++ b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestConfApi.java
@@ -0,0 +1,81 @@
+/**
+ *  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.kerby.has.server;
+
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import javax.ws.rs.core.MultivaluedMap;
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class TestConfApi extends TestRestApiBase {
+    @Test
+    public void testSetPlugin() {
+        WebResource webResource = getWebResource("conf/setplugin");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("plugin", "RAM");
+        String response = webResource.queryParams(params).put(String.class);
+        assertEquals("HAS plugin set successfully.", response);
+    }
+
+    @Test
+    public void testConfigBackend() {
+        WebResource webResource = getWebResource("conf/configbackend");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("backendType", "json");
+        String backend = null;
+        try {
+            backend = new File(testDir, "json-backend").getCanonicalPath();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        params.add("dir", backend);
+        String response = webResource.queryParams(params).put(String.class);
+        assertEquals("Json backend set successfully.", response);
+    }
+
+    @Test
+    public void testConfigKdc() {
+        WebResource webResource = getWebResource("conf/configkdc");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("realm", "HADOOP.COM");
+        params.add("host", "localhost");
+        params.add("port", "8866");
+        String response = webResource.queryParams(params).put(String.class);
+        assertEquals("HAS server KDC set successfully.", response);
+    }
+
+    @Test
+    public void testGetKrb5Conf() {
+        getKrb5Conf();
+    }
+
+    @Test
+    public void testGetHasClientConf() {
+        getHasClientConf();
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestHadminApi.java
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestHadminApi.java b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestHadminApi.java
new file mode 100644
index 0000000..21a7b18
--- /dev/null
+++ b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestHadminApi.java
@@ -0,0 +1,60 @@
+/**
+ *  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.kerby.has.server;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+import static org.junit.Assert.assertEquals;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class TestHadminApi extends TestRestApiBase {
+    @Test
+    public void testAddPrincipalsByRole() {
+        addPrincipalsByRole();
+    }
+
+    @Test
+    public void testExportKeytabsByRole() {
+        exportKeytabsByRole();
+    }
+
+    @Test
+    public void testHostRoles() {
+        WebResource webResource = getWebResource("hadmin/hostroles");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testSetConf() {
+        WebResource webResource = getWebResource("hadmin/setconf");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("isEnable", "true");
+        String response = webResource.queryParams(params).put(String.class);
+        assertEquals("Set conf to enable", response);
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestInitApi.java
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestInitApi.java b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestInitApi.java
new file mode 100644
index 0000000..a35749c
--- /dev/null
+++ b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestInitApi.java
@@ -0,0 +1,44 @@
+/**
+ *  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.kerby.has.server;
+
+import org.apache.hadoop.fs.FileUtil;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import java.io.File;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class TestInitApi extends TestRestApiBase {
+    @Test
+    public void testKdcStart() {
+        kdcStart();
+        File backendDir = new File(testDir, "json-backend");
+        if (backendDir.exists()) {
+            FileUtil.fullyDelete(backendDir);
+        }
+    }
+
+    @Test
+    public void testKdcInit() {
+        kdcInit();
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestKadminApi.java
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestKadminApi.java b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestKadminApi.java
new file mode 100644
index 0000000..9f4a71b
--- /dev/null
+++ b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestKadminApi.java
@@ -0,0 +1,62 @@
+/**
+ *  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.kerby.has.server;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class TestKadminApi extends TestRestApiBase {
+    @Test
+    public void testAddPrincipal() {
+        addPrincipal();
+    }
+
+    @Test
+    public void testGetPrincipal() {
+        getPrincipal();
+    }
+
+    @Test
+    public void testListPrincipals() {
+        listPrincipals();
+    }
+
+    @Test
+    public void testBRenamePrincipal() {
+        renamePrincipal();
+    }
+
+    @Test
+    public void testChangePassword() {
+        changePassword();
+    }
+
+    @Test
+    public void testExportKeytab() {
+      exportKeytab();
+    }
+
+    @Test
+    public void testXDeletePrincipal() {
+        deletePrincipal();
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestRestApiBase.java
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestRestApiBase.java b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestRestApiBase.java
new file mode 100644
index 0000000..681bf2d
--- /dev/null
+++ b/has-project/has-server/src/test/java/org/apache/kerby/has/server/TestRestApiBase.java
@@ -0,0 +1,339 @@
+/**
+ *  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.kerby.has.server;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.client.urlconnection.HTTPSProperties;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.kerby.has.common.HasConfig;
+import org.apache.kerby.has.common.HasConfigKey;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.common.spnego.AuthenticationException;
+import org.apache.kerby.has.common.util.URLConnectionFactory;
+import org.apache.kerby.has.server.web.WebConfigKey;
+import org.apache.kerby.has.server.web.WebServer;
+import org.apache.hadoop.http.HttpConfig;
+import org.apache.hadoop.security.ssl.SSLFactory;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.glassfish.jersey.SslConfigurator;
+import org.junit.After;
+import org.junit.Before;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.ws.rs.core.MultivaluedMap;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestRestApiBase {
+    private static String address;
+    protected static File testDir = new File(System.getProperty("test.dir", "target"));
+    private static File testClassDir = new File(testDir, "test-classes");
+    private static File confDir = new File(testClassDir, "conf");
+    private static File workDir = new File(testDir, "work-dir");
+    private static HasServer server = null;
+    private static final String KEY_STORE_DIR = TestUtil.getTempPath("keystore");
+    private static File keyStoreDir = new File(KEY_STORE_DIR);
+    private static HasConfig httpsConf;
+
+    @Before
+    public void startHasServer() throws Exception {
+        // Create test keystoreDir and workDir.
+        if (!keyStoreDir.exists() && !keyStoreDir.mkdirs()) {
+                System.err.println("Failed to create keystore-dir.");
+                System.exit(3);
+        }
+
+        if (!workDir.exists() && !workDir.mkdirs()) {
+                System.err.println("Failed to create work-dir.");
+                System.exit(3);
+        }
+
+        // Configure test HAS server.
+        httpsConf = new HasConfig();
+        String sslConfDir = TestUtil.getClasspathDir(TestRestApiBase.class);
+        TestUtil.setupSSLConfig(KEY_STORE_DIR, sslConfDir, httpsConf, false);
+        httpsConf.setString(WebConfigKey.HAS_HTTP_POLICY_KEY, HttpConfig.Policy.HTTPS_ONLY.name());
+        httpsConf.setString(HasConfigKey.FILTER_AUTH_TYPE, "simple");
+
+        // Start test HAS server.
+        int httpsPort = 10000 + (int) (System.currentTimeMillis() % 10000); // Generate test port randomly
+        String host = "localhost";
+        address = host + ":" + httpsPort;
+        httpsConf.setString(WebConfigKey.HAS_HTTPS_ADDRESS_KEY, address);
+
+        server = new HasServer(confDir);
+        server.setWebServer(new WebServer(httpsConf));
+        server.setWorkDir(workDir);
+        try {
+            server.startWebServer();
+        } catch (HasException e) {
+            System.err.println("Errors occurred when start HAS server: " + e.toString());
+            System.exit(6);
+        }
+    }
+
+    @After
+    public void stopHasServer() {
+        server.stopWebServer();
+        if (server.getKdcServer() != null) {
+            server.stopKdcServer();
+        }
+        if (keyStoreDir.exists()) {
+            FileUtil.fullyDelete(keyStoreDir);
+        }
+        if (workDir.exists()) {
+            FileUtil.fullyDelete(workDir);
+        }
+    }
+
+    private void startKdc() {
+        configKDCBackend();
+        WebResource webResource = getWebResource("init/kdcstart");
+        ClientResponse response = webResource.put(ClientResponse.class);
+        if (response.getStatus() != 200) {
+            System.err.println("Errors occurred when start HAS KDC server: "
+                + response.getEntity(String.class));
+            System.exit(6);
+        }
+    }
+
+    protected WebResource getWebResource(String restName) {
+        String apiUrl = "https://" + address + "/has/v1/" + restName;
+        HasConfig clientConf = new HasConfig();
+        try {
+            clientConf.addIniConfig(new File(httpsConf.getString(SSLFactory.SSL_CLIENT_CONF_KEY)));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        SslConfigurator sslConfigurator = SslConfigurator.newInstance()
+            .trustStoreFile(clientConf.getString("ssl.client.truststore.location"))
+            .trustStorePassword(clientConf.getString("ssl.client.truststore.password"));
+        sslConfigurator.securityProtocol("SSL");
+        SSLContext sslContext = sslConfigurator.createSSLContext();
+        ClientConfig clientConfig = new DefaultClientConfig();
+        clientConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
+            new HTTPSProperties(new HostnameVerifier() {
+                @Override
+                public boolean verify(String s, SSLSession sslSession) {
+                    return false;
+                }
+            }, sslContext));
+        Client client = Client.create(clientConfig);
+        return client.resource(apiUrl);
+    }
+
+    protected void configKDCBackend() {
+      WebResource webResource = getWebResource("conf/configbackend");
+      MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+      params.add("backendType", "json");
+      String backend = null;
+      try {
+        backend = new File(testDir, "json-backend").getCanonicalPath();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+      params.add("dir", backend);
+      ClientResponse response = webResource.queryParams(params).put(ClientResponse.class);
+      if (response.getStatus() != 200) {
+        System.err.println("Errors occurred when config HAS KDC backend: "
+            + response.getEntity(String.class));
+        System.exit(7);
+      }
+    }
+
+    protected void getKrb5Conf() {
+        WebResource webResource = getWebResource("conf/getkrb5conf");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        assertEquals(200, response.getStatus());
+    }
+
+    protected void getHasClientConf() {
+        WebResource webResource = getWebResource("conf/gethasclientconf");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        assertEquals(200, response.getStatus());
+        File hasConf = new File(confDir, "has-client.conf");
+        if (hasConf.exists() && !hasConf.delete()) {
+                System.err.println("Failed to delete has-client.conf.");
+        }
+    }
+
+    protected void kdcStart() {
+        WebResource webResource = getWebResource("init/kdcstart");
+        String response = webResource.put(String.class);
+        assertEquals("Succeed in starting KDC server.", response);
+    }
+
+    protected void kdcInit() {
+        startKdc();
+        WebResource webResource = getWebResource("init/kdcinit");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        assertEquals(200, response.getStatus());
+    }
+
+    protected void addPrincipalsByRole() {
+        String webServerUrl = "https://" + address + "/has/v1/";
+        startKdc();
+
+        // Create test host roles json object.
+        JSONObject hostRoles = new JSONObject();
+        try {
+            JSONObject host1 = new JSONObject();
+            host1.put("name", "host1");
+            host1.put("hostRoles", "HDFS,YARN");
+            JSONObject host2 = new JSONObject();
+            host2.put("name", "host2");
+            host2.put("hostRoles", "ZOOKEEPER,HBASE");
+            JSONArray hosts = new JSONArray();
+            hosts.put(host1);
+            hosts.put(host2);
+            hostRoles.put("HOSTS", hosts);
+        } catch (JSONException e) {
+            System.err.println("Failed to create test host roles json object. " + e.toString());
+            System.exit(6);
+        }
+
+        try {
+            URL url = null;
+            try {
+                url = new URL(webServerUrl + "hadmin/addprincipalsbyrole");
+            } catch (MalformedURLException e) {
+                e.printStackTrace();
+            }
+
+            URLConnectionFactory connectionFactory = URLConnectionFactory.newDefaultURLConnectionFactory(httpsConf);
+            HttpURLConnection httpConn = (HttpURLConnection) connectionFactory.openConnection(url, false, httpsConf);
+            httpConn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+            httpConn.setRequestMethod("PUT");
+            httpConn.setDoOutput(true);
+            httpConn.setDoInput(true);
+            httpConn.connect();
+
+            OutputStream out = httpConn.getOutputStream();
+            out.write(hostRoles.toString().getBytes());
+            out.flush();
+            out.close();
+
+            assertEquals(200, httpConn.getResponseCode());
+            BufferedReader reader = httpConn.getResponseCode()
+                == HttpURLConnection.HTTP_OK ? new BufferedReader(
+                new InputStreamReader(httpConn.getInputStream(),
+                    "UTF-8")) : new BufferedReader(
+                new InputStreamReader(httpConn.getErrorStream(),
+                    "UTF-8"));
+
+            System.out.println(reader.readLine());
+        } catch (IOException | AuthenticationException e) {
+            System.err.println("Failed to create principals by hostRoles. " + e.toString());
+            System.exit(6);
+        }
+    }
+
+    protected void exportKeytabsByRole() {
+        startKdc();
+        WebResource webResource = getWebResource("hadmin/exportKeytabsbyrole");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("host", "host1");
+        params.add("role", "HDFS");
+        ClientResponse response = webResource.queryParams(params).get(ClientResponse.class);
+        assertEquals(200, response.getStatus());
+    }
+
+    protected void addPrincipal() {
+        startKdc();
+        WebResource webResource = getWebResource("kadmin/addprincipal");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("principal", "admin");
+        params.add("password", "123");
+        String response = webResource.queryParams(params).post(String.class);
+        assertEquals("Add principal successfully.", response);
+    }
+
+    protected void renamePrincipal() {
+        startKdc();
+        WebResource webResource = getWebResource("kadmin/renameprincipal");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("oldprincipal", "admin");
+        params.add("newprincipal", "admin2");
+        String response = webResource.queryParams(params).post(String.class);
+        assertEquals("Rename principal successfully.", response);
+    }
+
+    protected void changePassword() {
+        startKdc();
+        WebResource webResource = getWebResource("kadmin/changepassword");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("principal", "admin2");
+        params.add("password", "12345");
+        String response = webResource.queryParams(params).post(String.class);
+        assertEquals("Change password successfully.", response);
+    }
+
+    protected void exportKeytab() {
+        startKdc();
+        WebResource webResource = getWebResource("kadmin/exportkeytab");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("principal", "admin2@HADOOP.COM");
+        ClientResponse response = webResource.queryParams(params).get(ClientResponse.class);
+        assertEquals(200, response.getStatus());
+    }
+
+    protected void getPrincipal() {
+        startKdc();
+        WebResource webResource = getWebResource("kadmin/getprincipal");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("principal", "admin2@HADOOP.COM");
+        ClientResponse response = webResource.queryParams(params).post(ClientResponse.class);
+        assertEquals(200, response.getStatus());
+    }
+
+    protected void listPrincipals() {
+        startKdc();
+        WebResource webResource = getWebResource("kadmin/listprincipals");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        ClientResponse response = webResource.queryParams(params).get(ClientResponse.class);
+        assertEquals(200, response.getStatus());
+    }
+
+    protected void deletePrincipal() {
+        startKdc();
+        WebResource webResource = getWebResource("kadmin/deleteprincipal");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("principal", "admin2");
+        String response = webResource.queryParams(params).delete(String.class);
+        assertEquals("Delete principal successfully.", response);
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/test/resources/conf/backend.conf
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/test/resources/conf/backend.conf b/has-project/has-server/src/test/resources/conf/backend.conf
new file mode 100644
index 0000000..2085217
--- /dev/null
+++ b/has-project/has-server/src/test/resources/conf/backend.conf
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+kdc_identity_backend = org.apache.kerby.kerberos.kdc.identitybackend.JsonIdentityBackend
+backend.json.dir = /tmp/test/has/jsonbackend

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/test/resources/conf/has-server.conf
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/test/resources/conf/has-server.conf b/has-project/has-server/src/test/resources/conf/has-server.conf
new file mode 100644
index 0000000..dcea4ad
--- /dev/null
+++ b/has-project/has-server/src/test/resources/conf/has-server.conf
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+[HAS]
+  https_host = localhost
+  https_port = 8092
+  enable_conf = true
+
+[PLUGIN]
+  auth_type = RAM

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/test/resources/conf/kdc.conf
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/test/resources/conf/kdc.conf b/has-project/has-server/src/test/resources/conf/kdc.conf
new file mode 100644
index 0000000..a74e180
--- /dev/null
+++ b/has-project/has-server/src/test/resources/conf/kdc.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+[kdcdefaults]
+  kdc_host = localhost
+  kdc_udp_port = 8866
+  kdc_tcp_port = 8866
+  kdc_realm = HADOOP.COM

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b40f88a7/has-project/has-server/src/test/resources/conf/krb5.conf
----------------------------------------------------------------------
diff --git a/has-project/has-server/src/test/resources/conf/krb5.conf b/has-project/has-server/src/test/resources/conf/krb5.conf
new file mode 100644
index 0000000..0f5c367
--- /dev/null
+++ b/has-project/has-server/src/test/resources/conf/krb5.conf
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+[libdefaults]
+    kdc_realm = HADOOP.COM
+    default_realm = HADOOP.COM
+    udp_preference_limit = 4096
+    kdc_tcp_port = 8866
+    kdc_udp_port = 8866
+
+[realms]
+    HADOOP.COM = {
+        kdc = localhost:8866
+    }