You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by pl...@apache.org on 2017/11/28 03:04:15 UTC

[14/15] directory-kerby git commit: Change the Maven groupId in HAS folder to org.apache.kerby.

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClientPluginRegistry.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClientPluginRegistry.java b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClientPluginRegistry.java
deleted file mode 100644
index 0254ed6..0000000
--- a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClientPluginRegistry.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.hadoop.has.client;
-
-import org.apache.hadoop.has.common.HasException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.ServiceLoader;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class HasClientPluginRegistry {
-    static final Logger LOG = LoggerFactory.getLogger(HasClientPluginRegistry.class);
-
-    private static Map<String, Class> allPlugins = new ConcurrentHashMap<>();
-
-    static {
-        ServiceLoader<HasClientPlugin> plugins = ServiceLoader.load(HasClientPlugin.class);
-
-        for (HasClientPlugin plugin : plugins) {
-            allPlugins.put(plugin.getLoginType(), plugin.getClass());
-        }
-    }
-
-    public static Set<String> registeredPlugins() {
-        return Collections.unmodifiableSet(allPlugins.keySet());
-    }
-
-    public static boolean registeredPlugin(String name) {
-        return allPlugins.containsKey(name);
-    }
-
-    public static HasClientPlugin createPlugin(String name) throws HasException {
-        if (!registeredPlugin(name)) {
-            throw new HasException("Unregistered plugin " + name);
-        }
-        try {
-            HasClientPlugin clientPlugin = (HasClientPlugin) allPlugins.get(name).newInstance();
-            return clientPlugin;
-        } catch (Exception e) {
-            LOG.error("Create {} plugin failed", name, e);
-            throw new HasException(e.getMessage());
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginException.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginException.java b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginException.java
deleted file mode 100644
index c07eb59..0000000
--- a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginException.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * 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.hadoop.has.client;
-
-import org.apache.hadoop.has.common.HasException;
-
-public class HasLoginException extends HasException {
-    private static final long serialVersionUID = 4140429098192628252L;
-
-    public HasLoginException(Throwable cause) {
-        super(cause);
-    }
-
-    public HasLoginException(String message) {
-        super(message);
-    }
-
-    public HasLoginException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginModule.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginModule.java b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginModule.java
deleted file mode 100644
index 6c71236..0000000
--- a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginModule.java
+++ /dev/null
@@ -1,491 +0,0 @@
-/**
- * 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.hadoop.has.client;
-
-import com.sun.security.auth.module.Krb5LoginModule;
-import org.apache.hadoop.has.common.HasException;
-import org.apache.kerby.kerberos.kerb.ccache.Credential;
-import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.krb5.Credentials;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.PrincipalName;
-
-import javax.security.auth.DestroyFailedException;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.kerberos.KerberosTicket;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-import java.io.IOException;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Login with tgt ticket
- * The client's TGT will be retrieved from the API of HasClient
- */
-//CHECKSTYLE.OFF
-public class HasLoginModule implements LoginModule {
-
-    public static final Logger LOG = LoggerFactory.getLogger(HasLoginModule.class);
-
-    Krb5LoginModule krb5LoginModule;
-
-    // initial state
-    private Subject subject;
-    private CallbackHandler callbackHandler;
-    private Map<String, Object> sharedState;
-    private Map<String, ?> options;
-
-    // configurable option
-    private boolean debug = false;
-    private boolean doNotPrompt = false;
-    private boolean useTgtTicket = false;
-    private String hadoopSecurityHas = null;
-    private String princName = null;
-
-    private boolean refreshKrb5Config = false;
-
-    // specify if initiator.
-    // perform authentication exchange if initiator
-    private boolean isInitiator = true;
-
-    // the authentication status
-    private boolean succeeded = false;
-    private boolean commitSucceeded = false;
-
-    private Credentials cred = null;
-
-    private PrincipalName principal = null;
-    private KerberosPrincipal kerbClientPrinc = null;
-    private KerberosTicket kerbTicket = null;
-    private StringBuffer krb5PrincName = null;
-    private boolean unboundServer = false;
-
-    /**
-     * Initialize this <code>LoginModule</code>.
-     * <p>
-     * <p>
-     *
-     * @param subject         the <code>Subject</code> to be authenticated. <p>
-     * @param callbackHandler a <code>CallbackHandler</code> for
-     *                        communication with the end user (prompting for
-     *                        usernames and passwords, for example). <p>
-     * @param sharedState     shared <code>LoginModule</code> state. <p>
-     * @param options         options specified in the login
-     *                        <code>Configuration</code> for this particular
-     *                        <code>LoginModule</code>.
-     */
-    public void initialize(Subject subject,
-                           CallbackHandler callbackHandler,
-                           Map<String, ?> sharedState,
-                           Map<String, ?> options) {
-
-        this.subject = subject;
-        this.callbackHandler = callbackHandler;
-        this.sharedState = (Map<String, Object>) sharedState;
-        this.options = options;
-
-        // initialize any configured options
-        useTgtTicket = "true".equalsIgnoreCase((String) options.get("useTgtTicket"));
-
-        if (useTgtTicket) {
-            debug = "true".equalsIgnoreCase((String) options.get("debug"));
-            doNotPrompt = "true".equalsIgnoreCase((String) options.get("doNotPrompt"));
-            useTgtTicket = "true".equalsIgnoreCase((String) options.get("useTgtTicket"));
-            hadoopSecurityHas = (String) options.get("hadoopSecurityHas");
-            princName = (String) options.get("principal");
-            refreshKrb5Config =
-                "true".equalsIgnoreCase((String) options.get("refreshKrb5Config"));
-
-            // check isInitiator value
-            String isInitiatorValue = ((String) options.get("isInitiator"));
-            if (isInitiatorValue != null) {
-                // use default, if value not set
-                isInitiator = "true".equalsIgnoreCase(isInitiatorValue);
-            }
-
-            if (debug) {
-                System.out.print("Debug is  " + debug
-                    + " doNotPrompt " + doNotPrompt
-                    + " isInitiator " + isInitiator
-                    + " refreshKrb5Config is " + refreshKrb5Config
-                    + " principal is " + princName + "\n");
-            }
-        } else {
-            krb5LoginModule = new Krb5LoginModule();
-            krb5LoginModule.initialize(subject, callbackHandler, sharedState, options);
-        }
-    }
-
-    /**
-     * Authenticate the user
-     * <p>
-     * <p>
-     *
-     * @return true in all cases since this <code>LoginModule</code>
-     * should not be ignored.
-     * @throws LoginException       if this <code>LoginModule</code>
-     *                              is unable to perform the authentication.
-     */
-    public boolean login() throws LoginException {
-
-        if (useTgtTicket) {
-            if (refreshKrb5Config) {
-                try {
-                    if (debug) {
-                        System.out.println("Refreshing Kerberos configuration");
-                    }
-                    sun.security.krb5.Config.refresh();
-                } catch (KrbException ke) {
-                    LoginException le = new LoginException(ke.getMessage());
-                    le.initCause(ke);
-                    throw le;
-                }
-            }
-            String principalProperty = System.getProperty("sun.security.krb5.principal");
-            if (principalProperty != null) {
-                krb5PrincName = new StringBuffer(principalProperty);
-            } else {
-                if (princName != null) {
-                    krb5PrincName = new StringBuffer(princName);
-                }
-            }
-
-            validateConfiguration();
-
-            if (krb5PrincName != null && krb5PrincName.toString().equals("*")) {
-                unboundServer = true;
-            }
-
-            // attempt the authentication by getting the username and pwd
-            // by prompting or configuration i.e. not from shared state
-
-            try {
-                attemptAuthentication(false);
-                succeeded = true;
-                cleanState();
-                return true;
-            } catch (LoginException e) {
-                // authentication failed -- clean out state
-                if (debug) {
-                    System.out.println("\t\t[HasLoginModule] "
-                        + "authentication failed \n"
-                        + e.getMessage());
-                }
-                succeeded = false;
-                cleanState();
-                throw e;
-            }
-        } else {
-            succeeded = krb5LoginModule.login();
-            return succeeded;
-        }
-    }
-
-    /**
-     * Process the configuration options
-     * Get the TGT from Has Client
-     */
-
-    private void attemptAuthentication(boolean getPasswdFromSharedState)
-        throws LoginException {
-
-        /*
-         * Check the creds cache to see whether
-         * we have TGT for this client principal
-         */
-        if (krb5PrincName != null) {
-            try {
-                principal = new PrincipalName(krb5PrincName.toString(),
-                        PrincipalName.KRB_NT_PRINCIPAL);
-            } catch (KrbException e) {
-                LoginException le = new LoginException(e.getMessage());
-                le.initCause(e);
-                throw le;
-            }
-        }
-
-        try {
-            if (useTgtTicket) {
-                if (debug) {
-                    System.out.println("use tgt ticket to login, acquire TGT TICKET...");
-                }
-
-                HasClient hasClient = new HasClient(hadoopSecurityHas);
-                TgtTicket tgtTicket = null;
-                try {
-                    tgtTicket = hasClient.requestTgt();
-                } catch (HasException e) {
-                    LoginException le = new LoginException(e.getMessage());
-                    le.initCause(e);
-                    throw le;
-                }
-                Credential credential = new Credential(tgtTicket);
-                boolean[] flags = new boolean[7];
-                int flag = credential.getTicketFlags().getFlags();
-                for (int i = 6; i >= 0; i--) {
-                    flags[i] = (flag & (1 << i)) != 0;
-                }
-                Date startTime = null;
-                if (credential.getStartTime() != null) {
-                    startTime = credential.getStartTime().getValue();
-                }
-                cred = new Credentials(credential.getTicket().encode(),
-                    credential.getClientName().getName(),
-                    credential.getServerName().getName(),
-                    credential.getKey().getKeyData(),
-                    credential.getKey().getKeyType().getValue(),
-                    flags,
-                    credential.getAuthTime().getValue(),
-                    startTime,
-                    credential.getEndTime().getValue(),
-                    credential.getRenewTill().getValue(),
-                    null);
-
-                if (cred != null) {
-                    // get the principal name from the ticket cache
-                    if (principal == null) {
-                        principal = cred.getClient();
-                    }
-                }
-                if (debug) {
-                    System.out.println("Principal is " + principal);
-                    if (cred == null) {
-                        System.out.println("null credentials from TGT Ticket");
-                    }
-                }
-            }
-        } catch (KrbException e) {
-            LoginException le = new LoginException(e.getMessage());
-            le.initCause(e);
-            throw le;
-        } catch (IOException ioe) {
-            LoginException ie = new LoginException(ioe.getMessage());
-            ie.initCause(ioe);
-            throw ie;
-        }
-    }
-
-    private void validateConfiguration() throws LoginException {
-        if (doNotPrompt && !useTgtTicket) {
-            throw new LoginException("Configuration Error"
-                + " - either doNotPrompt should be "
-                + " false or"
-                + " useTgtTicket"
-                + " should be true");
-        }
-
-        if (krb5PrincName != null && krb5PrincName.toString().equals("*")) {
-            if (isInitiator) {
-                throw new LoginException("Configuration Error"
-                        + " - principal cannot be * when isInitiator is true");
-            }
-        }
-    }
-
-    /**
-     * <p> This method is called if the LoginContext's
-     * overall authentication succeeded
-     *
-     * @return true if this LoginModule's own login and commit
-     * attempts succeeded, or false otherwise.
-     * @throws LoginException if the commit fails.
-     */
-
-    public boolean commit() throws LoginException {
-        if (debug) {
-            System.out.println("Login success? " + succeeded);
-        }
-
-        if (useTgtTicket) {
-        /*
-         * Let us add the Krb5 Creds to the Subject's
-         * private credentials. The credentials are of type
-         * KerberosKey or KerberosTicket
-         */
-            if (succeeded == false) {
-                return false;
-            } else {
-
-                if (isInitiator && (cred == null)) {
-                    succeeded = false;
-                    throw new LoginException("Null Client Credential");
-                }
-
-                if (subject.isReadOnly()) {
-                    cleanKerberosCred();
-                    throw new LoginException("Subject is Readonly");
-                }
-
-            /*
-             * Add the Principal (authenticated identity)
-             * to the Subject's principal set and
-             * add the credentials (TGT or Service key) to the
-             * Subject's private credentials
-             */
-
-                Set<Object> privCredSet = subject.getPrivateCredentials();
-                Set<java.security.Principal> princSet = subject.getPrincipals();
-                kerbClientPrinc = new KerberosPrincipal(principal.getName());
-
-                // create Kerberos Ticket
-                if (isInitiator) {
-                    kerbTicket = Krb5Util.credsToTicket(cred);
-                }
-
-                // Let us add the kerbClientPrinc,kerbTicket
-
-                // We won't add "*" as a KerberosPrincipal
-                if (!unboundServer
-                    && !princSet.contains(kerbClientPrinc)) {
-                    princSet.add(kerbClientPrinc);
-                }
-
-                // add the TGT
-                if (kerbTicket != null) {
-                    if (!privCredSet.contains(kerbTicket)) {
-                        privCredSet.add(kerbTicket);
-                    }
-                }
-            }
-            commitSucceeded = true;
-            if (debug) {
-                System.out.println("Commit Succeeded \n");
-            }
-            return true;
-        } else {
-            return krb5LoginModule.commit();
-        }
-    }
-
-    /**
-     * <p> This method is called if the LoginContext's
-     * overall authentication failed.
-     *
-     * @return false if this LoginModule's own login and/or commit attempts
-     * failed, and true otherwise.
-     * @throws LoginException if the abort fails.
-     */
-
-    public boolean abort() throws LoginException {
-        if (useTgtTicket) {
-            if (succeeded == false) {
-                return false;
-            } else if (succeeded == true && commitSucceeded == false) {
-                // login succeeded but overall authentication failed
-                succeeded = false;
-                cleanKerberosCred();
-            } else {
-                // overall authentication succeeded and commit succeeded,
-                // but someone else's commit failed
-                logout();
-            }
-            return true;
-        } else {
-            return krb5LoginModule.abort();
-        }
-    }
-
-    /**
-     * Logout the user.
-     * <p>
-     * <p> This method removes the <code>Krb5Principal</code>
-     * that was added by the <code>commit</code> method.
-     * <p>
-     * <p>
-     *
-     * @return true in all cases since this <code>LoginModule</code>
-     * should not be ignored.
-     * @throws LoginException if the logout fails.
-     */
-    public boolean logout() throws LoginException {
-
-        if (useTgtTicket) {
-            if (debug) {
-                System.out.println("\t\t[Krb5LoginModule]: "
-                    + "Entering logout");
-            }
-
-            if (subject.isReadOnly()) {
-                cleanKerberosCred();
-                throw new LoginException("Subject is Readonly");
-            }
-
-            subject.getPrincipals().remove(kerbClientPrinc);
-            // Let us remove all Kerberos credentials stored in the Subject
-            Iterator<Object> it = subject.getPrivateCredentials().iterator();
-            while (it.hasNext()) {
-                Object o = it.next();
-                if (o instanceof KerberosTicket) {
-                    it.remove();
-                }
-            }
-            // clean the kerberos ticket and keys
-            cleanKerberosCred();
-
-            succeeded = false;
-            commitSucceeded = false;
-            if (debug) {
-                System.out.println("\t\t[HasLoginModule]: "
-                    + "logged out Subject");
-            }
-            return true;
-        } else {
-            return krb5LoginModule.logout();
-        }
-    }
-
-    /**
-     * Clean Kerberos credentials
-     */
-    private void cleanKerberosCred() throws LoginException {
-        // Clean the ticket and server key
-        try {
-            if (kerbTicket != null) {
-                kerbTicket.destroy();
-            }
-        } catch (DestroyFailedException e) {
-            throw new LoginException("Destroy Failed on Kerberos Private Credentials");
-        }
-        kerbTicket = null;
-        kerbClientPrinc = null;
-    }
-
-    /**
-     * Clean out the state
-     */
-    private void cleanState() {
-
-        if (!succeeded) {
-            // remove temp results for the next try
-            principal = null;
-        }
-        if (krb5PrincName != null && krb5PrincName.length() != 0) {
-            krb5PrincName.delete(0, krb5PrincName.length());
-        }
-        krb5PrincName = null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-client/src/main/java/org/apache/kerby/has/client/AbstractHasClientPlugin.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/kerby/has/client/AbstractHasClientPlugin.java b/has/has-client/src/main/java/org/apache/kerby/has/client/AbstractHasClientPlugin.java
new file mode 100644
index 0000000..f60a6d0
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/kerby/has/client/AbstractHasClientPlugin.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.client;
+
+import org.apache.kerby.has.common.HasConfig;
+import org.apache.kerby.kerberos.kerb.KrbRuntime;
+import org.apache.kerby.kerberos.kerb.type.base.AuthToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractHasClientPlugin implements HasClientPlugin {
+    public static final Logger LOG = LoggerFactory.getLogger(AbstractHasClientPlugin.class);
+
+    protected abstract void doLogin(AuthToken token) throws HasLoginException;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public AuthToken login(HasConfig conf) throws HasLoginException {
+
+        AuthToken authToken = KrbRuntime.getTokenProvider("JWT").createTokenFactory().createToken();
+
+        doLogin(authToken);
+
+        return authToken;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-client/src/main/java/org/apache/kerby/has/client/HasAdminClient.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/kerby/has/client/HasAdminClient.java b/has/has-client/src/main/java/org/apache/kerby/has/client/HasAdminClient.java
new file mode 100644
index 0000000..7b6457a
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/kerby/has/client/HasAdminClient.java
@@ -0,0 +1,480 @@
+/**
+ * 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.client;
+
+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.kerby.has.common.HasAdmin;
+import org.apache.kerby.has.common.HasConfig;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.common.ssl.SSLFactory;
+import org.apache.kerby.has.common.util.URLConnectionFactory;
+import org.apache.kerby.kerberos.kerb.common.KrbUtil;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.ws.rs.core.MultivaluedMap;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Admin client API for applications to interact with KDC
+ */
+public class HasAdminClient implements HasAdmin {
+
+    public static final Logger LOG = LoggerFactory.getLogger(HasAdminClient.class);
+
+    private HasConfig hasConfig;
+    private File confDir;
+
+    public HasAdminClient(HasConfig hasConfig) {
+        this.hasConfig = hasConfig;
+    }
+    public HasAdminClient(HasConfig hasConfig, File confDir) {
+        this.hasConfig = hasConfig;
+        this.confDir = confDir;
+    }
+
+    public File getConfDir() {
+        return confDir;
+    }
+
+    public HasConfig getHasConfig() {
+        return hasConfig;
+    }
+
+    protected HttpURLConnection getHttpsConnection(URL url, boolean isSpnego) throws Exception {
+        HasConfig conf = new HasConfig();
+
+        conf.setString(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY, "ALLOW_ALL");
+        String sslClientConf = hasConfig.getSslClientConf();
+        conf.setString(SSLFactory.SSL_CLIENT_CONF_KEY, sslClientConf);
+        conf.setBoolean(SSLFactory.SSL_REQUIRE_CLIENT_CERT_KEY, false);
+
+        URLConnectionFactory connectionFactory = URLConnectionFactory
+                .newDefaultURLConnectionFactory(conf);
+        return (HttpURLConnection) connectionFactory.openConnection(url, isSpnego, hasConfig);
+    }
+
+    private WebResource getWebResource(String restName) {
+        Client client;
+        String server = null;
+        if ((hasConfig.getHttpsPort() != null) && (hasConfig.getHttpsHost() != null)) {
+            server = "https://" + hasConfig.getHttpsHost() + ":" + hasConfig.getHttpsPort()
+                    + "/has/v1/" + restName;
+            LOG.info("Admin request url: " + server);
+            HasConfig conf = new HasConfig();
+            try {
+                conf.addIniConfig(new File(hasConfig.getSslClientConf()));
+            } catch (IOException e) {
+                throw new RuntimeException("Errors occurred when adding ssl conf. "
+                    + e.getMessage());
+            }
+            SslConfigurator sslConfigurator = SslConfigurator.newInstance()
+                    .trustStoreFile(conf.getString("ssl.client.truststore.location"))
+                    .trustStorePassword(conf.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.create(clientConfig);
+        } else {
+            client = Client.create();
+        }
+        if (server == null) {
+            throw new RuntimeException("Please set the https address and port.");
+        }
+        return client.resource(server);
+    }
+
+    /**
+     * Change principals JSON string to a List.
+     *
+     * @param princs principals JSON string which like
+     *               "["HTTP\/host1@HADOOP.COM","HTTP\/host2@HADOOP.COM"]"
+     * @return principalLists.
+     */
+    private List<String> getPrincsList(String princs) {
+        List<String> principalLists = new ArrayList<>();
+        try {
+            JSONArray principals = new JSONArray(princs);
+            for (int i = 0; i < principals.length(); i++) {
+                principalLists.add("\t" + principals.getString(i));
+            }
+        } catch (Exception e) {
+            System.err.println("Errors occurred when getting the principals."
+                + e.getMessage());
+        }
+        return principalLists;
+    }
+
+    public void requestCreatePrincipals(String hostRoles) throws HasException {
+        WebResource webResource = getWebResource("admin/createprincipals");
+        String response = webResource.entity(hostRoles.toString().getBytes()).put(String.class);
+        try {
+            System.out.println(new JSONObject(response).getString("msg"));
+        } catch (JSONException e) {
+            throw new HasException(e);
+        }
+    }
+
+    @Override
+    public void addPrincipal(String principal) throws HasException {
+        WebResource webResource = getWebResource("admin/addprincipal");
+
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("principal", principal);
+        String response = webResource.queryParams(params).post(String.class);
+        try {
+            System.out.println(new JSONObject(response).getString("msg"));
+        } catch (JSONException e) {
+            System.err.println("Errors occurred when getting the message from response."
+                + e.getMessage());
+        }
+    }
+
+    @Override
+    public File getKeytabByHostAndRole(String host, String role) throws HasException {
+        WebResource webResource = getWebResource("admin/exportkeytabs");
+
+        String keytabName = host + ".zip";
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("host", host);
+        if (!role.equals("")) {
+            params.add("role", role);
+            keytabName = role + "-" + host + ".keytab";
+        }
+        ClientResponse response = webResource.queryParams(params).get(ClientResponse.class);
+        if (response.getStatus() != 200) {
+            System.err.println("Error : connection denied.");
+            return null;
+        }
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream(new File(keytabName));
+        } catch (FileNotFoundException e) {
+            System.err.println(e.getMessage());
+        }
+        InputStream in = response.getEntityInputStream();
+        byte[] buffer = new byte[4 * 1024];
+        int read;
+        try {
+            while ((read = in.read(buffer)) > 0) {
+                fos.write(buffer, 0, read);
+            }
+            fos.close();
+            in.close();
+        } catch (IOException e) {
+            System.err.println("Errors occurred when reading the buffer to write keytab file."
+                + e.getMessage());
+        }
+        System.out.println("Accept keytab file \"" + keytabName + "\" from server.");
+        return new File(keytabName);
+    }
+
+    @Override
+    public void addPrincipal(String principal, String password) throws HasException {
+        WebResource webResource = getWebResource("admin/addprincipal");
+
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("principal", principal);
+        params.add("password", password);
+        String response = webResource.queryParams(params).post(String.class);
+        try {
+            System.out.println(new JSONObject(response).getString("msg"));
+        } catch (JSONException e) {
+            System.err.println("Errors occurred when getting the message from response."
+                + e.getMessage());
+        }
+    }
+
+    @Override
+    public void deletePrincipal(String principal) throws HasException {
+        WebResource webResource = getWebResource("admin/deleteprincipal");
+
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("principal", principal);
+        String response = webResource.queryParams(params).delete(String.class);
+        try {
+            System.out.println(new JSONObject(response).getString("msg"));
+        } catch (JSONException e) {
+            System.err.println("Errors occurred when getting the message from response."
+                + e.getMessage());
+        }
+    }
+
+    @Override
+    public void renamePrincipal(String oldPrincipal, String newPrincipal) throws HasException {
+        WebResource webResource = getWebResource("admin/renameprincipal");
+
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("oldprincipal", oldPrincipal);
+        params.add("newprincipal", newPrincipal);
+        String response = webResource.queryParams(params).post(String.class);
+        try {
+            System.out.println(new JSONObject(response).getString("msg"));
+        } catch (JSONException e) {
+            System.err.println(e.getMessage());
+        }
+    }
+
+    @Override
+    public List<String> getPrincipals() throws HasException {
+        WebResource webResource = getWebResource("admin/getprincipals");
+
+        String response = webResource.get(String.class);
+        String princs = null;
+        try {
+            princs = new JSONObject(response).getString("msg");
+        } catch (JSONException e) {
+            System.err.println("Errors occurred when getting the message from response."
+                + e.getMessage());
+        }
+        return getPrincsList(princs);
+    }
+
+    @Override
+    public List<String> getPrincipals(String exp) throws HasException {
+        WebResource webResource = getWebResource("admin/getprincipals");
+
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("exp", exp);
+        String response = webResource.queryParams(params).get(String.class);
+        return getPrincsList(response);
+    }
+
+    /**
+     * Create http connection to has server.
+     *
+     * @param url
+     * @param method
+     * @return connection
+     * @throws IOException
+     */
+    protected HttpURLConnection createConnection(URL url, String method) throws IOException {
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod(method);
+        if (method.equals("POST") || method.equals("PUT")) {
+            conn.setDoOutput(true);
+        }
+        return conn;
+    }
+
+    @Override
+    public String addPrincByRole(String host, String role) throws HasException {
+        //TODO
+        return null;
+    }
+
+    @Override
+    public String getHadminPrincipal() {
+        return KrbUtil.makeKadminPrincipal(hasConfig.getRealm()).getName();
+    }
+
+    /**
+     * get size of principal
+     */
+    @Override
+    public int size() throws HasException {
+        return this.getPrincipals().size();
+    }
+
+    public String getKrb5conf() {
+        WebResource webResource = getWebResource("getkrb5conf");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        if (response.getStatus() == 200) {
+            return response.getEntity(String.class);
+        }
+        return null;
+    }
+
+    public String getHasconf() {
+        WebResource webResource = getWebResource("gethasconf");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        if (response.getStatus() == 200) {
+            return response.getEntity(String.class);
+        }
+        return null;
+    }
+    public void setPlugin(String plugin) {
+        WebResource webResource = getWebResource("conf/setplugin");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("plugin", plugin);
+        ClientResponse response = webResource.queryParams(params).put(ClientResponse.class);
+        if (response.getStatus() == 200) {
+            System.out.println(response.getEntity(String.class));
+        } else if (response.getStatus() == 400) {
+            System.err.println(response.getEntity(String.class));
+        }
+    }
+    public void configKdc(String port, String realm, String host) {
+        WebResource webResource = getWebResource("conf/configkdc");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("port", port);
+        params.add("realm", realm);
+        params.add("host", host);
+        ClientResponse response = webResource.queryParams(params).put(ClientResponse.class);
+        if (response.getStatus() == 200) {
+            System.out.println(response.getEntity(String.class));
+        } else if (response.getStatus() == 400) {
+            System.err.println(response.getEntity(String.class));
+        }
+    }
+    public void configKdcBackend(String backendType, String dir, String url, String user,
+                                 String password) {
+        WebResource webResource = getWebResource("conf/configkdcbackend");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("backendType", backendType);
+        if (backendType.equals("json")) {
+            params.add("dir", dir);
+        } else if (backendType.equals("mysql")) {
+            params.add("url", url);
+            params.add("user", user);
+            params.add("password", password);
+        }
+        ClientResponse response = webResource.queryParams(params).put(ClientResponse.class);
+        if (response.getStatus() == 200) {
+            System.out.println(response.getEntity(String.class));
+        } else if (response.getStatus() == 400) {
+            System.err.println(response.getEntity(String.class));
+        }
+    }
+    public void startKdc() {
+        WebResource webResource = getWebResource("kdcstart");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        try {
+            JSONObject result = new JSONObject(response.getEntity(String.class));
+            if (result.getString("result").equals("success")) {
+                System.out.println(result.getString("msg"));
+            } else {
+                System.err.println(result.getString("msg"));
+            }
+        } catch (JSONException e) {
+            System.err.println(e.getMessage());
+        }
+    }
+    public InputStream initKdc() {
+        WebResource webResource = getWebResource("kdcinit");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        if (response.getStatus() == 200) {
+            return response.getEntityInputStream();
+        }
+        return null;
+    }
+    public String getHostRoles() {
+        WebResource webResource = getWebResource("hostroles");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        if (response.getStatus() == 200) {
+            return response.getEntity(String.class);
+        }
+        return null;
+    }
+    public void setEnableOfConf(String isEnable) throws HasException {
+        WebResource webResource = getWebResource("admin/setconf");
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("isEnable", isEnable);
+        ClientResponse response = webResource.queryParams(params).put(ClientResponse.class);
+        if (response.getStatus() == 200) {
+            System.out.println(response.getEntity(String.class));
+        } else {
+            System.err.println(response.getEntity(String.class));
+        }
+    }
+
+    @Override
+    public void exportKeytab(File keytab, String principal) throws HasException {
+        WebResource webResource = getWebResource("admin/exportkeytab");
+
+        MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+        params.add("principal", principal);
+        ClientResponse response = webResource.queryParams(params).get(ClientResponse.class);
+        FileOutputStream fos;
+        try {
+            fos = new FileOutputStream(keytab);
+        } catch (FileNotFoundException e) {
+            throw new HasException("The keytab file: " + keytab + "not exist. " + e);
+        }
+        InputStream in = response.getEntityInputStream();
+        byte[] buffer = new byte[4 * 1024];
+        int read;
+        try {
+            while ((read = in.read(buffer)) > 0) {
+                fos.write(buffer, 0, read);
+            }
+            fos.close();
+            in.close();
+        } catch (IOException e) {
+            System.err.println("Errors occurred when writing the buffer to keytab file." + e.toString());
+        }
+        System.out.println("Accept keytab file \"" + keytab.getName() + "\" from server successfully.");
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile, List<String> principals) throws HasException {
+        WebResource webResource = getWebResource("admin/exportkeytab");
+        for (String principal: principals) {
+            MultivaluedMap<String, String> params = new MultivaluedMapImpl();
+            params.add("principal", principal);
+            ClientResponse response = webResource.queryParams(params).get(ClientResponse.class);
+            FileOutputStream fos;
+            try {
+                fos = new FileOutputStream(keytabFile);
+            } catch (FileNotFoundException e) {
+                throw new HasException("The keytab file: " + keytabFile.getName() + "not exist. " + e);
+            }
+            InputStream in = response.getEntityInputStream();
+            byte[] buffer = new byte[4 * 1024];
+            int read;
+            try {
+                while ((read = in.read(buffer)) > 0) {
+                    fos.write(buffer, 0, read);
+                }
+                fos.close();
+                in.close();
+            } catch (IOException e) {
+                LOG.error("Errors occurred when writing the buffer to keytab file." + e.toString());
+            }
+        }
+        System.out.println("Accept keytab file \"" + keytabFile.getName() + "\" from server successfully.");
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a8b1c28f/has/has-client/src/main/java/org/apache/kerby/has/client/HasAuthAdminClient.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/kerby/has/client/HasAuthAdminClient.java b/has/has-client/src/main/java/org/apache/kerby/has/client/HasAuthAdminClient.java
new file mode 100644
index 0000000..d8523f0
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/kerby/has/client/HasAuthAdminClient.java
@@ -0,0 +1,553 @@
+/**
+ * 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.client;
+
+import org.apache.kerby.has.common.HasConfig;
+import org.apache.kerby.has.common.HasException;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HasAuthAdminClient extends HasAdminClient {
+    public static final Logger LOG = LoggerFactory.getLogger(HasAuthAdminClient.class);
+
+    /**
+     * Create an instance of the HasAuthAdminClient.
+     *
+     * @param hasConfig the has config
+     */
+    public HasAuthAdminClient(HasConfig hasConfig) {
+        super(hasConfig);
+    }
+
+    /**
+     * Create an authenticated connection to the Has server.
+     * <p>
+     * It uses Hadoop-auth client authentication which by default supports
+     * Kerberos HTTP SPNEGO, Pseudo/Simple and anonymous.
+     *
+     * @param url    the URL to open a HTTP connection to.
+     * @param method the HTTP method for the HTTP connection.
+     * @return an authenticated connection to the has server.
+     * @throws IOException if an IO error occurred.
+     */
+    @Override
+    protected HttpURLConnection createConnection(URL url, String method) {
+        HttpURLConnection conn = null;
+        if ((getHasConfig().getHttpsPort() != null) && (getHasConfig().getHttpsHost() != null)) {
+            try {
+                conn = super.getHttpsConnection(url, true);
+            } catch (Exception e) {
+                System.err.println(e.getMessage());
+            }
+        }
+        if (method.equals("POST") || method.equals("PUT")) {
+            conn.setDoOutput(true);
+        }
+        return conn;
+    }
+
+    private String getBaseURL() {
+        String url = null;
+        if ((getHasConfig().getHttpsPort() != null) && (getHasConfig().getHttpsHost() != null)) {
+            url = "https://" + getHasConfig().getHttpsHost() + ":" + getHasConfig().getHttpsPort()
+                + "/has/v1/admin/";
+        }
+        if (url == null) {
+            throw new RuntimeException("Please set the https address and port.");
+        }
+        return url;
+    }
+
+    public void addPrincipal(String principal) throws HasException {
+        HttpURLConnection httpConn;
+
+        URL url;
+        try {
+            url = new URL(getBaseURL() + "addprincipal?principal=" + principal);
+        } catch (MalformedURLException e) {
+            throw new HasException(e);
+        }
+
+        httpConn = createConnection(url, "POST");
+
+        httpConn.setRequestProperty("Content-Type",
+            "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("POST");
+        } catch (ProtocolException e) {
+            LOG.error("Fail to add principal. " + e);
+            throw new HasException(e);
+        }
+        try {
+            httpConn.setDoOutput(true);
+            httpConn.setDoInput(true);
+            httpConn.connect();
+
+            if (httpConn.getResponseCode() == 200) {
+                System.out.println(getResponse(httpConn));
+            } else {
+                throw new HasException("Fail to add principal \"" + principal + "\".");
+            }
+        } catch (Exception e) {
+            LOG.error("Fail to add principal. " + e);
+            throw new HasException(e);
+        }
+    }
+
+    public void setEnableOfConf(String isEnable) throws HasException {
+        HttpURLConnection httpConn;
+
+        URL url;
+        try {
+            url = new URL(getBaseURL() + "setconf?isEnable=" + isEnable);
+        } catch (MalformedURLException e) {
+            throw new HasException(e);
+        }
+
+        httpConn = createConnection(url, "PUT");
+
+        httpConn.setRequestProperty("Content-Type",
+                "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("PUT");
+        } catch (ProtocolException e) {
+            throw new HasException(e);
+        }
+        try {
+            httpConn.setDoOutput(true);
+            httpConn.setDoInput(true);
+            httpConn.connect();
+            InputStream inputStream = httpConn.getResponseCode() == 200
+                    ? httpConn.getInputStream() : httpConn.getErrorStream();
+            BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(inputStream));
+            String s;
+            StringBuilder result = new StringBuilder();
+            while ((s = reader.readLine()) != null) {
+                result.append(s);
+            }
+            if (httpConn.getResponseCode() == 200) {
+                System.out.println(result);
+            } else {
+                System.err.println(result);
+            }
+        } catch (Exception e) {
+            LOG.error("Fail to connect to server. " + e);
+            throw new HasException(e);
+        }
+    }
+
+    /**
+     * Change principals JSON string to a List.
+     *
+     * @param princs principals JSON string which like
+     *               "["HTTP\/host1@HADOOP.COM","HTTP\/host2@HADOOP.COM"]"
+     * @return principalLists.
+     */
+    private List<String> getPrincsList(String princs) {
+        List<String> principalLists = new ArrayList<>();
+        try {
+            JSONArray principals = new JSONArray(princs);
+            for (int i = 0; i < principals.length(); i++) {
+                principalLists.add("\t" + principals.getString(i));
+            }
+        } catch (Exception e) {
+            System.err.println(e.getMessage());
+        }
+        return principalLists;
+    }
+
+    @Override
+    public void requestCreatePrincipals(String hostRoles) throws HasException {
+        HttpURLConnection httpConn;
+
+        URL url;
+        try {
+            url = new URL(getBaseURL() + "createprincipals");
+        } catch (MalformedURLException e) {
+            throw new HasException(e);
+        }
+
+        httpConn = createConnection(url, "POST");
+
+        httpConn.setRequestProperty("Content-Type",
+                "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("PUT");
+        } catch (ProtocolException e) {
+            throw new HasException(e);
+        }
+        httpConn.setDoOutput(true);
+        httpConn.setDoInput(true);
+        try {
+            httpConn.connect();
+            OutputStream out = httpConn.getOutputStream();
+            out.write(hostRoles.toString().getBytes());
+            out.flush();
+            out.close();
+            if (httpConn.getResponseCode() == 200) {
+                System.out.println(getResponse(httpConn));
+            } else {
+                throw new HasException("Connection deined.");
+            }
+        } catch (Exception e) {
+            throw new HasException(e);
+        }
+    }
+
+    @Override
+    public File getKeytabByHostAndRole(String host, String role) throws HasException {
+        String keytabName = host + ".zip";
+        HttpURLConnection httpConn;
+        String request = getBaseURL() + "exportkeytabs?host=" + host;
+        if (!role.equals("")) {
+            request = request + "&role=" + role;
+            keytabName = role + "-" + host + ".keytab";
+        }
+
+        URL url;
+        try {
+            url = new URL(request);
+        } catch (MalformedURLException e) {
+            throw new HasException(e);
+        }
+
+        httpConn = createConnection(url, "GET");
+
+        httpConn.setRequestProperty("Content-Type",
+            "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("GET");
+        } catch (ProtocolException e) {
+            throw new HasException(e);
+        }
+        httpConn.setDoOutput(true);
+        httpConn.setDoInput(true);
+        try {
+            httpConn.connect();
+
+            if (httpConn.getResponseCode() != 200) {
+                System.err.println("Error : connection denied.");
+                return null;
+            }
+            FileOutputStream fos = new FileOutputStream(new File(keytabName));
+            InputStream in = httpConn.getInputStream();
+            byte[] buffer = new byte[4 * 1024];
+            int read;
+            while ((read = in.read(buffer)) > 0) {
+                fos.write(buffer, 0, read);
+            }
+            fos.close();
+            in.close();
+        } catch (IOException e) {
+            throw new HasException(e);
+        }
+        System.out.println("Accept keytab file \"" + keytabName + "\" from server.");
+
+        return new File(keytabName);
+    }
+
+    @Override
+    public void exportKeytab(File keytab, String principal) throws HasException {
+        URL url = null;
+        try {
+            url = new URL(getBaseURL() + "exportkeytab?principal=" + principal);
+        } catch (MalformedURLException e) {
+            LOG.error("Fail to get url. " + e);
+            throw new HasException("Fail to get url.", e);
+        }
+
+        HttpURLConnection httpConn = createConnection(url, "GET");
+        httpConn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("GET");
+        } catch (ProtocolException e) {
+            throw new HasException(e);
+        }
+        httpConn.setDoOutput(true);
+        httpConn.setDoInput(true);
+        try {
+            httpConn.connect();
+            if (httpConn.getResponseCode() != 200) {
+                System.err.println("Error: connection denied.");
+            }
+            FileOutputStream fos = new FileOutputStream(keytab);
+            InputStream in = httpConn.getInputStream();
+            byte[] buffer = new byte[3 * 1024];
+            int read;
+            while ((read = in.read(buffer)) > 0) {
+                fos.write(buffer, 0, read);
+            }
+            fos.close();
+            in.close();
+        } catch (IOException e) {
+            throw new HasException(e);
+        }
+        System.out.println("Receive keytab file \"" + keytab.getName() + "\" from server successfully.");
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile, List<String> principals) throws HasException {
+        HttpURLConnection httpConn;
+        for (String principal: principals) {
+            String request = getBaseURL() + "exportkeytab?principal=" + principal;
+            URL url;
+            try {
+                url = new URL(request);
+            } catch (MalformedURLException e) {
+                throw new HasException(e);
+            }
+            httpConn = createConnection(url, "GET");
+            httpConn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+            try {
+                httpConn.setRequestMethod("GET");
+            } catch (ProtocolException e) {
+                throw new HasException(e);
+            }
+            httpConn.setDoOutput(true);
+            httpConn.setDoInput(true);
+            try {
+                httpConn.connect();
+                if (httpConn.getResponseCode() != 200) {
+                    System.err.println("Error: connection denied.");
+                }
+                FileOutputStream fos = new FileOutputStream(keytabFile);
+                InputStream in = httpConn.getInputStream();
+                byte[] buffer = new byte[4 * 1024];
+                int read;
+                while ((read = in.read(buffer)) > 0) {
+                    fos.write(buffer, 0, read);
+                }
+                fos.close();
+                in.close();
+            } catch (IOException e) {
+                throw new HasException(e);
+            }
+        }
+        System.out.println("Accept keytab file \"" + keytabFile.getName() + "\" from server.");
+    }
+
+    @Override
+    public void addPrincipal(String principal, String password) throws HasException {
+        HttpURLConnection httpConn;
+
+        URL url = null;
+        try {
+            url = new URL(getBaseURL() + "addprincipal?principal=" + principal
+                            + "&password=" + password);
+        } catch (MalformedURLException e) {
+            throw new HasException("Fail to get url.", e);
+        }
+
+        httpConn = createConnection(url, "POST");
+
+        httpConn.setRequestProperty("Content-Type",
+                "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("POST");
+        } catch (ProtocolException e) {
+            throw new HasException(e);
+        }
+        try {
+            httpConn.setDoOutput(true);
+            httpConn.setDoInput(true);
+            httpConn.connect();
+
+            if (httpConn.getResponseCode() == 200) {
+                System.out.println(getResponse(httpConn));
+            } else {
+                throw new HasException("Fail to add principal \"" + principal + "\".");
+            }
+        } catch (Exception e) {
+            throw new HasException(e);
+        }
+    }
+
+    @Override
+    public void deletePrincipal(String principal) throws HasException {
+        HttpURLConnection httpConn;
+
+        URL url;
+        try {
+            url = new URL(getBaseURL() + "deleteprincipal?principal=" + principal);
+        } catch (MalformedURLException e) {
+            throw new HasException(e);
+        }
+
+        httpConn = createConnection(url, "DELETE");
+
+        httpConn.setRequestProperty("Content-Type",
+                "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("DELETE");
+        } catch (ProtocolException e) {
+            throw new HasException(e);
+        }
+        try {
+            httpConn.setDoOutput(true);
+            httpConn.setDoInput(true);
+            httpConn.connect();
+
+            if (httpConn.getResponseCode() == 200) {
+                System.out.println(getResponse(httpConn));
+            } else {
+                throw new HasException("Connection deined.");
+            }
+        } catch (Exception e) {
+            throw new HasException(e);
+        }
+    }
+
+    @Override
+    public void renamePrincipal(String oldPrincipal, String newPrincipal) throws HasException {
+        HttpURLConnection httpConn;
+
+        URL url;
+        try {
+            url = new URL(getBaseURL() + "renameprincipal?oldprincipal=" + oldPrincipal
+                            + "&newprincipal=" + newPrincipal);
+        } catch (MalformedURLException e) {
+            throw new HasException(e);
+        }
+
+        httpConn = createConnection(url, "POST");
+
+        httpConn.setRequestProperty("Content-Type",
+                "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("POST");
+        } catch (ProtocolException e) {
+            throw new HasException(e);
+        }
+        try {
+            httpConn.setDoOutput(true);
+            httpConn.setDoInput(true);
+            httpConn.connect();
+
+            if (httpConn.getResponseCode() == 200) {
+                System.out.println(getResponse(httpConn));
+            } else {
+                throw new HasException("Connection to renameprincipal deined.");
+            }
+        } catch (Exception e) {
+            throw new HasException(e);
+        }
+    }
+
+    @Override
+    public List<String> getPrincipals() throws HasException {
+        HttpURLConnection httpConn;
+
+        URL url;
+        try {
+            url = new URL(getBaseURL() + "getprincipals");
+        } catch (MalformedURLException e) {
+            System.err.println(e.getMessage());
+            throw new HasException(e);
+        }
+
+        httpConn = createConnection(url, "GET");
+
+        httpConn.setRequestProperty("Content-Type",
+                "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("GET");
+        } catch (ProtocolException e) {
+            throw new HasException(e);
+        }
+        String response;
+        try {
+            httpConn.setDoInput(true);
+            httpConn.connect();
+
+            if (httpConn.getResponseCode() == 200) {
+                response = getResponse(httpConn);
+            } else {
+                throw new HasException("Connection to getprincipals deined.");
+            }
+        } catch (Exception e) {
+            LOG.error("Fail to get principals." + e);
+            throw new HasException("Fail to get principals.", e);
+        }
+        return getPrincsList(response);
+    }
+
+    @Override
+    public List<String> getPrincipals(String exp) throws HasException {
+        HttpURLConnection httpConn;
+
+        URL url;
+        try {
+            url = new URL(getBaseURL() + "getprincipals?exp=" + exp);
+        } catch (MalformedURLException e) {
+            throw new HasException(e);
+        }
+
+        httpConn = createConnection(url, "GET");
+
+        httpConn.setRequestProperty("Content-Type",
+                "application/json; charset=UTF-8");
+        try {
+            httpConn.setRequestMethod("GET");
+        } catch (ProtocolException e) {
+            LOG.error("Fail to get the principals with expression. " + e);
+            throw new HasException("Fail to get the principals with expression.", e);
+        }
+        String response;
+        try {
+            httpConn.setDoOutput(true);
+            httpConn.setDoInput(true);
+            httpConn.connect();
+
+            if (httpConn.getResponseCode() == 200) {
+                response = getResponse(httpConn);
+            } else {
+                throw new HasException("Connection to getprincipals deined.");
+            }
+        } catch (Exception e) {
+            throw new HasException(e);
+        }
+        return getPrincsList(response);
+    }
+
+    private String getResponse(HttpURLConnection httpConn) throws Exception {
+        StringBuilder data = new StringBuilder();
+        BufferedReader br = new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
+        String s;
+        while ((s = br.readLine()) != null) {
+            data.append(s);
+        }
+        return new JSONObject(data.toString()).getString("msg");
+    }
+}