com.ibm.watson.developer_cloud.util.CredentialUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.watson.developer_cloud.util.CredentialUtils.java

Source

/**
 * Copyright 2017 IBM Corp. 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.
 */
package com.ibm.watson.developer_cloud.util;

import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;

import okhttp3.Credentials;

/**
 * The Class CredentialUtils.
 */
public final class CredentialUtils {
    /**
     * A util class to easily store service credentials.
     *
     */
    public static class ServiceCredentials {
        private String password;
        private String username;

        private ServiceCredentials(String username, String password) {
            this.username = username;
            this.password = password;
        }

        /**
         * Gets the password.
         *
         * @return the password
         */
        public String getPassword() {
            return password;
        }

        /**
         * Gets the username.
         *
         * @return the username
         */
        public String getUsername() {
            return username;
        }
    }

    /** The Constant ALCHEMY_API. */
    private static final String ALCHEMY_API = "alchemy_api";

    /** The Constant APIKEY. */
    private static final String APIKEY = "apikey";

    /** The Constant CREDENTIALS. */
    private static final String CREDENTIALS = "credentials";

    /** The Constant log. */
    private static final Logger log = Logger.getLogger(CredentialUtils.class.getName());

    /** The Constant PASSWORD. */
    private static final String PASSWORD = "password";

    /** The Constant PLAN. */
    private static final String PLAN = "plan";

    /** The services. */
    private static String services;

    /** The Constant USERNAME. */
    private static final String USERNAME = "username";

    /** The Constant URL. */
    private static final String URL = "url";

    /** The Constant PLAN_EXPERIMENTAL. */
    public static final String PLAN_EXPERIMENTAL = "experimental";

    /** The Constant PLAN_FREE. */
    public static final String PLAN_FREE = "free";

    /** The Constant PLAN_STANDARD. */
    public static final String PLAN_STANDARD = "standard";

    private CredentialUtils() {
        // This is a utility class - no instantiation allowed.
    }

    /**
     * Attempt to get the Base64-encoded API key through JNDI.
     *
     * This method should always return null on Android due to the javax functions being unsupported
     *
     * @param serviceName Name of the bluemix service
     * @return The encoded API Key
     */
    private static String getKeyUsingJNDI(String serviceName) {
        if (!isClassAvailable("javax.naming.Context") || !isClassAvailable("javax.naming.InitialContext")) {
            log.info("JNDI string lookups is not available.");
            return null;
        }
        String lookupName = "watson-developer-cloud/" + serviceName + "/credentials";
        try {
            Context context = new InitialContext();
            return (String) context.lookup(lookupName);
        } catch (Exception e) {
            log.fine("JNDI " + lookupName + " not found.");
            return null;
        }
    }

    private static boolean isClassAvailable(String className) {
        try {
            Class.forName(className);
            return true;
        } catch (Throwable e) {
            return false;
        }
    }

    /**
     * Gets the <b>VCAP_SERVICES</b> environment variable and return it as a {@link JsonObject}.
     *
     * @return the VCAP_SERVICES as a {@link JsonObject}.
     */
    private static JsonObject getVCAPServices() {
        final String envServices = services != null ? services : System.getenv("VCAP_SERVICES");
        if (envServices == null) {
            return null;
        }

        JsonObject vcapServices = null;

        try {
            final JsonParser parser = new JsonParser();
            vcapServices = (JsonObject) parser.parse(envServices);
        } catch (final JsonSyntaxException e) {
            log.log(Level.INFO, "Error parsing VCAP_SERVICES", e);
        }
        return vcapServices;
    }

    /**
     * Returns the apiKey from the VCAP_SERVICES or null if doesn't exists.
     *
     * @param serviceName the service name
     * @return the API key or null if the service cannot be found.
     */
    public static String getAPIKey(String serviceName) {
        return getAPIKey(serviceName, null);
    }

    /**
     * Returns the apiKey from the VCAP_SERVICES or null if doesn't exists. If plan is specified, then only credentials
     * for the given plan will be returned.
     *
     * @param serviceName the service name
     * @param plan the service plan: standard, free or experimental
     * @return the API key
     */
    public static String getAPIKey(String serviceName, String plan) {
        if ((serviceName == null) || serviceName.isEmpty()) {
            return null;
        }

        final JsonObject services = getVCAPServices();
        if (services == null) {
            return getKeyUsingJNDI(serviceName);
        }
        if (serviceName.equalsIgnoreCase(ALCHEMY_API)) {
            final JsonObject credentials = getCredentialsObject(services, serviceName, plan);
            if ((credentials != null) && serviceName.equalsIgnoreCase(ALCHEMY_API)) {
                return credentials.get(APIKEY).getAsString();
            }
        } else {
            ServiceCredentials credentials = getUserNameAndPassword(serviceName, plan);
            if (credentials != null) {
                return Credentials.basic(credentials.getUsername(), credentials.getPassword());
            }
        }
        return null;
    }

    /**
     * Returns the username and password as defined in the VCAP_SERVICES or null if they do not exist or are not
     * accessible. This is a utility method for {@link #getUserNameAndPassword(String, String)}. Invoking this method is
     * identical to calling <code>getUserNameAndPassword(serviceName, null);</code>
     *
     * @param serviceName the name of the service whose credentials are sought
     * @return an object representing the service's credentials
     */
    public static ServiceCredentials getUserNameAndPassword(String serviceName) {
        return getUserNameAndPassword(serviceName, null);
    }

    /**
     * Returns the username and password as defined in the VCAP_SERVICES or null if they do not exist or are not
     * accessible. If a plan is provided then only the credentials for that plan (and service) will be returned. Null will
     * be returned if the plan does not exist.
     *
     * @param serviceName the name of the service whose credentials are sought
     * @param plan the plan name
     * @return an object representing the service's credentials
     */
    public static ServiceCredentials getUserNameAndPassword(String serviceName, String plan) {
        if ((serviceName == null) || serviceName.isEmpty()) {
            return null;
        }

        final JsonObject services = getVCAPServices();
        if (services == null) {
            return null;
        }

        JsonObject jsonCredentials = getCredentialsObject(services, serviceName, plan);
        if (jsonCredentials != null) {
            String username = null;
            if (jsonCredentials.has(USERNAME)) {
                username = jsonCredentials.get(USERNAME).getAsString();
            }
            String password = null;
            if (jsonCredentials.has(PASSWORD)) {
                password = jsonCredentials.get(PASSWORD).getAsString();
            }
            if ((username != null) || (password != null)) {
                // both will be null in the case of Alchemy API
                return new ServiceCredentials(username, password);
            }
        }
        return null;
    }

    /**
     * A helper method to retrieve the appropriate 'credentials' JSON property value from the VCAP_SERVICES.
     *
     * @param vcapServices JSON object representing the VCAP_SERVICES
     * @param serviceName the name of the service whose credentials are sought
     * @param plan the name of the plan for which the credentials are sought, e.g. 'standard', 'beta' etc, may be null
     * @return the first set of credentials that match the search criteria, service name and plan. May return null
     */
    private static JsonObject getCredentialsObject(JsonObject vcapServices, String serviceName, String plan) {
        for (final Entry<String, JsonElement> entry : vcapServices.entrySet()) {
            final String key = entry.getKey();
            if (key.startsWith(serviceName)) {
                final JsonArray servInstances = vcapServices.getAsJsonArray(key);
                for (final JsonElement instance : servInstances) {
                    final JsonObject service = instance.getAsJsonObject();
                    final String instancePlan = service.get(PLAN).getAsString();
                    if ((plan == null) || plan.equalsIgnoreCase(instancePlan)) {
                        return instance.getAsJsonObject().getAsJsonObject(CREDENTIALS);
                    }
                }
            }
        }
        return null;
    }

    /**
     * Gets the API url.
     *
     * @param serviceName the service name
     * @return the API url
     */

    public static String getAPIUrl(String serviceName) {
        return getAPIUrl(serviceName, null);
    }

    /**
     * Returns the apiKey from the VCAP_SERVICES or null if doesn't exists. If plan is specified, then only credentials
     * for the given plan will be returned.
     *
     * @param serviceName the service name
     * @param plan the service plan: standard, free or experimental
     * @return the API key
     */
    public static String getAPIUrl(String serviceName, String plan) {
        if ((serviceName == null) || serviceName.isEmpty()) {
            return null;
        }

        final JsonObject services = getVCAPServices();
        if (services == null) {
            return null;
        }

        final JsonObject credentials = getCredentialsObject(services, serviceName, plan);
        if ((credentials != null) && credentials.has(URL)) {
            return credentials.get(URL).getAsString();
        }

        return null;
    }

    /**
     * Sets the VCAP_SERVICES variable. This is utility variable for testing
     *
     * @param services the VCAP_SERVICES
     */
    public static void setServices(String services) {
        CredentialUtils.services = services;
    }
}