org.wso2.carbon.identity.application.authentication.endpoint.util.TenantDataManager.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.identity.application.authentication.endpoint.util.TenantDataManager.java

Source

/*
 * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. 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.wso2.carbon.identity.application.authentication.endpoint.util;

import org.apache.axiom.om.util.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.wso2.carbon.identity.application.authentication.endpoint.util.AuthenticationException;
import org.wso2.securevault.SecretResolver;
import org.wso2.securevault.SecretResolverFactory;
import org.xml.sax.InputSource;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class TenantDataManager {

    private static final Log log = LogFactory.getLog(TenantDataManager.class);

    // Tenant list dropdown related properties
    private static final String USERNAME = "mutual.ssl.username";
    private static final String USERNAME_HEADER = "username.header";
    private static final String HOST = "identity.server.host";
    private static final String PORT = "identity.server.port";
    private static final String CLIENT_KEY_STORE = "client.keyStore";
    private static final String CLIENT_TRUST_STORE = "client.trustStore";
    private static final String CLIENT_KEY_STORE_PASSWORD = "Carbon.Security.KeyStore.Password";
    private static final String CLIENT_TRUST_STORE_PASSWORD = "Carbon.Security.TrustStore.Password";
    private static final String HOSTNAME_VERIFICATION_ENABLED = "hostname.verification.enabled";
    private static final String KEY_MANAGER_TYPE = "key.manager.type";
    private static final String TRUST_MANAGER_TYPE = "trust.manager.type";
    private static final String TENANT_LIST_ENABLED = "tenantListEnabled";

    // Service URL constants
    private static final String HTTPS_URL = "https://";
    private static final String TENANT_MGT_ADMIN_SERVICE_URL = "/services/TenantMgtAdminService/retrieveTenants";
    private static final String COLON = ":";

    // String constants for SOAP response processing
    private static final String RETURN = "return";
    private static final String RETRIEVE_TENANTS_RESPONSE = "retrieveTenantsResponse";
    private static final String TENANT_DOMAIN = "tenantDomain";
    private static final String ACTIVE = "active";
    private static final String TENANT_DATA_SEPARATOR = ",";
    private static final String RELATIVE_PATH_START_CHAR = ".";
    private static final String CHARACTER_ENCODING = "UTF-8";
    private static final String CONFIG_RELATIVE_PATH = "./repository/conf/TenantConfig.properties";
    private static final String CONFIG_FILE_NAME = "TenantConfig.properties";
    private static Properties prop;
    private static String carbonLogin = "";
    private static String serviceURL;
    private static String usernameHeaderName = "";
    private static List<String> tenantDomainList = new ArrayList<String>();
    private static boolean initialized = false;
    private static boolean initAttempted = false;

    private TenantDataManager() {
    }

    /**
     * Initialize Tenant data manager
     */
    public static synchronized void init() {

        InputStream inputStream = null;
        initAttempted = true;

        try {
            if (!initialized) {
                prop = new Properties();
                String configFilePath = buildFilePath(CONFIG_RELATIVE_PATH);
                File configFile = new File(configFilePath);

                if (configFile.exists()) {
                    log.info(CONFIG_FILE_NAME + " file loaded from " + CONFIG_RELATIVE_PATH);
                    inputStream = new FileInputStream(configFile);

                    prop.load(inputStream);
                    // Resolve encrypted properties with secure vault
                    resolveSecrets(prop);
                } else {
                    log.info(CONFIG_FILE_NAME + " file loaded from authentication endpoint webapp");
                    inputStream = TenantDataManager.class.getClassLoader().getResourceAsStream(CONFIG_FILE_NAME);
                    prop.load(inputStream);
                }

                usernameHeaderName = getPropertyValue(USERNAME_HEADER);

                carbonLogin = getPropertyValue(USERNAME);

                // Base64 encoded username
                carbonLogin = Base64.encode(carbonLogin.getBytes(CHARACTER_ENCODING));

                String clientKeyStorePath = buildFilePath(getPropertyValue(CLIENT_KEY_STORE));
                String clientTrustStorePath = buildFilePath(getPropertyValue(CLIENT_TRUST_STORE));

                if (StringUtils.isNotEmpty(getPropertyValue(KEY_MANAGER_TYPE))) {
                    TenantMgtAdminServiceClient.setKeyManagerType(getPropertyValue(KEY_MANAGER_TYPE));
                }
                if (StringUtils.isNotEmpty(getPropertyValue(TRUST_MANAGER_TYPE))) {
                    TenantMgtAdminServiceClient.setTrustManagerType(getPropertyValue(TRUST_MANAGER_TYPE));
                }

                TenantMgtAdminServiceClient.loadKeyStore(clientKeyStorePath,
                        getPropertyValue(CLIENT_KEY_STORE_PASSWORD));
                TenantMgtAdminServiceClient.loadTrustStore(clientTrustStorePath,
                        getPropertyValue(CLIENT_TRUST_STORE_PASSWORD));
                TenantMgtAdminServiceClient.initMutualSSLConnection(
                        Boolean.parseBoolean(getPropertyValue(HOSTNAME_VERIFICATION_ENABLED)));

                // Build the service URL of tenant management admin service
                StringBuilder builder = new StringBuilder();
                serviceURL = builder.append(HTTPS_URL).append(getPropertyValue(HOST)).append(COLON)
                        .append(getPropertyValue(PORT)).append(TENANT_MGT_ADMIN_SERVICE_URL).toString();

                initialized = true;
            }

        } catch (AuthenticationException | IOException e) {
            log.error("Initialization failed : ", e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("Failed to close the FileInputStream, file : " + CONFIG_FILE_NAME, e);
                }
            }
        }
    }

    /**
     * Build the absolute path of a give file path
     *
     * @param path File path
     * @return Absolute file path
     * @throws java.io.IOException
     */
    private static String buildFilePath(String path) throws IOException {

        if (StringUtils.isNotEmpty(path) && path.startsWith(RELATIVE_PATH_START_CHAR)) {
            // Relative file path is given
            File currentDirectory = new File(new File(RELATIVE_PATH_START_CHAR).getAbsolutePath());
            path = currentDirectory.getCanonicalPath() + File.separator + path;
        }

        if (log.isDebugEnabled()) {
            log.debug("File path for KeyStore/TrustStore : " + path);
        }
        return path;
    }

    /**
     * Get property value by key
     *
     * @param key Property key
     * @return Property value
     */
    private static String getPropertyValue(String key) {
        return prop.getProperty(key);
    }

    /**
     * Call service and return response
     *
     * @param url Service URL
     * @return Response from service
     */
    private static String getServiceResponse(String url) {
        String serviceResponse = null;
        Map<String, String> headerParams = new HashMap<String, String>();
        // Set the username in HTTP header for mutual ssl authentication
        headerParams.put(usernameHeaderName, carbonLogin);
        serviceResponse = TenantMgtAdminServiceClient.sendPostRequest(url, null, headerParams);
        return serviceResponse;
    }

    /**
     * Get active tenants list
     *
     * @return List of tenant domains
     */
    public static List<String> getAllActiveTenantDomains() {

        if (initialized && tenantDomainList.isEmpty()) {
            refreshActiveTenantDomainsList();
        }
        return tenantDomainList;
    }

    /**
     * Set the updated tenant domains list
     *
     * @param dataList List of active tenant domains
     */
    public static void setTenantDataList(String dataList) {

        if (!initialized) {
            if (log.isDebugEnabled()) {
                log.debug("Tenant domains list not set as TenantDataManager is not initialized.");
            }
            return;
        }

        if (StringUtils.isNotEmpty(dataList)) {
            synchronized (tenantDomainList) {
                String[] domains = dataList.split(TENANT_DATA_SEPARATOR);
                // Remove all existing tenant domains from the list
                tenantDomainList.clear();

                Collections.addAll(tenantDomainList, domains);
                // Sort the tenant domains list according to alphabetical order
                Collections.sort(tenantDomainList);
            }
        } else {
            // Reset active tenant domains list
            tenantDomainList.clear();
        }
    }

    /**
     * Retrieve latest active tenant domains list
     */
    private static void refreshActiveTenantDomainsList() {

        try {
            String xmlString = getServiceResponse(serviceURL);

            if (StringUtils.isNotEmpty(xmlString)) {

                XPathFactory xpf = XPathFactory.newInstance();
                XPath xpath = xpf.newXPath();

                InputSource inputSource = new InputSource(new StringReader(xmlString));
                String xPathExpression = "/*[local-name() = '" + RETRIEVE_TENANTS_RESPONSE + "']/*[local-name() = '"
                        + RETURN + "']";
                NodeList nodeList = null;
                nodeList = (NodeList) xpath.evaluate(xPathExpression, inputSource, XPathConstants.NODESET);

                // Reset existing tenant domains list
                tenantDomainList.clear();

                // For each loop is not supported for NodeList
                for (int i = 0; i < nodeList.getLength(); i++) {
                    Node node = nodeList.item(i);
                    if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
                        Element element = (Element) node;
                        NodeList tenantData = element.getChildNodes();
                        boolean activeChecked = false;
                        boolean domainChecked = false;
                        boolean isActive = false;
                        String tenantDomain = null;

                        // For each loop is not supported for NodeList
                        for (int j = 0; j < tenantData.getLength(); j++) {
                            Node dataItem = tenantData.item(j);
                            String localName = dataItem.getLocalName();

                            if (ACTIVE.equals(localName)) {
                                // Current element has domain status active or inactive
                                activeChecked = true;
                                if (Boolean.parseBoolean(dataItem.getTextContent())) {
                                    isActive = true;
                                }
                            }

                            if (TENANT_DOMAIN.equals(localName)) {
                                // Current element has domain name of the tenant
                                domainChecked = true;
                                tenantDomain = dataItem.getTextContent();
                            }

                            if (activeChecked && domainChecked) {
                                if (isActive) {
                                    tenantDomainList.add(tenantDomain);

                                    if (log.isDebugEnabled()) {
                                        log.debug(tenantDomain + " is active and added to the dropdown list");
                                    }
                                } else {
                                    if (log.isDebugEnabled()) {
                                        log.debug(tenantDomain + " is inactive and not added to the dropdown list");
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
                // Sort the list of tenant domains alphabetically
                Collections.sort(tenantDomainList);
            }
        } catch (Exception e) {
            // Catching the general exception as if no tenants are available it should stop processing
            log.error("Retrieving list of active tenant domains failed. Ignore this if there are no tenants : ", e);
        }
    }

    /**
     * Get status of the tenant list dropdown enabled or disabled
     *
     * @return Tenant list enabled or disabled status
     */
    public static boolean isTenantListEnabled() {
        if (!initAttempted && !initialized) {
            init();
        }
        return Boolean.parseBoolean(getPropertyValue(TENANT_LIST_ENABLED));
    }

    /**
     * There can be sensitive information like passwords in configuration file. If they are encrypted using secure
     * vault, this method will resolve them and replace with original values.
     */
    private static void resolveSecrets(Properties properties) {

        SecretResolver secretResolver = SecretResolverFactory.create(properties);
        Enumeration propertyNames = properties.propertyNames();
        if (secretResolver != null && secretResolver.isInitialized()) {
            // Iterate through whole config file and find encrypted properties and resolve them
            while (propertyNames.hasMoreElements()) {
                String key = (String) propertyNames.nextElement();
                if (secretResolver.isTokenProtected(key)) {
                    if (log.isDebugEnabled()) {
                        log.debug("Resolving and replacing secret for " + key);
                    }
                    // Resolving the secret password.
                    String value = secretResolver.resolve(key);
                    // Replaces the original encrypted property with resolved property
                    properties.put(key, value);
                } else {
                    if (log.isDebugEnabled()) {
                        log.debug("No encryption done for value with key :" + key);
                    }
                }
            }
        } else {
            log.warn("Secret Resolver is not present. Will not resolve encryptions in " + CONFIG_RELATIVE_PATH
                    + " file");
        }
    }
}