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/15 05:12:14 UTC

[09/10] directory-kerby git commit: Add the HAS project to Kerby.

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-client/src/main/java/org/apache/hadoop/has/client/HasAdminClient.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasAdminClient.java b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasAdminClient.java
new file mode 100644
index 0000000..3f5e3fa
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/hadoop/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.hadoop.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.hadoop.has.common.HasAdmin;
+import org.apache.hadoop.has.common.HasConfig;
+import org.apache.hadoop.has.common.HasException;
+import org.apache.hadoop.has.common.ssl.SSLFactory;
+import org.apache.hadoop.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/be580566/has/has-client/src/main/java/org/apache/hadoop/has/client/HasAuthAdminClient.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasAuthAdminClient.java b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasAuthAdminClient.java
new file mode 100644
index 0000000..d7e3f5a
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/hadoop/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.hadoop.has.client;
+
+import org.apache.hadoop.has.common.HasConfig;
+import org.apache.hadoop.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");
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClient.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClient.java b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClient.java
new file mode 100755
index 0000000..5f612d3
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClient.java
@@ -0,0 +1,677 @@
+/**
+ * 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.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.text.CharacterPredicates;
+import org.apache.commons.text.RandomStringGenerator;
+import org.apache.hadoop.has.common.HasConfig;
+import org.apache.hadoop.has.common.HasConfigKey;
+import org.apache.hadoop.has.common.HasException;
+import org.apache.hadoop.has.common.ssl.SSLFactory;
+import org.apache.hadoop.has.common.util.HasUtil;
+import org.apache.hadoop.has.common.util.URLConnectionFactory;
+import org.apache.kerby.kerberos.kerb.KrbCodec;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.KrbRuntime;
+import org.apache.kerby.kerberos.kerb.crypto.EncryptionHandler;
+import org.apache.kerby.kerberos.kerb.provider.TokenEncoder;
+import org.apache.kerby.kerberos.kerb.type.base.AuthToken;
+import org.apache.kerby.kerberos.kerb.type.base.EncryptedData;
+import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
+import org.apache.kerby.kerberos.kerb.type.base.KeyUsage;
+import org.apache.kerby.kerberos.kerb.type.base.KrbError;
+import org.apache.kerby.kerberos.kerb.type.base.KrbMessage;
+import org.apache.kerby.kerberos.kerb.type.base.KrbMessageType;
+import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
+import org.apache.kerby.kerberos.kerb.type.kdc.EncAsRepPart;
+import org.apache.kerby.kerberos.kerb.type.kdc.EncKdcRepPart;
+import org.apache.kerby.kerberos.kerb.type.kdc.KdcRep;
+import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
+import org.apache.kerby.util.IOUtil;
+import org.codehaus.jettison.json.JSONException;
+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.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+/**
+ * HAS client
+ */
+public class HasClient {
+
+    public static final Logger LOG = LoggerFactory.getLogger(HasClient.class);
+
+    public static final String JAVA_SECURITY_KRB5_CONF = "java.security.krb5.conf";
+    public static final String HAS_HTTP_PORT_DEFAULT = "9870";
+    public static final String HAS_CONFIG_DEFAULT = "/etc/has/has-client.conf";
+    public static final String CA_ROOT_DEFAULT = "/etc/has/ca-root.pem";
+
+    private String hadoopSecurityHas = null;
+    private String type;
+    private File clientConfigFolder;
+
+
+    public HasClient() { }
+
+    /**
+     * Create an instance of the HasClient.
+     *
+     * @param hadoopSecurityHas the has config
+     */
+    public HasClient(String hadoopSecurityHas) {
+        this.hadoopSecurityHas = hadoopSecurityHas;
+    }
+
+
+    public TgtTicket requestTgt() throws HasException {
+        HasConfig config;
+        if (hadoopSecurityHas == null) {
+            String hasClientConf = System.getenv("HAS_CLIENT_CONF");
+            if (hasClientConf == null) {
+                hasClientConf = HAS_CONFIG_DEFAULT;
+            }
+            LOG.debug("has-client conf path: " + hasClientConf);
+            File confFile = new File(hasClientConf);
+            if (!confFile.exists()) {
+                throw new HasException("The HAS client config file: " + hasClientConf
+                    + " does not exist.");
+            }
+            try {
+                config = HasUtil.getHasConfig(confFile);
+            } catch (HasException e) {
+                LOG.error("Failed to get has client config: " + e.getMessage());
+                throw new HasException("Failed to get has client config: " + e);
+            }
+        } else {
+            config = new HasConfig();
+            String[] urls = hadoopSecurityHas.split(";");
+            String host = "";
+            int port = 0;
+            try {
+                for (String url : urls) {
+                    URI uri = new URI(url.trim());
+
+                    // parse host
+                    host = host + uri.getHost() + ",";
+
+                    // parse port
+                    if (port == 0) {
+                        port = uri.getPort();
+                    } else {
+                        if (port != uri.getPort()) {
+                            throw new HasException("Invalid port: not even.");
+                        }
+                    }
+
+                    // We will get the auth type from env first
+                    type = System.getenv("auth_type");
+                    // parse host
+                    if (type == null) {
+                        String[] strs = uri.getQuery().split("=");
+                        if (strs[0].equals("auth_type")) {
+                            type = strs[1];
+                        } else {
+                            LOG.warn("No auth type in conf.");
+                        }
+                    }
+                }
+                if (host == null || port == 0) {
+                    throw new HasException("host is null.");
+                } else {
+                    host = host.substring(0, host.length() - 1);
+                    config.setString(HasConfigKey.HTTPS_HOST, host);
+                    config.setInt(HasConfigKey.HTTPS_PORT, port);
+                    config.setString(HasConfigKey.AUTH_TYPE, type);
+                }
+            } catch (URISyntaxException e) {
+                LOG.error("Errors occurred when getting web url. " + e.getMessage());
+                throw new HasException(
+                    "Errors occurred when getting web url. " + e.getMessage());
+            }
+        }
+        if (config == null) {
+            throw new HasException("Failed to get HAS client config.");
+        }
+        clientConfigFolder = new File("/etc/has/" + config.getHttpsHost());
+        if (!clientConfigFolder.exists()) {
+            clientConfigFolder.mkdirs();
+        }
+
+        // get and set ssl-client/trustStore first
+        String sslClientConfPath = clientConfigFolder + "/ssl-client.conf";
+        loadSslClientConf(config, sslClientConfPath);
+        config.setString(HasConfigKey.SSL_CLIENT_CONF, sslClientConfPath);
+
+        createKrb5Conf(config);
+
+        HasClientPlugin plugin;
+        try {
+            plugin = getClientTokenPlugin(config);
+        } catch (HasException e) {
+            LOG.error("Failed to get client token plugin from config: " + e.getMessage());
+            throw new HasException(
+                "Failed to get client token plugin from config: " + e.getMessage());
+        }
+        AuthToken authToken;
+        try {
+            authToken = plugin.login(config);
+        } catch (HasLoginException e) {
+            LOG.error("Plugin login failed: " + e.getMessage());
+            throw new HasException(
+                "Plugin login failed: " + e.getMessage());
+        }
+        type = plugin.getLoginType();
+
+        LOG.info("The plugin type is: " + type);
+
+        return requestTgt(authToken, type, config);
+    }
+
+    private void createKrb5Conf(HasConfig config) throws HasException {
+        HasAdminClient hasAdminClient = new HasAdminClient(config);
+        File krb5Conf = new File(clientConfigFolder + "/krb5.conf");
+        if (!krb5Conf.exists()) {
+            String content = hasAdminClient.getKrb5conf();
+            if (content == null) {
+                LOG.error("Failed to get krb5.conf.");
+                throw new HasException("Failed to get krb5.conf.");
+            }
+            try {
+                PrintStream ps = new PrintStream(new FileOutputStream(krb5Conf));
+                ps.println(content);
+                LOG.info("krb5.conf has saved in : " + krb5Conf.getAbsolutePath());
+            } catch (FileNotFoundException e) {
+                LOG.error(e.getMessage());
+                throw new HasException(e);
+            }
+        }
+        System.setProperty(JAVA_SECURITY_KRB5_CONF, krb5Conf.getAbsolutePath());
+    }
+
+
+    private HasClientPlugin getClientTokenPlugin(HasConfig config) throws HasException {
+        String pluginName = config.getPluginName();
+        LOG.info("The plugin name getting from config is: " + pluginName);
+        HasClientPlugin clientPlugin;
+        if (pluginName != null) {
+            clientPlugin = HasClientPluginRegistry.createPlugin(pluginName);
+        } else {
+            throw new HasException("Please set the plugin name in has client conf");
+        }
+        if (clientPlugin == null) {
+            throw new HasException("Failed to create client plugin: " + pluginName);
+        }
+        LOG.info("The plugin class is: " + clientPlugin);
+
+        return clientPlugin;
+    }
+
+    /**
+     * Request a TGT with user token, plugin type and has config.
+     * @param authToken
+     * @param type
+     * @param config
+     * @return TGT
+     * @throws HasException e
+     */
+    public TgtTicket requestTgt(AuthToken authToken, String type, HasConfig config)
+        throws HasException {
+        TokenEncoder tokenEncoder = KrbRuntime.getTokenProvider("JWT").createTokenEncoder();
+
+        String tokenString;
+        try {
+            tokenString = tokenEncoder.encodeAsString(authToken);
+        } catch (KrbException e) {
+            LOG.debug("Failed to decode the auth token.");
+            throw new HasException("Failed to decode the auth token." + e.getMessage());
+        }
+
+        JSONObject json = null;
+        int responseStatus = 0;
+        boolean success = false;
+        if ((config.getHttpsPort() != null) && (config.getHttpsHost() != null)) {
+            String sslClientConfPath = clientConfigFolder + "/ssl-client.conf";
+            config.setString(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY, "ALLOW_ALL");
+            config.setString(SSLFactory.SSL_CLIENT_CONF_KEY, sslClientConfPath);
+            config.setBoolean(SSLFactory.SSL_REQUIRE_CLIENT_CERT_KEY, false);
+
+            URLConnectionFactory connectionFactory = URLConnectionFactory
+                .newDefaultURLConnectionFactory(config);
+
+            URL url;
+            String[] hosts = config.getHttpsHost().split(",");
+            for (String host : hosts) {
+                try {
+                    url = new URL("https://" + host.trim() + ":" + config.getHttpsPort()
+                        + "/has/v1?type=" + type + "&authToken=" + tokenString);
+                } catch (MalformedURLException e) {
+                    LOG.warn("Failed to get url. " + e.toString());
+                    continue;
+                }
+                HttpURLConnection conn;
+                try {
+                    conn = (HttpURLConnection) connectionFactory.openConnection(url);
+                } catch (IOException e) {
+                    LOG.warn("Failed to open connection. " + e.toString());
+                    continue;
+                }
+
+                conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+                try {
+                    conn.setRequestMethod("PUT");
+                } catch (ProtocolException e) {
+                    LOG.warn("Failed to set request method. " + e.toString());
+                    continue;
+                }
+                conn.setDoOutput(true);
+                conn.setDoInput(true);
+
+                try {
+                    conn.connect();
+
+                    responseStatus = conn.getResponseCode();
+                    switch (responseStatus) {
+                        case 200:
+                        case 201:
+                            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+                            StringBuilder sb = new StringBuilder();
+                            String line;
+                            while ((line = br.readLine()) != null) {
+                                sb.append(line + "\n");
+                            }
+                            br.close();
+
+                            json = new JSONObject(sb.toString());
+                    }
+
+                } catch (IOException | JSONException e) {
+                    LOG.warn("ERROR! " + e.toString());
+                    continue;
+                }
+
+                if (responseStatus == 200 || responseStatus == 201) {
+                    success = true;
+                    break;
+                }
+            }
+            if (!success) {
+                throw new HasException("Failed : HTTP error code : "
+                    + responseStatus);
+            }
+        } else {
+            WebResource webResource;
+            Client client = Client.create();
+            String[] hosts = config.getHttpHost().split(",");
+            for (String host : hosts) {
+                webResource = client
+                    .resource("http://" + host.trim() + ":" + config.getHttpPort()
+                        + "/has/v1?type=" + type + "&authToken="
+                        + tokenString);
+                try {
+                    ClientResponse response = webResource.accept("application/json")
+                        .put(ClientResponse.class);
+
+                    if (response.getStatus() != 200) {
+                        LOG.warn("WARN! " + response.getEntity(String.class));
+                        responseStatus = response.getStatus();
+                        continue;
+                    }
+                    json = response.getEntity(JSONObject.class);
+                } catch (ClientHandlerException e) {
+                    LOG.warn("WARN! " + e.toString());
+                    continue;
+                }
+                success = true;
+                break;
+            }
+            if (!success) {
+                throw new HasException("Failed : HTTP error code : "
+                    + responseStatus);
+            }
+        }
+
+        LOG.debug("Return from Server .... \n");
+
+        try {
+            return handleResponse(json, (String) authToken.getAttributes().get("passPhrase"));
+        } catch (HasException e) {
+            LOG.debug("Failed to handle response when requesting tgt ticket in client."
+                + e.getMessage());
+            throw new HasException(e);
+        }
+    }
+
+    private File loadSslClientConf(HasConfig config, String sslClientConfPath) throws HasException {
+        File sslClientConf = new File(sslClientConfPath);
+        if (!sslClientConf.exists()) {
+            String httpHost = config.getHttpHost();
+            String httpPort = config.getHttpPort();
+            if (httpHost == null) {
+                LOG.info("Can't find the http host in config, the https host will be used.");
+                httpHost = config.getHttpsHost();
+            }
+            if (httpPort == null) {
+                LOG.info("Can't find the http port in config, the default http port will be used.");
+                httpPort = HAS_HTTP_PORT_DEFAULT;
+            }
+            X509Certificate certificate = getCertificate(httpHost, httpPort);
+            if (verifyCertificate(certificate)) {
+                String password = createTrustStore(config.getHttpsHost(), certificate);
+                createClientSSLConfig(password);
+            } else {
+                throw new HasException("The certificate from HAS server is invalid.");
+            }
+        }
+        return sslClientConf;
+    }
+
+    public KrbMessage getKrbMessage(JSONObject json) throws HasException {
+
+        LOG.debug("Starting to get the message from has server.");
+
+        try {
+            boolean success = json.getBoolean("success");
+            if (!success) {
+                throw new HasException("Failed: " + json.getString("krbMessage"));
+            }
+        } catch (JSONException e) {
+            LOG.debug("Failed to get message." + e);
+            throw new HasException("Failed to get message." + e);
+        }
+
+        String typeString;
+        try {
+            typeString = json.getString("type");
+        } catch (JSONException e) {
+            LOG.debug("Failed to get message." + e);
+            throw new HasException("Failed to get message." + e);
+        }
+
+        if (typeString != null && typeString.equals(type)) {
+            LOG.debug("The message type is " + type);
+            String krbMessageString = null;
+            try {
+                krbMessageString = json.getString("krbMessage");
+            } catch (JSONException e) {
+                LOG.debug("Failed to get the krbMessage. " + e);
+            }
+            Base64 base64 = new Base64(0);
+            byte[] krbMessage = base64.decode(krbMessageString);
+            ByteBuffer byteBuffer = ByteBuffer.wrap(krbMessage);
+            KrbMessage kdcRep;
+            try {
+                kdcRep = KrbCodec.decodeMessage(byteBuffer);
+            } catch (IOException e) {
+                throw new HasException("Krb decoding message failed", e);
+            }
+            return kdcRep;
+        } else {
+            throw new HasException("Can't get the right message from server.");
+        }
+    }
+
+    public TgtTicket handleResponse(JSONObject json, String passPhrase)
+        throws HasException {
+        KrbMessage kdcRep = getKrbMessage(json);
+
+        KrbMessageType messageType = kdcRep.getMsgType();
+        if (messageType == KrbMessageType.AS_REP) {
+            return processResponse((KdcRep) kdcRep, passPhrase);
+        } else if (messageType == KrbMessageType.KRB_ERROR) {
+            KrbError error = (KrbError) kdcRep;
+            LOG.error("KDC server response with message: "
+                + error.getErrorCode().getMessage());
+
+            throw new HasException(error.getEtext());
+        }
+        return null;
+    }
+
+    public TgtTicket processResponse(KdcRep kdcRep, String passPhrase)
+        throws HasException {
+
+        PrincipalName clientPrincipal = kdcRep.getCname();
+        String clientRealm = kdcRep.getCrealm();
+        clientPrincipal.setRealm(clientRealm);
+
+        // Get the client to decrypt the EncryptedData
+        EncryptionKey clientKey = null;
+        try {
+            clientKey = HasUtil.getClientKey(clientPrincipal.getName(),
+                passPhrase,
+                kdcRep.getEncryptedEncPart().getEType());
+        } catch (KrbException e) {
+            throw new HasException("Could not generate key. " + e.getMessage());
+        }
+
+        byte[] decryptedData = decryptWithClientKey(kdcRep.getEncryptedEncPart(),
+            KeyUsage.AS_REP_ENCPART, clientKey);
+        if ((decryptedData[0] & 0x1f) == 26) {
+            decryptedData[0] = (byte) (decryptedData[0] - 1);
+        }
+        EncKdcRepPart encKdcRepPart = new EncAsRepPart();
+        try {
+            encKdcRepPart.decode(decryptedData);
+        } catch (IOException e) {
+            throw new HasException("Failed to decode EncAsRepPart", e);
+        }
+        kdcRep.setEncPart(encKdcRepPart);
+
+//        if (getChosenNonce() != encKdcRepPart.getNonce()) {
+//            throw new KrbException("Nonce didn't match");
+//        }
+
+//        PrincipalName returnedServerPrincipal = encKdcRepPart.getSname();
+//        returnedServerPrincipal.setRealm(encKdcRepPart.getSrealm());
+//        PrincipalName requestedServerPrincipal = getServerPrincipal();
+//        if (requestedServerPrincipal.getRealm() == null) {
+//            requestedServerPrincipal.setRealm(getContext().getKrbSetting().getKdcRealm());
+//        }
+//        if (!returnedServerPrincipal.equals(requestedServerPrincipal)) {
+//            throw new KrbException(KrbErrorCode.KDC_ERR_SERVER_NOMATCH);
+//        }
+
+//        HostAddresses hostAddresses = getHostAddresses();
+//        if (hostAddresses != null) {
+//            List<HostAddress> requestHosts = hostAddresses.getElements();
+//            if (!requestHosts.isEmpty()) {
+//                List<HostAddress> responseHosts = encKdcRepPart.getCaddr().getElements();
+//                for (HostAddress h : requestHosts) {
+//                    if (!responseHosts.contains(h)) {
+//                        throw new KrbException("Unexpected client host");
+//                    }
+//                }
+//            }
+//        }
+
+        TgtTicket tgtTicket = getTicket(kdcRep);
+        LOG.info("Ticket expire time: " + tgtTicket.getEncKdcRepPart().getEndTime());
+        return tgtTicket;
+
+    }
+
+    protected byte[] decryptWithClientKey(EncryptedData data,
+                                          KeyUsage usage,
+                                          EncryptionKey clientKey) throws HasException {
+        if (clientKey == null) {
+            throw new HasException("Client key isn't available");
+        }
+        try {
+            return EncryptionHandler.decrypt(data, clientKey, usage);
+        } catch (KrbException e) {
+            throw new HasException("Errors occurred when decrypting the data." + e.getMessage());
+        }
+    }
+
+    /**
+     * Get the tgt ticket from KdcRep
+     *
+     * @param kdcRep
+     */
+    public TgtTicket getTicket(KdcRep kdcRep) {
+        TgtTicket tgtTicket = new TgtTicket(kdcRep.getTicket(),
+            (EncAsRepPart) kdcRep.getEncPart(), kdcRep.getCname());
+        return tgtTicket;
+    }
+
+    /**
+     * Get certificate from HAS server.
+     *
+     */
+    private X509Certificate getCertificate(String host, String port) throws HasException {
+        X509Certificate certificate;
+        Client client = Client.create();
+        WebResource webResource = client.resource("http://" + host + ":" + port + "/has/v1/getcert");
+        ClientResponse response = webResource.get(ClientResponse.class);
+        if (response.getStatus() != 200) {
+            throw new HasException(response.getEntity(String.class));
+        }
+        try {
+            CertificateFactory factory = CertificateFactory.getInstance("X.509");
+            InputStream in = response.getEntityInputStream();
+            certificate = (X509Certificate) factory.generateCertificate(in);
+        } catch (CertificateException e) {
+            throw new HasException("Failed to get certificate from HAS server", e);
+        }
+
+        return certificate;
+    }
+
+    /**
+     * Verify certificate.
+     */
+    private boolean verifyCertificate(X509Certificate certificate) throws HasException {
+        // Check if certificate is expired
+        try {
+            Date date = new Date();
+            certificate.checkValidity(date);
+        } catch (GeneralSecurityException e) {
+            return false;
+        }
+
+        // Get certificate from ca root
+        X509Certificate caRoot;
+        try {
+            //Get the ca root path from env, client should export it.
+            String caRootPath = System.getenv("CA_ROOT");
+            if (caRootPath == null) {
+                caRootPath = CA_ROOT_DEFAULT;
+            }
+            File caRootFile;
+            if (caRootPath != null) {
+                caRootFile = new File(caRootPath);
+                if (!caRootFile.exists()) {
+                    throw new HasException("CA_ROOT: " + caRootPath + " not exist.");
+                }
+            } else {
+                throw new HasException("Please set the CA_ROOT.");
+            }
+
+            CertificateFactory factory = CertificateFactory.getInstance("X.509");
+            FileInputStream in = new FileInputStream(caRootFile);
+            caRoot = (X509Certificate) factory.generateCertificate(in);
+        } catch (CertificateException | FileNotFoundException e) {
+            throw new HasException("Failed to get certificate from ca root file", e);
+        }
+
+        // Verify certificate with root certificate
+        try {
+            PublicKey publicKey = caRoot.getPublicKey();
+            certificate.verify(publicKey);
+        } catch (GeneralSecurityException e) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Create and save truststore file based on certificate.
+     *
+     */
+    private String createTrustStore(String host, X509Certificate certificate) throws HasException {
+        KeyStore trustStore;
+
+        // Create password
+        RandomStringGenerator generator = new RandomStringGenerator.Builder()
+            .withinRange('a', 'z')
+            .filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
+            .build();
+        String password = generator.generate(15);
+
+        File trustStoreFile = new File(clientConfigFolder + "/truststore.jks");
+        try {
+            trustStore = KeyStore.getInstance("jks");
+            trustStore.load(null, null);
+            trustStore.setCertificateEntry(host, certificate);
+            FileOutputStream out = new FileOutputStream(trustStoreFile);
+            trustStore.store(out, password.toCharArray());
+            out.close();
+        } catch (IOException | GeneralSecurityException e) {
+            throw new HasException("Failed to create and save truststore file", e);
+        }
+        return password;
+    }
+
+    /**
+     * Create ssl configuration file for client.
+     *
+     */
+    private void createClientSSLConfig(String password) throws HasException {
+        String resourcePath = "/ssl-client.conf.template";
+        InputStream templateResource = getClass().getResourceAsStream(resourcePath);
+        try {
+            String content = IOUtil.readInput(templateResource);
+            content = content.replaceAll("_location_", clientConfigFolder.getAbsolutePath()
+                + "/truststore.jks");
+            content = content.replaceAll("_password_", password);
+
+            IOUtil.writeFile(content, new File(clientConfigFolder + "/ssl-client.conf"));
+        } catch (IOException e) {
+            throw new HasException("Failed to create client ssl configuration file", e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClientPlugin.java
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClientPlugin.java b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClientPlugin.java
new file mode 100644
index 0000000..4bd0749
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClientPlugin.java
@@ -0,0 +1,42 @@
+/**
+ * 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.HasConfig;
+import org.apache.kerby.kerberos.kerb.type.base.AuthToken;
+
+public interface HasClientPlugin {
+
+    /**
+     * Get the login module type ID, used to distinguish this module from others.
+     * Should correspond to the server side module.
+     *
+     * @return login type
+     */
+    String getLoginType();
+
+    /**
+     * Perform all the client side login logics, the results wrapped in an AuthToken,
+     * will be validated by HAS server.
+     *
+     * @param conf token plugin config
+     * @return user auth token
+     */
+    AuthToken login(HasConfig conf) throws HasLoginException;
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/be580566/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
new file mode 100644
index 0000000..0254ed6
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasClientPluginRegistry.java
@@ -0,0 +1,63 @@
+/**
+ * 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/be580566/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
new file mode 100644
index 0000000..c07eb59
--- /dev/null
+++ b/has/has-client/src/main/java/org/apache/hadoop/has/client/HasLoginException.java
@@ -0,0 +1,37 @@
+/**
+ * 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);
+    }
+
+}