ddf.security.sts.claimsHandler.ClaimsHandlerManager.java Source code

Java tutorial

Introduction

Here is the source code for ddf.security.sts.claimsHandler.ClaimsHandlerManager.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p>
 * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or any later version.
 * <p>
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License
 * is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package ddf.security.sts.claimsHandler;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import org.apache.commons.io.IOUtils;
import org.apache.cxf.sts.claims.ClaimsHandler;
import org.codice.ddf.configuration.PropertyResolver;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LDAPOptions;
import org.forgerock.opendj.ldap.LdapException;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ddf.security.encryption.EncryptionService;

/**
 * Creates and registers LDAP and Role claims handlers.
 */
public class ClaimsHandlerManager {

    public static final String URL = "url";

    public static final String START_TLS = "startTls";

    public static final String LDAP_BIND_USER_DN = "ldapBindUserDn";

    public static final String PASSWORD = "password";

    public static final String USER_NAME_ATTRIBUTE = "userNameAttribute";

    public static final String USER_BASE_DN = "userBaseDn";

    public static final String OBJECT_CLASS = "objectClass";

    public static final String MEMBER_NAME_ATTRIBUTE = "memberNameAttribute";

    public static final String GROUP_BASE_DN = "groupBaseDn";

    public static final String USER_DN = "userDn";

    public static final String PROPERTY_FILE_LOCATION = "propertyFileLocation";

    private static final Logger LOGGER = LoggerFactory.getLogger(ClaimsHandlerManager.class);

    private static final String PROTOCOL = "TLS";

    private EncryptionService encryptService;

    private ServiceRegistration<ClaimsHandler> roleHandlerRegistration = null;

    private ServiceRegistration<ClaimsHandler> ldapHandlerRegistration = null;

    private Map<String, Object> ldapProperties = new HashMap<>();

    private Map<String, Object> props;

    /**
     * Creates a new instance of the ClaimsHandlerManager.
     *
     * @param encryptService Encryption service used to decrypt passwords from the configurations.
     */
    public ClaimsHandlerManager(EncryptionService encryptService) {
        this.encryptService = encryptService;
    }

    /**
     * Callback method that is called when configuration is updated. Also called by the
     * blueprint init-method when all properties have been set.
     *
     * @param props Map of properties.
     */
    public void update(Map<String, Object> props) {
        if (props == null) {
            return;
        }
        LOGGER.debug("Received an updated set of configurations for the LDAP/Role Claims Handlers.");
        String url = props.get(ClaimsHandlerManager.URL).toString();
        Boolean startTls;
        if (props.get(ClaimsHandlerManager.START_TLS) instanceof String) {
            startTls = Boolean.valueOf((String) props.get(ClaimsHandlerManager.START_TLS));
        } else {
            startTls = (Boolean) props.get(ClaimsHandlerManager.START_TLS);
        }
        String userDn = (String) props.get(ClaimsHandlerManager.LDAP_BIND_USER_DN);
        String password = (String) props.get(ClaimsHandlerManager.PASSWORD);
        String userBaseDn = (String) props.get(ClaimsHandlerManager.USER_BASE_DN);
        String objectClass = (String) props.get(ClaimsHandlerManager.OBJECT_CLASS);
        String memberNameAttribute = (String) props.get(ClaimsHandlerManager.MEMBER_NAME_ATTRIBUTE);
        String groupBaseDn = (String) props.get(ClaimsHandlerManager.GROUP_BASE_DN);
        String userNameAttribute = (String) props.get(ClaimsHandlerManager.USER_NAME_ATTRIBUTE);
        String propertyFileLocation = (String) props.get(ClaimsHandlerManager.PROPERTY_FILE_LOCATION);
        try {
            if (encryptService != null) {
                password = encryptService.decryptValue(password);
            }
            LDAPConnectionFactory connection1 = createLdapConnectionFactory(url, startTls);
            LDAPConnectionFactory connection2 = createLdapConnectionFactory(url, startTls);
            registerRoleClaimsHandler(connection1, propertyFileLocation, userBaseDn, userNameAttribute, objectClass,
                    memberNameAttribute, groupBaseDn, userDn, password);
            registerLdapClaimsHandler(connection2, propertyFileLocation, userBaseDn, userNameAttribute, userDn,
                    password);

        } catch (Exception e) {
            LOGGER.warn(
                    "Experienced error while configuring claims handlers. Handlers are NOT configured and claim retrieval will not work.",
                    e);
        }
        this.props = props;
    }

    public void destroy() {

    }

    protected LDAPConnectionFactory createLdapConnectionFactory(String url, Boolean startTls) throws LdapException {
        boolean useSsl = url.startsWith("ldaps");
        boolean useTls = !url.startsWith("ldaps") && startTls;

        LDAPOptions lo = new LDAPOptions();

        String truststoreLoc = System.getProperty("javax.net.ssl.trustStore");
        String truststorePass = System.getProperty("javax.net.ssl.trustStorePassword");
        String keystoreLoc = System.getProperty("javax.net.ssl.keyStore");
        String keystorePass = System.getProperty("javax.net.ssl.keyStorePassword");
        if (encryptService != null) {
            keystorePass = encryptService.decryptValue(keystorePass);
            truststorePass = encryptService.decryptValue(truststorePass);
        }

        try {
            if (useSsl || useTls) {
                SSLContext sslContext = SSLContext.getInstance(PROTOCOL);
                if (keystoreLoc != null && truststoreLoc != null) {

                    sslContext.init(createKeyManagerFactory(keystoreLoc, keystorePass).getKeyManagers(),
                            createTrustManagerFactory(truststoreLoc, truststorePass).getTrustManagers(),
                            new SecureRandom());
                }

                lo.setSSLContext(sslContext);
            }
        } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
            LOGGER.error("Error encountered while configuring SSL. Secure connection will fail.", e);
        }

        lo.setUseStartTLS(useTls);
        lo.addEnabledCipherSuite(System.getProperty("https.cipherSuites").split(","));
        lo.addEnabledProtocol(System.getProperty("https.protocols").split(","));
        lo.setProviderClassLoader(ClaimsHandlerManager.class.getClassLoader());

        String host = url.substring(url.indexOf("://") + 3, url.lastIndexOf(":"));
        Integer port = useSsl ? 636 : 389;
        try {
            port = Integer.valueOf(url.substring(url.lastIndexOf(":") + 1));
        } catch (NumberFormatException ignore) {
        }

        return new LDAPConnectionFactory(host, port, lo);
    }

    /**
     * Registers a new Role-based ClaimsHandler.
     *
     * @param connection      LdapTemplate used to query ldap for the roles.
     * @param propertyFileLoc File location of the property file.
     * @param userBaseDn      Base DN to determine the roles.
     * @param userNameAttr    Identifier that defines the user.
     * @param groupBaseDn     Base DN of the group.
     */
    private void registerRoleClaimsHandler(LDAPConnectionFactory connection, String propertyFileLoc,
            String userBaseDn, String userNameAttr, String objectClass, String memberNameAttribute,
            String groupBaseDn, String userDn, String password) {
        RoleClaimsHandler roleHandler = new RoleClaimsHandler();
        roleHandler.setLdapConnectionFactory(connection);
        roleHandler.setPropertyFileLocation(propertyFileLoc);
        roleHandler.setUserBaseDn(userBaseDn);
        roleHandler.setUserNameAttribute(userNameAttr);
        roleHandler.setObjectClass(objectClass);
        roleHandler.setMemberNameAttribute(memberNameAttribute);
        roleHandler.setGroupBaseDn(groupBaseDn);
        roleHandler.setBindUserDN(userDn);
        roleHandler.setBindUserCredentials(password);
        LOGGER.debug("Registering new role claims handler.");
        roleHandlerRegistration = registerClaimsHandler(roleHandler, roleHandlerRegistration);
    }

    /**
     * Registers a new Ldap-based Claims Handler.
     *
     * @param connection      LdapTemplate used to query ldap for the roles.
     * @param propertyFileLoc File location of the property file.
     * @param userBaseDn      Base DN to determine the roles.
     * @param userNameAttr    Identifier that defines the user.
     */
    private void registerLdapClaimsHandler(LDAPConnectionFactory connection, String propertyFileLoc,
            String userBaseDn, String userNameAttr, String userDn, String password) {
        LdapClaimsHandler ldapHandler = new LdapClaimsHandler();
        ldapHandler.setLdapConnectionFactory(connection);
        ldapHandler.setPropertyFileLocation(propertyFileLoc);
        ldapHandler.setUserBaseDN(userBaseDn);
        ldapHandler.setUserNameAttribute(userNameAttr);
        ldapHandler.setBindUserDN(userDn);
        ldapHandler.setBindUserCredentials(password);
        LOGGER.debug("Registering new ldap claims handler.");
        ldapHandlerRegistration = registerClaimsHandler(ldapHandler, ldapHandlerRegistration);
    }

    /**
     * Utility method that registers a ClaimsHandler and returns the service
     * registration.
     *
     * @param handler      Handler that should be registered.
     * @param registration Previous registration, will be used to unregister if not null.
     * @return new registration for the service.
     */
    private ServiceRegistration<ClaimsHandler> registerClaimsHandler(ClaimsHandler handler,
            ServiceRegistration<ClaimsHandler> registration) {
        BundleContext context = getContext();
        if (null != context) {
            if (registration != null) {
                ClaimsHandler oldClaimsHandler = context.getService(registration.getReference());
                if (oldClaimsHandler instanceof RoleClaimsHandler) {
                    ((RoleClaimsHandler) oldClaimsHandler).disconnect();
                } else if (oldClaimsHandler instanceof LdapClaimsHandler) {
                    ((LdapClaimsHandler) oldClaimsHandler).disconnect();
                }
                registration.unregister();
            }

            return context.registerService(ClaimsHandler.class, handler, null);
        }
        return null;
    }

    protected BundleContext getContext() {
        Bundle cxfBundle = FrameworkUtil.getBundle(ClaimsHandlerManager.class);
        if (cxfBundle != null) {
            return cxfBundle.getBundleContext();
        }
        return null;
    }

    public void setUrl(String url) {
        LOGGER.trace("Setting url: {}", url);
        ldapProperties.put(URL, new PropertyResolver(url));
    }

    public void setStartTls(boolean startTls) {
        LOGGER.trace("Setting startTls: {}", startTls);
        ldapProperties.put(START_TLS, startTls);
    }

    public void setStartTls(String startTls) {
        LOGGER.trace("Setting startTls: {}", startTls);
        ldapProperties.put(START_TLS, startTls);
    }

    public void setLdapBindUserDn(String bindUserDn) {
        LOGGER.trace("Setting bindUserDn: {}", bindUserDn);
        ldapProperties.put(LDAP_BIND_USER_DN, bindUserDn);
    }

    public void setPassword(String password) {
        LOGGER.trace("Setting password: [HIDDEN]");
        ldapProperties.put(PASSWORD, password);
    }

    public void setUserNameAttribute(String userNameAttribute) {
        LOGGER.trace("Setting userNameAttribute: {}", userNameAttribute);
        ldapProperties.put(USER_NAME_ATTRIBUTE, userNameAttribute);
    }

    public void setUserBaseDn(String userBaseDn) {
        LOGGER.trace("Setting userBaseDn: {}", userBaseDn);
        ldapProperties.put(USER_BASE_DN, userBaseDn);
    }

    public void setObjectClass(String objectClass) {
        LOGGER.trace("Setting objectClass: {}", objectClass);
        ldapProperties.put(OBJECT_CLASS, objectClass);
    }

    public void setMemberNameAttribute(String memberNameAttribute) {
        LOGGER.trace("Setting memberNameAttribute: {}", memberNameAttribute);
        ldapProperties.put(MEMBER_NAME_ATTRIBUTE, memberNameAttribute);
    }

    public void setGroupBaseDn(String groupBaseDn) {
        LOGGER.trace("Setting groupBaseDn: {}", groupBaseDn);
        ldapProperties.put(GROUP_BASE_DN, groupBaseDn);
    }

    public void setPropertyFileLocation(String propertyFileLocation) {
        LOGGER.trace("Setting propertyFileLocation: {}", propertyFileLocation);
        ldapProperties.put(PROPERTY_FILE_LOCATION, propertyFileLocation);
    }

    public void configure() {
        LOGGER.trace("configure method called - calling update");
        update(ldapProperties);
    }

    public static KeyManagerFactory createKeyManagerFactory(String keyStoreLoc, String keyStorePass)
            throws IOException {
        KeyManagerFactory kmf;
        try {
            // keystore stuff
            KeyStore keyStore = KeyStore.getInstance(System.getProperty("javax.net.ssl.keyStoreType"));
            LOGGER.debug("keyStoreLoc = {}", keyStoreLoc);
            FileInputStream keyFIS = new FileInputStream(keyStoreLoc);
            try {
                LOGGER.debug("Loading keyStore");
                keyStore.load(keyFIS, keyStorePass.toCharArray());
            } catch (CertificateException e) {
                throw new IOException("Unable to load certificates from keystore. " + keyStoreLoc, e);
            } finally {
                IOUtils.closeQuietly(keyFIS);
            }
            kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(keyStore, keyStorePass.toCharArray());
            LOGGER.debug("key manager factory initialized");
        } catch (NoSuchAlgorithmException e) {
            throw new IOException(
                    "Problems creating SSL socket. Usually this is "
                            + "referring to the certificate sent by the server not being trusted by the client.",
                    e);
        } catch (UnrecoverableKeyException e) {
            throw new IOException("Unable to load keystore. " + keyStoreLoc, e);
        } catch (KeyStoreException e) {
            throw new IOException("Unable to read keystore. " + keyStoreLoc, e);
        }

        return kmf;
    }

    public static TrustManagerFactory createTrustManagerFactory(String trustStoreLoc, String trustStorePass)
            throws IOException {
        TrustManagerFactory tmf;
        try {
            // truststore stuff
            KeyStore trustStore = KeyStore.getInstance(System.getProperty("javax.net.ssl.keyStoreType"));
            LOGGER.debug("trustStoreLoc = {}", trustStoreLoc);
            FileInputStream trustFIS = new FileInputStream(trustStoreLoc);
            try {
                LOGGER.debug("Loading trustStore");
                trustStore.load(trustFIS, trustStorePass.toCharArray());
            } catch (CertificateException e) {
                throw new IOException("Unable to load certificates from truststore. " + trustStoreLoc, e);
            } finally {
                IOUtils.closeQuietly(trustFIS);
            }

            tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(trustStore);
            LOGGER.debug("trust manager factory initialized");
        } catch (NoSuchAlgorithmException e) {
            throw new IOException(
                    "Problems creating SSL socket. Usually this is "
                            + "referring to the certificate sent by the server not being trusted by the client.",
                    e);
        } catch (KeyStoreException e) {
            throw new IOException("Unable to read keystore. " + trustStoreLoc, e);
        }
        return tmf;
    }
}