ch.cyclops.gatekeeper.GKDriver.java Source code

Java tutorial

Introduction

Here is the source code for ch.cyclops.gatekeeper.GKDriver.java

Source

/*
 * Copyright (c) 2016. Zuercher Hochschule fuer Angewandte Wissenschaften
 *  All Rights Reserved.
 *
 *     Licensed 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.
 */

/*
 *     Author: Piyush Harsh,
 *     URL: piyush-harsh.info
 */
package ch.cyclops.gatekeeper;

import okhttp3.*;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.*;
import org.apache.log4j.pattern.IntegerPatternConverter;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;

public class GKDriver {
    private String gatekeeperUri;
    private int gatekeeperPort;
    private String adminUserId;
    private String adminPassword;
    private Logger driverLogger;
    private boolean internalStatus;
    private String adminToken;

    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    private int attempCount;

    /**
     * Constructor class, creates the object given the configuration file path.
     * <p>
     * @param confFile  Path to the driver configuration file
     * @param uid   gatekeeper user-id
     * @param pass  gatekeeper account password
     */
    public GKDriver(String confFile, int uid, String pass) {
        CompositeConfiguration config = new CompositeConfiguration();
        config.addConfiguration(new SystemConfiguration());
        try {
            config.addConfiguration(new PropertiesConfiguration(confFile));
            //now logger configuration is done, we can start using it.
            driverLogger = Logger.getLogger("gatekeeper-driver.Driver");
            gatekeeperUri = config.getProperty("gatekeeper.uri").toString();
            gatekeeperPort = Integer.parseInt(config.getProperty("gatekeeper.port").toString());
            adminUserId = Integer.toString(uid, 10);
            adminPassword = pass;
            internalStatus = true;
            adminToken = "";
            driverLogger.info("gatekeeper driver initialized properly.");
        } catch (Exception ex) {
            internalStatus = false;
            if (driverLogger != null)
                driverLogger.fatal("Error initializing driver: " + ex.getMessage());
        }
    }

    /**
     * This function gets the list of registered users with Gatekeeper.
     * <p>
     * @param attemptCount int value that allows internal attempts to fulfill the request. Should be a value between 0 and 4
     * @return list of registered users as string ArrayList object, on error a null is returned
     * @throws Exception    an Exception is raised and must be caught or processed
     */
    public ArrayList<String> getUserList(int attemptCount) throws Exception {
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(JSON, "");
        ArrayList<String> result = null;
        attemptCount++;
        //this is an admin only call
        //try first with the available token, if fails then generate a new token
        if (adminToken.length() > 0) {
            Request request = new Request.Builder().url(gatekeeperUri + ":" + gatekeeperPort + "/admin/user/")
                    .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Token", adminToken).get()
                    .build();
            Response response = client.newCall(request).execute();

            driverLogger.info("List User::Response code: " + response.code());

            if (response.code() == 200) {
                ResponseBody rBody = response.body();
                JSONObject jsonObj = new JSONObject(rBody.string());
                JSONArray uArray = jsonObj.getJSONArray("userlist");
                JSONArray uidArray = jsonObj.getJSONArray("userids");
                driverLogger.info("Got user-list with : " + uArray.length() + " users.");
                result = new ArrayList<String>(uArray.length());
                for (int i = 0; i < uArray.length(); i++) {
                    result.add(i, uArray.getString(i) + "," + uidArray.getString(i));
                }
            } else {
                //something wrong with the token.
                driverLogger
                        .warn("Error probably with the admin-token. Remaking this call automatically. Attempt - "
                                + attemptCount);
                this.adminToken = "";
                if (attemptCount < 5) {
                    response.body().close();
                    return this.getUserList(attemptCount);
                } else {
                    response.body().close();
                    driverLogger.error("Retry limit reached! Failing gracefully.");
                }
            }
        } else {
            //generate a new token
            if (attemptCount < 5) {
                adminToken = generateToken(Integer.parseInt(adminUserId), adminPassword);
                if (adminToken == null)
                    adminToken = "";
                driverLogger.warn("AttemptCount: " + attemptCount);
                return this.getUserList(attemptCount); //call the function again now that token has been set
            }
        }
        return result;
    }

    /**
     * Allows simple authentication process.
     * <p>
     * @param userId    int value representing the user-id
     * @param password  string value representing user's password
     * @return  true if authentication is successfull, else false is returned.
     * @throws Exception
     */
    public boolean simpleAuthentication(int userId, String password) throws Exception {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder().url(gatekeeperUri + ":" + gatekeeperPort + "/auth/" + userId)
                .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Password", password).get().build();
        Response response = client.newCall(request).execute();

        driverLogger.info("Authentication Check::Response code: " + response.code());
        if (response.code() == 202) {
            response.body().close();
            return true;
        }
        response.body().close();
        return false;
    }

    /**
     * Method to generate a valid token given an userId and corresponding password.
     * <p>
     * @param userId    the user's id
     * @param password  the user's password
     * @return  the token as String, if authentication fails a null is returned.
     * @throws Exception
     */
    public String generateToken(int userId, String password) throws Exception {
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(JSON, "");

        //generate a new token
        Request request = new Request.Builder().url(gatekeeperUri + ":" + gatekeeperPort + "/token/")
                .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Password", password)
                .addHeader("X-Auth-Uid", Integer.toString(userId)).post(body).build();
        Response response = client.newCall(request).execute();
        driverLogger.info("Request to generate a new token, return code: " + response.code());
        if (response.code() == 200) {
            ResponseBody rBody = response.body();

            JSONObject jsonObj = new JSONObject(rBody.string());
            JSONObject temp = new JSONObject(jsonObj.get("token").toString());
            driverLogger.info("Received user-token: " + temp.get("id").toString());
            response.body().close();
            return temp.get("id").toString();
        } else {
            response.body().close();
            return null;
        }
    }

    /**
     * This method allows registration of a new user with Gatekeeper service. This is an admin-only call.
     * <p>
     * @param username  the desired username
     * @param password  the account password associated with this user
     * @param isAdmin   true if user is an admin-user, else false
     * @param accessList    comma separated list of resources/services this user has access to, use ALL to grant access to everything
     * @param attemptCount  integer value controlling the self execution iterations, should be a value between 0 and 4
     * @return  the user-id as integer if registration is successful, -1 if the process fails.
     * @throws Exception
     */
    public int registerUser(String username, String password, boolean isAdmin, String accessList, int attemptCount)
            throws Exception {
        OkHttpClient client = new OkHttpClient();
        JSONObject callBody = new JSONObject();
        callBody.put("username", username);
        callBody.put("password", password);
        if (isAdmin)
            callBody.put("isadmin", "y");
        else
            callBody.put("isadmin", "n");
        callBody.put("accesslist", accessList.trim());
        driverLogger.info("register-user call with json: " + callBody.toString());
        RequestBody body = RequestBody.create(JSON, callBody.toString());
        attemptCount++;

        //this is an admin only call
        //try first with the available token, if fails then generate a new token
        if (adminToken.length() > 0) {
            Request request = new Request.Builder().url(gatekeeperUri + ":" + gatekeeperPort + "/admin/user/")
                    .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Token", adminToken).post(body)
                    .build();

            Response response = client.newCall(request).execute();

            driverLogger.info("Register User::Response code: " + response.code());

            if (response.code() == 200) {
                ResponseBody rBody = response.body();
                JSONObject jsonObj = new JSONObject(rBody.string());
                JSONArray uinfoArray = jsonObj.getJSONArray("info");
                JSONObject temp = new JSONObject(uinfoArray.get(0).toString());
                driverLogger.info("Got user-id: " + temp.get("id"));
                response.body().close();
                return Integer.parseInt(temp.get("id").toString());
            }
            response.body().close();
        } else {
            //generate a new token
            if (attemptCount < 5) {
                adminToken = generateToken(Integer.parseInt(adminUserId), adminPassword);
                if (adminToken == null)
                    adminToken = "";
                if (attempCount < 5)
                    return this.registerUser(username, password, isAdmin, accessList, attemptCount); //call the function again now that token has been set
            }
        }
        return -1;
    }

    /**
     * This method allows an admin user to delete an user identified by user-id
     * <p>
     * @param userId        the user's id whose account is to be deleted from Gatekeeper
     * @param attemptCount  integer value controlling the self execution iterations, should be a value between 0 and 4
     * @return  true if the account was successfully deleted, else false
     * @throws Exception
     */
    public boolean deleteUser(int userId, int attemptCount) throws Exception {
        OkHttpClient client = new OkHttpClient();
        attemptCount++;

        //this is an admin only call
        //try first with the available token, if fails then generate a new token
        if (adminToken.length() > 0) {
            Request request = new Request.Builder()
                    .url(gatekeeperUri + ":" + gatekeeperPort + "/admin/user/" + userId)
                    .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Token", adminToken).delete()
                    .build();

            Response response = client.newCall(request).execute();

            driverLogger.info("Delete User::Response code: " + response.code());

            if (response.code() == 200) {
                response.body().close();
                return true;
            }
            response.body().close();
        } else {
            //generate a new token
            if (attemptCount < 5) {
                adminToken = generateToken(Integer.parseInt(adminUserId), adminPassword);
                if (adminToken == null)
                    adminToken = "";
                if (attempCount < 5)
                    return this.deleteUser(userId, attemptCount); //call the function again now that token has been set
            }
        }
        return false;
    }

    /**
     * This method allows an admin user to check if another user is admin or not
     * <p>
     * @param userId        the user's id whose account is to be checked as admin from Gatekeeper
     * @param attemptCount  integer value controlling the self execution iterations, should be a value between 0 and 4
     * @return  true if the account is an admin, else false
     * @throws Exception
     */
    public boolean isAdmin(int userId, int attemptCount) throws Exception {
        OkHttpClient client = new OkHttpClient();
        attemptCount++;

        //this is an admin only call
        //try first with the available token, if fails then generate a new token
        if (adminToken.length() > 0) {
            Request request = new Request.Builder()
                    .url(gatekeeperUri + ":" + gatekeeperPort + "/admin/user/" + userId)
                    .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Token", adminToken).get()
                    .build();

            Response response = client.newCall(request).execute();

            driverLogger.info("Is Admin User::Response code: " + response.code());

            if (response.code() == 200) {
                ResponseBody rBody = response.body();
                JSONObject jsonObj = new JSONObject(rBody.string());
                JSONArray uinfoArray = jsonObj.getJSONArray("info");
                JSONObject temp = new JSONObject(uinfoArray.get(0).toString());
                String is_admin = temp.get("isadmin").toString();
                driverLogger.info("Got isAdmin flag: " + is_admin);
                response.body().close();
                return is_admin.equalsIgnoreCase("y");
            }
            response.body().close();
        } else {
            //generate a new token
            if (attemptCount < 5) {
                adminToken = generateToken(Integer.parseInt(adminUserId), adminPassword);
                if (adminToken == null)
                    adminToken = "";
                if (attempCount < 5)
                    return this.isAdmin(userId, attemptCount); //call the function again now that token has been set
            }
        }
        return false;
    }

    /**
     * This method allows validation of a token against an user-id, if token is valid and belongs to a claimed user the response is true, else false.
     * <p>
     * @param token     The token which needs to be validated
     * @param userId    user-id to be validated with
     * @return  true if the validation is successful, else a false is returned
     * @throws Exception
     */
    public boolean validateToken(String token, int userId) throws Exception {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url(gatekeeperUri + ":" + gatekeeperPort + "/token/validate/" + token)
                .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Uid", Integer.toString(userId)).get()
                .build();
        Response response = client.newCall(request).execute();

        driverLogger.info("Token Validation::Response code: " + response.code());
        if (response.code() == 200) {
            response.body().close();
            return true;
        }
        response.body().close();
        return false;
    }

    /**
     * This method check the token for authorization to use a particular service, if the token is valid, and the user
     * associated with this token has access rights to the service - the respose is true, otherwise a false is returned.
     * <p>
     * @param token     The token which needs to be validated
     * @param serviceKey    The service-key of the service which is requesting the validation of this token
     * @return  true if the user associated with token has access to this service provided the token is valid, false otherwise.
     * @throws Exception
     */
    public boolean validateToken(String token, String serviceKey) throws Exception {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url(gatekeeperUri + ":" + gatekeeperPort + "/token/validate/" + token)
                .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Service-Key", serviceKey).get()
                .build();
        Response response = client.newCall(request).execute();

        driverLogger.info("Token Validation by service::Response code: " + response.code());
        if (response.code() == 200) {
            response.body().close();
            return true;
        }
        response.body().close();
        return false;
    }

    /**
     * This method allows the user to update their own password.
     * <p>
     * @param token     The token which needs to be validated
     * @param userId    The userId of the account
     * @return  true if the password was updated successfully, false otherwise
     * @throws Exception
     */
    public boolean updatePassword(String userId, String password, String token) throws Exception {
        OkHttpClient client = new OkHttpClient();
        JSONObject callBody = new JSONObject();
        callBody.put("password", password);
        driverLogger.info("update password call with json: " + callBody.toString());
        RequestBody body = RequestBody.create(JSON, callBody.toString());

        Request request = new Request.Builder().url(gatekeeperUri + ":" + gatekeeperPort + "/password/" + userId)
                .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Token", token).put(body).build();
        Response response = client.newCall(request).execute();

        driverLogger.info("Password update service::Response code: " + response.code());
        if (response.code() == 200) {
            response.body().close();
            return true;
        }
        response.body().close();
        return false;
    }

    /**
     * This admin only method allows admins to get the list of registered services with Gatekeeper. The return array
     * contains service-short-name,service-key as its elements.
     * <p>
     * @param attemptCount integer value controlling the self execution iterations, should be a value between 0 and 4
     * @return list of registered services as string ArrayList object, each element as name,key,id tuple, on error a null is returned.
     * @throws Exception
     */
    public ArrayList<String> getServiceList(int attemptCount) throws Exception {
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(JSON, "");
        ArrayList<String> result = null;
        attemptCount++;
        //this is an admin only call
        //try first with the available token, if fails then generate a new token
        if (adminToken.length() > 0) {
            Request request = new Request.Builder().url(gatekeeperUri + ":" + gatekeeperPort + "/admin/service/")
                    .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Token", adminToken).get()
                    .build();
            Response response = client.newCall(request).execute();

            driverLogger.info("List Service::Response code: " + response.code());

            if (response.code() == 200) {
                ResponseBody rBody = response.body();
                JSONObject jsonObj = new JSONObject(rBody.string());
                JSONObject sList = new JSONObject(jsonObj.get("servicelist").toString());
                JSONArray sNameList = sList.getJSONArray("shortname");
                JSONArray sKeyList = sList.getJSONArray("service-key");
                JSONArray sIdList = sList.getJSONArray("id");
                driverLogger.info("Got service-list with : " + sNameList.length() + " services.");
                result = new ArrayList<String>(sNameList.length());
                for (int i = 0; i < sNameList.length(); i++) {
                    result.add(i,
                            sNameList.getString(i) + "," + sKeyList.getString(i) + "," + sIdList.getString(i));
                }
                response.body().close();
            } else {
                //something wrong with the token.
                driverLogger
                        .warn("Error probably with the admin-token. Remaking this call automatically. Attempt - "
                                + attemptCount);
                response.body().close();
                this.adminToken = "";
                if (attemptCount < 5)
                    return this.getServiceList(attemptCount);
                else {
                    driverLogger.error("Retry limit reached! Failing gracefully.");
                }
            }
        } else {
            //generate a new token
            adminToken = generateToken(Integer.parseInt(adminUserId), adminPassword);
            if (adminToken == null)
                adminToken = "";
            if (attemptCount < 5)
                return this.getServiceList(attemptCount); //call the function again now that token has been set
        }
        return result;
    }

    /**
     * This method allows an admin user to register a new service with Gatekeeper, what is returned is a service-uri and
     * service-key. The key should be made available to the service itself for authorizing access to its service, the uri
     * can be stored by admin to manipulate this service representation in Gatekeeper later on.
     * <p>
     * @param shortName     a unique short-name (one-word) for this service being registered, this short-name can be used
     *                      in the access-list of users granting then access to this service.
     * @param description   a sentence describing this service.
     * @param attemptCount  integer value controlling the self execution iterations, should be a value between 0 and 4
     * @return  a HashMap object containing the service uri and the key. null is returned in case of error.
     * @throws Exception
     */
    public HashMap<String, String> registerService(String shortName, String description, int attemptCount)
            throws Exception {
        OkHttpClient client = new OkHttpClient();
        JSONObject callBody = new JSONObject();
        callBody.put("shortname", shortName);
        callBody.put("description", description);
        attemptCount++;

        driverLogger.info("register-service call with json: " + callBody.toString());
        RequestBody body = RequestBody.create(JSON, callBody.toString());

        //this is an admin only call
        //try first with the available token, if fails then generate a new token
        if (adminToken.length() > 0) {
            Request request = new Request.Builder().url(gatekeeperUri + ":" + gatekeeperPort + "/admin/service/")
                    .header("User-Agent", "OkHttp Headers.java").addHeader("X-Auth-Token", adminToken).post(body)
                    .build();

            Response response = client.newCall(request).execute();

            driverLogger.info("Register Service::Response code: " + response.code());

            if (response.code() == 200) {
                ResponseBody rBody = response.body();
                JSONObject jsonObj = new JSONObject(rBody.string());
                JSONArray uinfoArray = jsonObj.getJSONArray("info");
                JSONObject temp = new JSONObject(uinfoArray.get(0).toString());
                driverLogger.info("Got service-uri: " + temp.get("service-uri"));
                HashMap<String, String> result = new HashMap<String, String>();
                result.put("uri", temp.getString("service-uri"));
                result.put("key", temp.getString("service-key"));
                response.body().close();
                return result;
            }
            response.body().close();
        } else {
            //generate a new token
            if (attemptCount < 5) {
                adminToken = generateToken(Integer.parseInt(adminUserId), adminPassword);
                if (adminToken == null)
                    adminToken = "";
                if (attempCount < 5)
                    return this.registerService(shortName, description, attemptCount); //call the function again now that token has been set
            }
        }
        return null;
    }
}