DIRKRB-673 Add REST API for Kerby KDC configuration.
diff --git a/has-project/has-server/pom.xml b/has-project/has-server/pom.xml
index 624c464..8d762a8 100644
--- a/has-project/has-server/pom.xml
+++ b/has-project/has-server/pom.xml
@@ -64,6 +64,16 @@
       <version>${bouncycastle.version}</version>
     </dependency>
     <dependency>
+      <groupId>commons-dbutils</groupId>
+      <artifactId>commons-dbutils</artifactId>
+      <version>${commons-dbutils.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.drizzle.jdbc</groupId>
+      <artifactId>drizzle-jdbc</artifactId>
+      <version>${drizzle-jdbc.version}</version>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
diff --git a/has-project/has-server/src/main/java/org/apache/kerby/has/server/HasServer.java b/has-project/has-server/src/main/java/org/apache/kerby/has/server/HasServer.java
index 121c7ca..5937782 100644
--- a/has-project/has-server/src/main/java/org/apache/kerby/has/server/HasServer.java
+++ b/has-project/has-server/src/main/java/org/apache/kerby/has/server/HasServer.java
@@ -19,6 +19,7 @@
  */
 package org.apache.kerby.has.server;
 
+import org.apache.commons.dbutils.DbUtils;
 import org.apache.hadoop.http.HttpConfig;
 import org.apache.kerby.has.common.HasConfig;
 import org.apache.kerby.has.common.HasException;
@@ -32,15 +33,31 @@
 import org.apache.kerby.kerberos.kerb.client.ClientUtil;
 import org.apache.kerby.kerberos.kerb.client.KrbConfig;
 import org.apache.kerby.kerberos.kerb.client.KrbSetting;
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
 import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
 import org.apache.kerby.kerberos.kerb.server.KdcServer;
+import org.apache.kerby.kerberos.kerb.server.KdcUtil;
+import org.apache.kerby.util.IOUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
 
 /**
  * The HAS KDC server implementation.
@@ -103,6 +120,16 @@
     }
 
     public void startKdcServer() throws HasException {
+        BackendConfig backendConfig;
+        try {
+            backendConfig = KdcUtil.getBackendConfig(getConfDir());
+        } catch (KrbException e) {
+            throw new HasException("Failed to get backend config. " + e);
+        }
+        String backendJar = backendConfig.getString("kdc_identity_backend");
+        if (backendJar.equals("org.apache.kerby.has.server.kdc.MySQLIdentityBackend")) {
+            updateKdcConf();
+        }
         try {
             kdcServer = new KdcServer(confDir);
         } catch (KrbException e) {
@@ -208,6 +235,340 @@
         return nameString;
     }
 
+     /**
+     * Update conf file.
+     *
+     * @param confName  conf file name
+     * @param values    customized values
+     * @throws IOException throw IOException
+     * @throws KrbException e
+     */
+    public void updateConfFile(String confName, Map<String, String> values)
+        throws IOException, HasException {
+        File confFile = new File(getConfDir().getAbsolutePath(), confName);
+        if (confFile.exists()) {
+            // Update conf file content
+            InputStream templateResource;
+            if (confName.equals("has-server.conf")) {
+                templateResource = new FileInputStream(confFile);
+            } else {
+                String resourcePath = "/" + confName + ".template";
+                templateResource = getClass().getResourceAsStream(resourcePath);
+            }
+            String content = IOUtil.readInput(templateResource);
+            for (Map.Entry<String, String> entry : values.entrySet()) {
+                content = content.replaceAll(Pattern.quote(entry.getKey()), entry.getValue());
+            }
+
+            // Delete the original conf file
+            boolean delete = confFile.delete();
+            if (!delete) {
+                throw new HasException("Failed to delete conf file: " + confName);
+            }
+
+            // Save the updated conf file
+            IOUtil.writeFile(content, confFile);
+        } else {
+            throw new HasException("Conf file: " + confName + " not found.");
+        }
+    }
+
+    /**
+     * Get KDC Config from MySQL.
+     *
+     * @return Kdc config
+     * @throws KrbException e
+     */
+    private Map<String, String> getKdcConf() throws HasException {
+        PreparedStatement preStm = null;
+        ResultSet result = null;
+        Map<String, String> kdcConf = new HashMap<>();
+        BackendConfig backendConfig;
+        try {
+            backendConfig = KdcUtil.getBackendConfig(getConfDir());
+        } catch (KrbException e) {
+            throw new HasException("Getting backend config failed." + e.getMessage());
+        }
+        String driver = backendConfig.getString("mysql_driver");
+        String url = backendConfig.getString("mysql_url");
+        String user = backendConfig.getString("mysql_user");
+        String password = backendConfig.getString("mysql_password");
+        Connection connection = startConnection(driver, url, user, password);
+        try {
+
+            // Get Kdc configuration from kdc_config table
+            String stmKdc = "SELECT * FROM `kdc_config` WHERE id = 1";
+            preStm = connection.prepareStatement(stmKdc);
+            result = preStm.executeQuery();
+            while (result.next()) {
+                String realm = result.getString("realm");
+                String servers = result.getString("servers");
+                String port = String.valueOf(result.getInt("port"));
+                kdcConf.put("servers", servers);
+                kdcConf.put("_PORT_", port);
+                kdcConf.put("_REALM_", realm);
+            }
+
+        } catch (SQLException e) {
+            LOG.error("Error occurred while getting kdc config.");
+            throw new HasException("Failed to get kdc config. ", e);
+        } finally {
+            DbUtils.closeQuietly(preStm);
+            DbUtils.closeQuietly(result);
+            DbUtils.closeQuietly(connection);
+        }
+
+        return kdcConf;
+    }
+
+    /**
+     * Update KDC conf file.
+     *
+     * @throws KrbException e
+     */
+    private void updateKdcConf() throws HasException {
+        try {
+            Map<String, String> values = getKdcConf();
+            String host = getKdcHost();
+            if (host == null) {
+                host = getWebServer().getBindAddress().getHostName();
+            }
+            values.remove("servers");
+            values.put("_HOST_", host);
+            updateConfFile("kdc.conf", values);
+        } catch (IOException e) {
+            throw new HasException("Failed to update kdc config. ", e);
+        }
+    }
+
+      /**
+     * Start the MySQL connection.
+     *
+     * @param url url of connection
+     * @param user username of connection
+     * @param password password of connection
+     * @throws KrbException e
+     * @return MySQL JDBC connection
+     */
+    private Connection startConnection(String driver, String url, String user,
+                                       String password) throws HasException {
+        Connection connection;
+        try {
+            Class.forName(driver);
+            connection = DriverManager.getConnection(url, user, password);
+            if (!connection.isClosed()) {
+                LOG.info("Succeeded in connecting to MySQL.");
+            }
+        } catch (ClassNotFoundException e) {
+            throw new HasException("JDBC Driver Class not found. ", e);
+        } catch (SQLException e) {
+            throw new HasException("Failed to connecting to MySQL. ", e);
+        }
+
+        return connection;
+    }
+
+    /**
+     * Config HAS server KDC which have MySQL backend.
+     * @param backendConfig MySQL backend config
+     * @param realm KDC realm to set
+     * @param host KDC host to set
+     * @param hasServer has server to get param
+     * @throws HasException e
+     */
+    public void configMySQLKdc(BackendConfig backendConfig, String realm, int port,
+                               String host, HasServer hasServer) throws HasException {
+
+        // Start mysql connection
+        String driver = backendConfig.getString("mysql_driver");
+        String url = backendConfig.getString("mysql_url");
+        String user = backendConfig.getString("mysql_user");
+        String password = backendConfig.getString("mysql_password");
+        Connection connection = startConnection(driver, url, user, password);
+
+        ResultSet resConfig = null;
+        PreparedStatement preStm = null;
+        try {
+            createKdcTable(connection); // Create kdc_config table if not exists
+            String stm = "SELECT * FROM `kdc_config` WHERE id = 1";
+            preStm = connection.prepareStatement(stm);
+            resConfig = preStm.executeQuery();
+            if (!resConfig.next()) {
+                addKdcConfig(connection, realm, port, host);
+            } else {
+                String oldHost = hasServer.getKdcHost();
+                String servers = resConfig.getString("servers");
+                String[] serverArray = servers.split(",");
+                List<String> serverList = new ArrayList<>();
+                Collections.addAll(serverList, serverArray);
+                if (serverList.contains(oldHost)) {
+                    servers = servers.replaceAll(oldHost, host);
+                } else {
+                    servers = servers + "," + host;
+                }
+                boolean initialized = resConfig.getBoolean("initialized");
+                updateKdcConfig(connection, initialized, port, realm, servers);
+            }
+            hasServer.setKdcHost(host);
+        } catch (SQLException e) {
+            throw new HasException("Failed to config HAS KDC. ", e);
+        } finally {
+            DbUtils.closeQuietly(preStm);
+            DbUtils.closeQuietly(resConfig);
+            DbUtils.closeQuietly(connection);
+        }
+    }
+
+    /**
+     * Create kdc_config table in database.
+     * @param conn database connection
+     * @throws KrbException e
+     */
+    private void createKdcTable(final Connection conn) throws HasException {
+        PreparedStatement preStm = null;
+        try {
+            String stm = "CREATE TABLE IF NOT EXISTS `kdc_config` ("
+                + "port INTEGER DEFAULT 88, servers VARCHAR(255) NOT NULL, "
+                + "initialized bool DEFAULT FALSE, realm VARCHAR(255) "
+                + "DEFAULT NULL, id INTEGER DEFAULT 1, CHECK (id=1), PRIMARY KEY (id)) "
+                + "ENGINE=INNODB;";
+            preStm = conn.prepareStatement(stm);
+            preStm.executeUpdate();
+        } catch (SQLException e) {
+            throw new HasException("Failed to create kdc_config table. ", e);
+        } finally {
+            DbUtils.closeQuietly(preStm);
+        }
+    }
+
+    /**
+     * Add KDC Config information in database.
+     * @param conn database connection
+     * @param realm realm to add
+     * @param port port to add
+     * @param host host to add
+     */
+    private void addKdcConfig(Connection conn, String realm, int port, String host)
+        throws HasException {
+        PreparedStatement preStm = null;
+        try {
+            String stm = "INSERT INTO `kdc_config` (port, servers, realm)" + " VALUES(?, ?, ?)";
+            preStm = conn.prepareStatement(stm);
+            preStm.setInt(1, port);
+            preStm.setString(2, host);
+            preStm.setString(3, realm);
+            preStm.executeUpdate();
+        } catch (SQLException e) {
+            throw new HasException("Failed to insert into kdc_config table. ", e);
+        } finally {
+            DbUtils.closeQuietly(preStm);
+        }
+    }
+
+    /**
+     * Update KDC Config record in database.
+     * @param conn database connection
+     * @param realm realm to update
+     * @param port port to update
+     * @param servers servers to update
+     * @param initialized initial state of KDC Config
+     */
+    private void updateKdcConfig(Connection conn, boolean initialized, int port,
+                                 String realm, String servers) throws HasException {
+        PreparedStatement preStm = null;
+        try {
+            if (initialized) {
+                String stmUpdate = "UPDATE `kdc_config` SET servers = ? WHERE id = 1";
+                preStm = conn.prepareStatement(stmUpdate);
+                preStm.setString(1, servers);
+                preStm.executeUpdate();
+            } else {
+                String stmUpdate = "UPDATE `kdc_config` SET port = ?, realm = ?, servers = ? WHERE id = 1";
+                preStm = conn.prepareStatement(stmUpdate);
+                preStm.setInt(1, port);
+                preStm.setString(2, realm);
+                preStm.setString(3, servers);
+                preStm.executeUpdate();
+            }
+        } catch (SQLException e) {
+            throw new HasException("Failed to update KDC Config. ", e);
+        } finally {
+            DbUtils.closeQuietly(preStm);
+        }
+    }
+
+    /**
+     * Read in krb5-template.conf and substitute in the correct port.
+     *
+     * @return krb5 conf file
+     * @throws IOException e
+     * @throws KrbException e
+     */
+    public File generateKrb5Conf() throws HasException {
+        Map<String, String> kdcConf = getKdcConf();
+        String[] servers = kdcConf.get("servers").split(",");
+        int kdcPort = Integer.parseInt(kdcConf.get("_PORT_"));
+        String kdcRealm = kdcConf.get("_REALM_");
+        StringBuilder kdcBuilder = new StringBuilder();
+        for (String server : servers) {
+            String append = "\t\tkdc = " + server.trim() + ":" + kdcPort + "\n";
+            kdcBuilder.append(append);
+        }
+        String kdc = kdcBuilder.toString();
+        kdc = kdc.substring(0, kdc.length() - 1);
+        String resourcePath = "/krb5.conf.template";
+        InputStream templateResource = getClass().getResourceAsStream(resourcePath);
+        String content = null;
+        try {
+            content = IOUtil.readInput(templateResource);
+        } catch (IOException e) {
+            throw new HasException("Read template resource failed. " + e);
+        }
+        content = content.replaceAll("_REALM_", kdcRealm);
+        content = content.replaceAll("_PORT_", String.valueOf(kdcPort));
+        content = content.replaceAll("_UDP_LIMIT_", "4096");
+        content = content.replaceAll("_KDCS_", kdc);
+        File confFile = new File(confDir, "krb5.conf");
+        if (confFile.exists()) {
+            boolean delete = confFile.delete();
+            if (!delete) {
+                throw new HasException("File delete error!");
+            }
+        }
+        try {
+            IOUtil.writeFile(content, confFile);
+        } catch (IOException e) {
+            throw new HasException("Write content to conf file failed. " + e);
+        }
+
+        return confFile;
+    }
+
+    /**
+     * Read in has-server.conf and create has-client.conf.
+     *
+     * @return has conf file
+     * @throws IOException e
+     * @throws HasException e
+     */
+    public File generateHasConf() throws HasException, IOException {
+        Map<String, String> kdcConf = getKdcConf();
+        String servers = kdcConf.get("servers");
+        File confFile = new File(getConfDir().getAbsolutePath(), "has-server.conf");
+        HasConfig hasConfig = HasUtil.getHasConfig(confFile);
+        if (hasConfig != null) {
+            String defaultValue = hasConfig.getHttpsHost();
+            InputStream templateResource = new FileInputStream(confFile);
+            String content = IOUtil.readInput(templateResource);
+            content = content.replaceFirst(Pattern.quote(defaultValue), servers);
+            File hasFile = new File(confDir, "has-client.conf");
+            IOUtil.writeFile(content, hasFile);
+            return hasFile;
+        } else {
+            throw new HasException("has-server.conf not found. ");
+        }
+    }
+
     public void stopKdcServer() {
         try {
             kdcServer.stop();
diff --git a/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/ConfFilter.java b/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/ConfFilter.java
index 04c3537..e300886 100644
--- a/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/ConfFilter.java
+++ b/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/ConfFilter.java
@@ -40,7 +40,7 @@
                     new File(hasServer.getConfDir(), "has-server.conf"));
             String isEnableConf = hasConfig.getEnableConf();
             if (!isEnableConf.equals("true")) {
-                throw new RuntimeException("The kdc has started.");
+                throw new RuntimeException("The KDC has started, please stop KDC before setting.");
             }
             filterChain.doFilter(servletRequest, servletResponse);
         } catch (HasException e) {
diff --git a/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/ConfigApi.java b/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/ConfigApi.java
new file mode 100644
index 0000000..fdb5cac
--- /dev/null
+++ b/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/ConfigApi.java
@@ -0,0 +1,278 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kerby.has.server.web.rest;
+
+import org.apache.kerby.has.common.HasConfig;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.common.util.HasUtil;
+import org.apache.kerby.has.server.HasServer;
+import org.apache.kerby.has.server.web.WebServer;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
+import org.apache.kerby.kerberos.kerb.server.KdcUtil;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * HAS configure web methods implementation.
+ */
+@Path("/conf")
+public class ConfigApi {
+
+    @Context
+    private ServletContext context;
+
+    @Context
+    private HttpServletRequest httpRequest;
+
+    /**
+     * Set HAS plugin.
+     *
+     * @param plugin HAS plugin name
+     * @return Response
+     */
+    @PUT
+    @Path("/setplugin")
+    @Consumes({MediaType.TEXT_PLAIN})
+    @Produces({MediaType.TEXT_PLAIN})
+    public Response setPlugin(@QueryParam("plugin") final String plugin) {
+        if (httpRequest.isSecure()) {
+            final HasServer hasServer = WebServer.getHasServerFromContext(context);
+            WebServer.LOG.info("Set HAS plugin...");
+            String msg;
+            try {
+                Map<String, String> values = new HashMap<>();
+                File hasConfFile = new File(hasServer.getConfDir(), "has-server.conf");
+                HasConfig hasConfig = HasUtil.getHasConfig(hasConfFile);
+                if (hasConfig != null) {
+                    String defaultValue = hasConfig.getPluginName();
+                    values.put(defaultValue, plugin);
+                } else {
+                    msg = "has-server.conf not found.";
+                    WebServer.LOG.error(msg);
+                    return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
+                }
+                hasServer.updateConfFile("has-server.conf", values);
+            } catch (IOException | HasException e) {
+                msg = "Failed to set HAS plugin, because: " + e.getMessage();
+                WebServer.LOG.error(msg);
+                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
+            }
+            msg = "HAS plugin set successfully.";
+            WebServer.LOG.info(msg);
+            return Response.ok(msg).build();
+        }
+        return Response.status(Response.Status.FORBIDDEN).entity("HTTPS required.\n").build();
+    }
+
+    /**
+     * Config HAS server backend.
+     *
+     * @param backendType type of backend
+     * @param dir         json dir
+     * @param driver      mysql JDBC connector driver
+     * @param url         mysql JDBC connector url
+     * @param user        mysql user name
+     * @param password    mysql password of user
+     * @return Response
+     */
+    @PUT
+    @Path("/configbackend")
+    @Consumes({MediaType.APPLICATION_JSON})
+    @Produces({MediaType.TEXT_PLAIN})
+    public Response configBackend(
+        @QueryParam("backendType") final String backendType,
+        @QueryParam("dir") @DefaultValue("/tmp/has/jsonbackend") final String dir,
+        @QueryParam("driver") @DefaultValue("org.drizzle.jdbc.DrizzleDriver") final String driver,
+        @QueryParam("url") @DefaultValue("jdbc:mysql:thin://127.0.0.1:3306/mysqlbackend") final String url,
+        @QueryParam("user") @DefaultValue("root") final String user,
+        @QueryParam("password") @DefaultValue("passwd") final String password) {
+
+        if (httpRequest.isSecure()) {
+            final HasServer hasServer = WebServer.getHasServerFromContext(context);
+            String msg;
+            if ("json".equals(backendType)) {
+                WebServer.LOG.info("Set Json backend...");
+                try {
+                    Map<String, String> values = new HashMap<>();
+                    values.put("_JAR_", "org.apache.kerby.kerberos.kdc.identitybackend.JsonIdentityBackend");
+                    values.put("#_JSON_DIR_", "backend.json.dir = " + dir);
+                    values.put("#_MYSQL_\n", "");
+                    hasServer.updateConfFile("backend.conf", values);
+                } catch (IOException | HasException e) {
+                    msg = "Failed to set Json backend, because: " + e.getMessage();
+                    WebServer.LOG.error(msg);
+                    return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
+                }
+                msg = "Json backend set successfully.";
+                WebServer.LOG.info(msg);
+                return Response.ok(msg).build();
+            } else if ("mysql".equals(backendType)) {
+                WebServer.LOG.info("Set MySQL backend...");
+                try {
+                    String drizzleUrl = url.replace("jdbc:mysql:", "jdbc:mysql:thin:");
+                    String mysqlConfig = "mysql_driver = " + driver + "\nmysql_url = " + drizzleUrl
+                        + "\nmysql_user = " + user + "\nmysql_password = " + password;
+                    Map<String, String> values = new HashMap<>();
+                    values.put("_JAR_", "org.apache.kerby.has.server.kdc.MySQLIdentityBackend");
+                    values.put("#_JSON_DIR_\n", "");
+                    values.put("#_MYSQL_", mysqlConfig);
+                    hasServer.updateConfFile("backend.conf", values);
+                } catch (IOException | HasException e) {
+                    msg = "Failed to set MySQL backend, because: " + e.getMessage();
+                    WebServer.LOG.error(msg);
+                    return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
+                }
+                msg = "MySQL backend set successfully.";
+                WebServer.LOG.info(msg);
+                return Response.ok(msg).build();
+            } else {
+                msg = backendType + " is not supported.";
+                WebServer.LOG.info(msg);
+                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
+            }
+        }
+        return Response.status(Response.Status.FORBIDDEN).entity("HTTPS required.\n").build();
+    }
+
+    /**
+     * Config HAS server KDC.
+     * @param port KDC port to set
+     * @param realm KDC realm to set
+     * @param host KDC host to set
+     * @return Response
+     */
+    @PUT
+    @Path("/configkdc")
+    @Consumes({MediaType.TEXT_PLAIN})
+    @Produces({MediaType.TEXT_PLAIN})
+    public Response configKdc(
+        @QueryParam("port") final int port,
+        @QueryParam("realm") final String realm,
+        @QueryParam("host") final String host) {
+        if (httpRequest.isSecure()) {
+            final HasServer hasServer = WebServer.getHasServerFromContext(context);
+            WebServer.LOG.info("Config HAS server KDC...");
+            String msg;
+            try {
+                BackendConfig backendConfig = KdcUtil.getBackendConfig(hasServer.getConfDir());
+                String backendJar = backendConfig.getString("kdc_identity_backend");
+                if (backendJar.equals("org.apache.kerby.has.server.kdc.MySQLIdentityBackend")) {
+                    hasServer.configMySQLKdc(backendConfig, realm, port, host, hasServer);
+                } else {
+                    Map<String, String> values = new HashMap<>();
+                    values.put("_HOST_", host);
+                    values.put("_PORT_", String.valueOf(port));
+                    values.put("_REALM_", realm);
+                    hasServer.updateConfFile("kdc.conf", values);
+                    String kdc = "\t\tkdc = " + host + ":" + port;
+                    values.put("_KDCS_", kdc);
+                    values.put("_UDP_LIMIT_", "4096");
+                    hasServer.updateConfFile("krb5.conf", values);
+                }
+            } catch (IOException | HasException | KrbException e) {
+                msg = "Failed to config HAS KDC, because: " + e.getMessage();
+                WebServer.LOG.error(msg);
+                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
+            }
+            msg = "HAS server KDC set successfully.";
+            WebServer.LOG.info(msg);
+            return Response.ok(msg).build();
+        }
+        return Response.status(Response.Status.FORBIDDEN).entity("HTTPS required.\n").build();
+    }
+
+    /**
+     * Get krb5.conf file.
+     *
+     * @return Response
+     */
+    @GET
+    @Path("/getkrb5conf")
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response getKrb5Conf() {
+        if (httpRequest.isSecure()) {
+            final HasServer hasServer = WebServer.getHasServerFromContext(context);
+            String msg;
+            try {
+                BackendConfig backendConfig = KdcUtil.getBackendConfig(hasServer.getConfDir());
+                String backendJar = backendConfig.getString("kdc_identity_backend");
+                File conf;
+                if (backendJar.equals("org.apache.kerby.has.server.kdc.MySQLIdentityBackend")) {
+                    conf = hasServer.generateKrb5Conf();
+                } else {
+                    File confDir = hasServer.getConfDir();
+                    conf = new File(confDir, "krb5.conf");
+                }
+                return Response.ok(conf).header("Content-Disposition", "attachment; filename=krb5.conf").build();
+            } catch (KrbException | HasException e) {
+                msg = "Failed to get Krb5.conf, because: " + e.getMessage();
+                WebServer.LOG.error(msg);
+                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
+            }
+        }
+        return Response.status(Response.Status.FORBIDDEN).entity("HTTPS required.\n").build();
+    }
+
+    /**
+     * Get has-client.conf file.
+     *
+     * @return Response
+     */
+    @GET
+    @Path("/gethasclientconf")
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response getHasConf() {
+        if (httpRequest.isSecure()) {
+            final HasServer hasServer = WebServer.getHasServerFromContext(context);
+            String msg;
+            try {
+                BackendConfig backendConfig = KdcUtil.getBackendConfig(hasServer.getConfDir());
+                String backendJar = backendConfig.getString("kdc_identity_backend");
+                File conf;
+                if (backendJar.equals("org.apache.kerby.has.server.kdc.MySQLIdentityBackend")) {
+                    conf = hasServer.generateHasConf();
+                } else {
+                    File confDir = hasServer.getConfDir();
+                    conf = new File(confDir, "has-server.conf");
+                }
+                return Response.ok(conf).header("Content-Disposition", "attachment; filename=has-client.conf").build();
+            } catch (IOException | KrbException | HasException e) {
+                msg = "Failed to get has-client.conf, because: " + e.getMessage();
+                WebServer.LOG.error(msg);
+                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
+            }
+        }
+        return Response.status(Response.Status.FORBIDDEN).entity("HTTPS required.\n").build();
+    }
+}
diff --git a/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/HadminApi.java b/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/HadminApi.java
index f1ba87c..d91f49d 100644
--- a/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/HadminApi.java
+++ b/has-project/has-server/src/main/java/org/apache/kerby/has/server/web/rest/HadminApi.java
@@ -257,7 +257,7 @@
                 return Response.serverError().entity("has-server.conf is not exists.")
                     .build();
             }
-            String result = "";
+            String result;
             if (isEnable.equals("true")) {
                 result = "enable";
             } else if (isEnable.equals("false")) {
diff --git a/has-project/has-server/src/main/resources/backend.conf.template b/has-project/has-server/src/main/resources/backend.conf.template
new file mode 100644
index 0000000..598be24
--- /dev/null
+++ b/has-project/has-server/src/main/resources/backend.conf.template
@@ -0,0 +1,21 @@
+#
+# 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 = _JAR_
+#_JSON_DIR_
+#_MYSQL_
diff --git a/has-project/has-server/src/main/resources/kdc.conf.template b/has-project/has-server/src/main/resources/kdc.conf.template
new file mode 100644
index 0000000..3800cb3
--- /dev/null
+++ b/has-project/has-server/src/main/resources/kdc.conf.template
@@ -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 = _HOST_
+  kdc_udp_port = _PORT_
+  kdc_tcp_port = _PORT_
+  kdc_realm = _REALM_
diff --git a/has-project/has-server/src/main/resources/krb5.conf.template b/has-project/has-server/src/main/resources/krb5.conf.template
new file mode 100644
index 0000000..ee090c4
--- /dev/null
+++ b/has-project/has-server/src/main/resources/krb5.conf.template
@@ -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 = _REALM_
+    default_realm = _REALM_
+    udp_preference_limit = _UDP_LIMIT_
+    kdc_tcp_port = _PORT_
+    kdc_udp_port = _PORT_
+
+[realms]
+    _REALM_ = {
+_KDCS_
+    }
diff --git a/kerby-dist/has-dist/conf/has-server.conf b/kerby-dist/has-dist/conf/has-server.conf
index 43a83fb..70e643b 100644
--- a/kerby-dist/has-dist/conf/has-server.conf
+++ b/kerby-dist/has-dist/conf/has-server.conf
@@ -22,3 +22,5 @@
   filter_auth_type = kerberos
   enable_conf = true
 
+[PLUGIN]
+  auth_type = RAM
diff --git a/pom.xml b/pom.xml
index aea984f..40a4719 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,6 +69,8 @@
     <jersey.container.version>2.17</jersey.container.version>
     <jersey.version>1.19</jersey.version>
     <commons-text.version>1.1</commons-text.version>
+    <commons-dbutils.version>1.6</commons-dbutils.version>
+    <drizzle-jdbc.version>1.4</drizzle-jdbc.version>
   </properties>
 
   <prerequisites>