/******************************************************************************* * 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.exceptions.filter.SchemaValidationException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.filter.SignatureValidationException; 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 providersinuse = new HashMap(); Map loadedproviders = new HashMap(); ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider; //make a Map of all actually loaded HTTPMetadataProvider List 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 oaList = ConfigurationDBRead .getAllActiveOnlineApplications(); Iterator 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 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(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 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 providersinuse = new HashMap(); List oaList = ConfigurationDBRead .getAllActiveOnlineApplications(); if (oaList.size() == 0) Logger.info("No Online-Application configuration found. PVP 2.1 metadata provider initialization failed!"); Iterator 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(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); } if (e.getCause() != null && e.getCause().getCause() instanceof SignatureValidationException) { Logger.warn("Signature verification for metadata" + metadataURL + " FAILED.", e); } if (e.getCause() != null && e.getCause().getCause() instanceof SchemaValidationException) { Logger.warn("Schema validation for metadata " + metadataURL + " FAILED.", 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 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); } }