at.gv.egovernment.moa.id.protocols.pvp2x.metadata.MOAMetadataProvider.java Source code

Java tutorial

Introduction

Here is the source code for at.gv.egovernment.moa.id.protocols.pvp2x.metadata.MOAMetadataProvider.java

Source

/*******************************************************************************
 * Copyright 2014 Federal Chancellery Austria
 * MOA-ID has been developed in a cooperation between BRZ, the Federal
 * Chancellery Austria - ICT staff unit, and Graz University of Technology.
 *
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
 * the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * http://www.osor.eu/eupl/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 *
 * This product combines work with different licenses. See the "NOTICE" text
 * file for details on the various modules and licenses.
 * The "NOTICE" text file is part of the distribution. Any derivative works
 * that you distribute must include a readable copy of the "NOTICE" text file.
 *******************************************************************************/
package at.gv.egovernment.moa.id.protocols.pvp2x.metadata;

import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;

import javax.net.ssl.SSLHandshakeException;
import javax.xml.namespace.QName;

import org.apache.commons.httpclient.MOAHttpClient;
import org.opensaml.saml2.metadata.EntitiesDescriptor;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.RoleDescriptor;
import org.opensaml.saml2.metadata.provider.ChainingMetadataProvider;
import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider;
import org.opensaml.saml2.metadata.provider.MetadataFilter;
import org.opensaml.saml2.metadata.provider.MetadataProvider;
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.parse.BasicParserPool;

import at.gv.egovernment.moa.id.commons.db.ConfigurationDBRead;
import at.gv.egovernment.moa.id.commons.db.dao.config.ChainingModeType;
import at.gv.egovernment.moa.id.commons.db.dao.config.OAPVP2;
import at.gv.egovernment.moa.id.commons.db.dao.config.OnlineApplication;
import at.gv.egovernment.moa.id.commons.ex.MOAHttpProtocolSocketFactoryException;
import at.gv.egovernment.moa.id.commons.utils.MOAHttpProtocolSocketFactory;
import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider;
import at.gv.egovernment.moa.id.protocols.pvp2x.PVPConstants;
import at.gv.egovernment.moa.id.protocols.pvp2x.verification.metadata.InterfederatedIDPPublicServiceFilter;
import at.gv.egovernment.moa.id.protocols.pvp2x.verification.metadata.MetadataFilterChain;
import at.gv.egovernment.moa.id.protocols.pvp2x.verification.metadata.SchemaValidationFilter;
import at.gv.egovernment.moa.logging.Logger;
import at.gv.egovernment.moa.util.MiscUtil;

public class MOAMetadataProvider implements MetadataProvider {

    private static MOAMetadataProvider instance = null;

    private static Object mutex = new Object();
    private static Date timestamp = null;

    public static MOAMetadataProvider getInstance() {
        if (instance == null) {
            synchronized (mutex) {
                if (instance == null) {
                    instance = new MOAMetadataProvider();
                }
            }
        }
        return instance;
    }

    public static Date getTimeStamp() {
        return timestamp;
    }

    public static void reInitialize() {
        synchronized (mutex) {

            /**add new Metadataprovider or remove Metadataprovider which are not in use any more.**/
            if (instance != null)
                instance.addAndRemoveMetadataProvider();

            else
                Logger.info("MOAMetadataProvider is not loaded.");
        }
    }

    public static void destroy() {
        if (instance != null) {
            instance.internalDestroy();

        } else {
            Logger.info("MOAMetadataProvider is not loaded. Accordingly it can not be destroyed");
        }
    }

    MetadataProvider internalProvider;

    private void addAndRemoveMetadataProvider() {
        if (internalProvider != null && internalProvider instanceof ChainingMetadataProvider) {
            Logger.info("Relaod MOAMetaDataProvider.");

            /*OpenSAML ChainingMetadataProvider can not remove a MetadataProvider (UnsupportedOperationException)
             *The ChainingMetadataProvider use internal a unmodifiableList to hold all registrated MetadataProviders.*/
            Map<String, MetadataProvider> providersinuse = new HashMap<String, MetadataProvider>();

            Map<String, HTTPMetadataProvider> loadedproviders = new HashMap<String, HTTPMetadataProvider>();
            ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider;

            //make a Map of all actually loaded HTTPMetadataProvider
            List<MetadataProvider> providers = chainProvider.getProviders();
            for (MetadataProvider provider : providers) {
                if (provider instanceof HTTPMetadataProvider) {
                    HTTPMetadataProvider httpprovider = (HTTPMetadataProvider) provider;
                    loadedproviders.put(httpprovider.getMetadataURI(), httpprovider);
                }
            }

            //set Timestamp
            Date oldTimeStamp = timestamp;
            timestamp = new Date();

            //load all PVP2 OAs form ConfigurationDatabase and 
            //compare actually loaded Providers with configured PVP2 OAs
            List<OnlineApplication> oaList = ConfigurationDBRead.getAllActiveOnlineApplications();

            Iterator<OnlineApplication> oaIt = oaList.iterator();
            while (oaIt.hasNext()) {
                HTTPMetadataProvider httpProvider = null;

                try {
                    OnlineApplication oa = oaIt.next();
                    OAPVP2 pvp2Config = oa.getAuthComponentOA().getOAPVP2();
                    if (pvp2Config != null && MiscUtil.isNotEmpty(pvp2Config.getMetadataURL())) {

                        String metadataurl = pvp2Config.getMetadataURL();

                        if (loadedproviders.containsKey(metadataurl)) {

                            if (pvp2Config.getUpdateRequiredItem() != null
                                    && pvp2Config.getUpdateRequiredItem().after(oldTimeStamp)) {
                                //PVP2 OA is actually loaded, but update is requested
                                Logger.info("Reload metadata for: " + oa.getFriendlyName());
                                loadedproviders.get(metadataurl).refresh();

                            }

                            //   PVP2 OA is actually loaded, to nothing
                            providersinuse.put(metadataurl, loadedproviders.get(metadataurl));
                            loadedproviders.remove(metadataurl);

                        } else if (MiscUtil.isNotEmpty(metadataurl) && !providersinuse.containsKey(metadataurl)) {
                            //PVP2 OA is new, add it to MOAMetadataProvider

                            Logger.info("Loading metadata for: " + oa.getFriendlyName());
                            httpProvider = createNewHTTPMetaDataProvider(pvp2Config.getMetadataURL(),
                                    pvp2Config.getCertificate(), oa.getFriendlyName(), buildMetadataFilterChain(oa,
                                            pvp2Config.getMetadataURL(), pvp2Config.getCertificate()));

                            if (httpProvider != null)
                                providersinuse.put(metadataurl, httpProvider);

                        }
                    }
                } catch (Throwable e) {
                    Logger.error("Failed to add Metadata (unhandled reason: " + e.getMessage(), e);

                    if (httpProvider != null) {
                        Logger.debug("Destroy failed Metadata provider");
                        httpProvider.destroy();
                    }

                }
            }

            //remove all actually loaded MetadataProviders with are not in ConfigurationDB any more
            Collection<HTTPMetadataProvider> notusedproviders = loadedproviders.values();
            for (HTTPMetadataProvider provider : notusedproviders) {
                String metadataurl = provider.getMetadataURI();

                try {

                    provider.destroy();

                    /*OpenSAML ChainingMetadataProvider can not remove a MetadataProvider (UnsupportedOperationException)
                     *The ChainingMetadataProvider use internal a unmodifiableList to hold all registrated MetadataProviders.*/
                    //chainProvider.removeMetadataProvider(provider);

                    Logger.info("Remove not used MetadataProvider with MetadataURL " + metadataurl);

                } catch (Throwable e) {
                    Logger.error("HTTPMetadataProvider with URL " + metadataurl
                            + " can not be removed from the list of actually loaded Providers.", e);

                }

            }

            try {
                chainProvider.setProviders(new ArrayList<MetadataProvider>(providersinuse.values()));

            } catch (MetadataProviderException e) {
                Logger.warn(
                        "ReInitalize MOAMetaDataProvider is not possible! MOA-ID Instance has to be restarted manualy",
                        e);

            }

        } else {
            Logger.warn(
                    "ReInitalize MOAMetaDataProvider is not possible! MOA-ID Instance has to be restarted manualy");
        }

    }

    public void internalDestroy() {
        if (internalProvider != null && internalProvider instanceof ChainingMetadataProvider) {
            Logger.info("Destrorying MOAMetaDataProvider.");
            ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider;

            List<MetadataProvider> providers = chainProvider.getProviders();
            for (MetadataProvider provider : providers) {
                if (provider instanceof HTTPMetadataProvider) {
                    HTTPMetadataProvider httpprovider = (HTTPMetadataProvider) provider;
                    Logger.debug("Destroy HTTPMetadataProvider +" + httpprovider.getMetadataURI());
                    httpprovider.destroy();

                } else {
                    Logger.warn("MetadataProvider can not be destroyed.");
                }
            }
            instance = null;
        } else {
            Logger.warn(
                    "ReInitalize MOAMetaDataProvider is not possible! MOA-ID Instance has to be restarted manualy");
        }
    }

    private MOAMetadataProvider() {
        ChainingMetadataProvider chainProvider = new ChainingMetadataProvider();
        Logger.info("Loading metadata");

        Map<String, MetadataProvider> providersinuse = new HashMap<String, MetadataProvider>();

        List<OnlineApplication> oaList = ConfigurationDBRead.getAllActiveOnlineApplications();

        if (oaList.size() == 0)
            Logger.info(
                    "No Online-Application configuration found. PVP 2.1 metadata provider initialization failed!");

        Iterator<OnlineApplication> oaIt = oaList.iterator();
        while (oaIt.hasNext()) {
            HTTPMetadataProvider httpProvider = null;

            try {
                OnlineApplication oa = oaIt.next();
                Logger.info("Loading metadata for: " + oa.getFriendlyName());
                OAPVP2 pvp2Config = oa.getAuthComponentOA().getOAPVP2();
                if (pvp2Config != null && MiscUtil.isNotEmpty(pvp2Config.getMetadataURL())) {
                    String metadataURL = pvp2Config.getMetadataURL();

                    if (!providersinuse.containsKey(metadataURL)) {

                        httpProvider = createNewHTTPMetaDataProvider(metadataURL, pvp2Config.getCertificate(),
                                oa.getFriendlyName(),
                                buildMetadataFilterChain(oa, metadataURL, pvp2Config.getCertificate()));

                        if (httpProvider != null)
                            providersinuse.put(metadataURL, httpProvider);

                    } else {
                        Logger.info(metadataURL + " are already added.");
                    }

                } else {
                    Logger.info(oa.getFriendlyName() + " is not a PVP2 Application skipping");
                }
            } catch (Throwable e) {
                Logger.error("Failed to add Metadata (unhandled reason: " + e.getMessage(), e);

                if (httpProvider != null) {
                    Logger.debug("Destroy failed Metadata provider");
                    httpProvider.destroy();
                }
            }
        }

        try {
            chainProvider.setProviders(new ArrayList<MetadataProvider>(providersinuse.values()));

        } catch (MetadataProviderException e) {
            Logger.error("Failed to add Metadata (unhandled reason: " + e.getMessage(), e);
        }

        internalProvider = chainProvider;
        timestamp = new Date();
    }

    private MetadataFilterChain buildMetadataFilterChain(OnlineApplication oa, String metadataURL,
            byte[] certificate) throws CertificateException {
        MetadataFilterChain filterChain = new MetadataFilterChain(metadataURL, certificate);
        filterChain.getFilters().add(new SchemaValidationFilter());

        if (oa.isIsInterfederationIDP() != null && oa.isIsInterfederationIDP()) {
            Logger.info("Online-Application is an interfederated IDP. Add addional Metadata policies");
            filterChain.getFilters().add(new InterfederatedIDPPublicServiceFilter(metadataURL, oa.getType()));

        }

        return filterChain;
    }

    private HTTPMetadataProvider createNewHTTPMetaDataProvider(String metadataURL, byte[] certificate,
            String oaName, MetadataFilterChain filter) {
        HTTPMetadataProvider httpProvider = null;
        Timer timer = null;
        MOAHttpClient httpClient = null;
        try {
            httpClient = new MOAHttpClient();

            if (metadataURL.startsWith("https:")) {
                try {
                    MOAHttpProtocolSocketFactory protoSocketFactory = new MOAHttpProtocolSocketFactory(
                            PVPConstants.SSLSOCKETFACTORYNAME,
                            AuthConfigurationProvider.getInstance().getCertstoreDirectory(),
                            AuthConfigurationProvider.getInstance().getTrustedCACertificates(), null,
                            ChainingModeType
                                    .fromValue(AuthConfigurationProvider.getInstance().getDefaultChainingMode()),
                            AuthConfigurationProvider.getInstance().isTrustmanagerrevoationchecking());

                    httpClient.setCustomSSLTrustStore(metadataURL, protoSocketFactory);

                } catch (MOAHttpProtocolSocketFactoryException e) {
                    Logger.warn("MOA SSL-TrustStore can not initialized. Use default Java TrustStore.");

                }
            }

            timer = new Timer();
            httpProvider = new HTTPMetadataProvider(timer, httpClient, metadataURL);
            httpProvider.setParserPool(new BasicParserPool());
            httpProvider.setRequireValidMetadata(true);
            httpProvider.setMinRefreshDelay(1000 * 60 * 15); //15 minutes
            httpProvider.setMaxRefreshDelay(1000 * 60 * 60 * 24); //24 hours
            //httpProvider.setRefreshDelayFactor(0.1F);

            if (filter == null) {
                filter = new MetadataFilterChain(metadataURL, certificate);
            }
            httpProvider.setMetadataFilter(filter);
            httpProvider.initialize();

            httpProvider.setRequireValidMetadata(true);

            return httpProvider;

        } catch (Throwable e) {
            if (e.getCause() != null && e.getCause().getCause() instanceof SSLHandshakeException) {
                Logger.warn("SSL-Server certificate for metadata " + metadataURL + " not trusted.", e);
            }

            Logger.error("Failed to add Metadata file for " + oaName + "[ " + e.getMessage() + " ]", e);

            if (httpProvider != null) {
                Logger.debug("Destroy failed Metadata provider");
                httpProvider.destroy();
            }

            if (timer != null) {
                Logger.debug("Destroy Timer.");
                timer.cancel();
            }

        }

        return null;
    }

    public boolean requireValidMetadata() {
        return internalProvider.requireValidMetadata();
    }

    public void setRequireValidMetadata(boolean requireValidMetadata) {
        internalProvider.setRequireValidMetadata(requireValidMetadata);
    }

    public MetadataFilter getMetadataFilter() {
        return internalProvider.getMetadataFilter();
    }

    public void setMetadataFilter(MetadataFilter newFilter) throws MetadataProviderException {
        internalProvider.setMetadataFilter(newFilter);
    }

    public XMLObject getMetadata() throws MetadataProviderException {
        return internalProvider.getMetadata();
    }

    public EntitiesDescriptor getEntitiesDescriptor(String name) throws MetadataProviderException {
        return internalProvider.getEntitiesDescriptor(name);
    }

    public EntityDescriptor getEntityDescriptor(String entityID) throws MetadataProviderException {
        return internalProvider.getEntityDescriptor(entityID);
    }

    public List<RoleDescriptor> getRole(String entityID, QName roleName) throws MetadataProviderException {
        return internalProvider.getRole(entityID, roleName);
    }

    public RoleDescriptor getRole(String entityID, QName roleName, String supportedProtocol)
            throws MetadataProviderException {
        return internalProvider.getRole(entityID, roleName, supportedProtocol);
    }

}