/******************************************************************************* * 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.io.IOException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Timer; import javax.xml.namespace.QName; 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.saml2.metadata.provider.ObservableMetadataProvider; import org.opensaml.xml.XMLObject; import org.springframework.stereotype.Service; import at.gv.egovernment.moa.id.auth.IDestroyableObject; import at.gv.egovernment.moa.id.auth.IGarbageCollectorProcessing; import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; import at.gv.egovernment.moa.id.commons.config.MOAIDConfigurationConstants; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.metadata.InterfederatedIDPPublicServiceFilter; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.metadata.PVPMetadataFilterChain; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.metadata.SchemaValidationFilter; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.Base64Utils; import at.gv.egovernment.moa.util.MiscUtil; @Service("PVPMetadataProvider") public class MOAMetadataProvider extends SimpleMOAMetadataProvider implements ObservableMetadataProvider, IGarbageCollectorProcessing, IMOARefreshableMetadataProvider, IDestroyableObject { //private static final int METADATA_GARBAGE_TIMEOUT_SEC = 604800; //7 days // private static MOAMetadataProvider instance = null; MetadataProvider internalProvider = null; private Timer timer = null; private static Object mutex = new Object(); //private Map lastAccess = null; public MOAMetadataProvider() { internalProvider = new ChainingMetadataProvider(); //lastAccess = new HashMap(); } // public static MOAMetadataProvider getInstance() { // if (instance == null) { // synchronized (mutex) { // if (instance == null) { // instance = new MOAMetadataProvider(); // // //add this to MOA garbage collector // MOAGarbageCollector.addModulForGarbageCollection(instance); // // } // } // } // return instance; // } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.config.auth.IGarbageCollectorProcessing#runGarbageCollector() */ @Override public void runGarbageCollector() { synchronized (mutex) { /**add new Metadataprovider or remove Metadataprovider which are not in use any more.**/ try { Logger.trace("Check consistence of PVP2X metadata"); addAndRemoveMetadataProvider(); } catch (ConfigurationException e) { Logger.error("Access to MOA-ID configuration FAILED.", e); } } } // private static void reInitialize() { // synchronized (mutex) { // // /**add new Metadataprovider or remove Metadataprovider which are not in use any more.**/ // if (instance != null) // try { // Logger.trace("Check consistence of PVP2X metadata"); // instance.addAndRemoveMetadataProvider(); // // } catch (ConfigurationException e) { // Logger.error("Access to MOA-ID configuration FAILED.", e); // // } // else // Logger.info("MOAMetadataProvider is not loaded."); // } // } public void fullyDestroy() { internalDestroy(); } @Override public boolean refreshMetadataProvider(String entityID) { try { IOAAuthParameters oaParam = AuthConfigurationProviderFactory.getInstance().getOnlineApplicationParameter(entityID); if (oaParam != null) { String metadataURL = oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_PVP2X_URL); if (MiscUtil.isNotEmpty(metadataURL)) { Map actuallyLoadedProviders = getAllActuallyLoadedProviders(); // check if MetadataProvider is actually loaded if (actuallyLoadedProviders.containsKey(metadataURL)) { actuallyLoadedProviders.get(metadataURL).refresh(); Logger.info("PVP2X metadata for onlineApplication: " + entityID + " is refreshed."); return true; } else { //load new Metadata Provider String certBase64 = oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_PVP2X_CERTIFICATE); if (MiscUtil.isNotEmpty(certBase64)) { byte[] cert = Base64Utils.decode(certBase64, false); String oaFriendlyName = oaParam.getFriendlyName(); if (timer == null) timer = new Timer(true); ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider; HTTPMetadataProvider newMetadataProvider = createNewHTTPMetaDataProvider(metadataURL, buildMetadataFilterChain(oaParam, metadataURL, cert), oaFriendlyName, timer); chainProvider.addMetadataProvider(newMetadataProvider); emitChangeEvent(); Logger.info("PVP2X metadata for onlineApplication: " + entityID + " is added."); return true; } else Logger.debug("Can not refresh PVP2X metadata: NO PVP2X metadata certificate for OA with Id: " + entityID); } } else Logger.debug("Can not refresh PVP2X metadata: NO PVP2X metadata URL for OA with Id: " + entityID); } else Logger.debug("Can not refresh PVP2X metadata: NO onlineApplication with Id: " + entityID); } catch (ConfigurationException e) { Logger.warn("Access MOA-ID configuration FAILED.", e); } catch (MetadataProviderException e) { Logger.warn("Refresh PVP2X metadata for onlineApplication: " + entityID + " FAILED.", e); } catch (IOException e) { Logger.warn("Refresh PVP2X metadata for onlineApplication: " + entityID + " FAILED.", e); } catch (CertificateException e) { Logger.warn("Refresh PVP2X metadata for onlineApplication: " + entityID + " FAILED.", e); } return false; } private Map getAllActuallyLoadedProviders() { 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); } } return loadedproviders; } private void addAndRemoveMetadataProvider() throws ConfigurationException { if (internalProvider != null && internalProvider instanceof ChainingMetadataProvider) { Logger.info("Reload MOAMetaDataProvider."); /*OpenSAML ChainingMetadataProvider can not remove a MetadataProvider (UnsupportedOperationException) *The ChainingMetadataProvider use internal a unmodifiableList to hold all registrated MetadataProviders.*/ Map providersinuse = new HashMap(); ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider; //get all actually loaded metadata providers Map loadedproviders = getAllActuallyLoadedProviders(); /* TODO: maybe add metadata provider destroy after timeout. * But could be a problem if one Metadataprovider load an EntitiesDescriptor * with more the multiple EntityDescriptors. If one of this EntityDesciptors * are expired the full EntitiesDescriptor is removed. * * Timeout requires a better solution in this case! */ // Date now = new Date(); // Date expioredate = new Date(now.getTime() - (METADATA_GARBAGE_TIMEOUT_SEC * 1000)); // Logger.debug("Starting PVP Metadata garbag collection (Expioredate:" // + expioredate + ")"); //load all PVP2 OAs form ConfigurationDatabase and //compare actually loaded Providers with configured PVP2 OAs Map allOAs = AuthConfigurationProviderFactory.getInstance().getConfigurationWithWildCard( MOAIDConfigurationConstants.PREFIX_MOAID_SERVICES + ".%." + MOAIDConfigurationConstants.SERVICE_UNIQUEIDENTIFIER); if (allOAs != null) { Iterator> oaInterator = allOAs.entrySet().iterator(); while (oaInterator.hasNext()) { Entry oaKeyPair = oaInterator.next(); IOAAuthParameters oaParam = AuthConfigurationProviderFactory.getInstance().getOnlineApplicationParameter(oaKeyPair.getValue()); if (oaParam != null) { String metadataurl = oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_PVP2X_URL); HTTPMetadataProvider httpProvider = null; try { if (MiscUtil.isNotEmpty(metadataurl)) { if (loadedproviders.containsKey(metadataurl)) { // PVP2 OA is actually loaded, to nothing providersinuse.put(metadataurl, loadedproviders.get(metadataurl)); loadedproviders.remove(metadataurl); //INFO: load metadata dynamically if they are requested // } else if ( MiscUtil.isNotEmpty(metadataurl) && // !providersinuse.containsKey(metadataurl) ) { // //PVP2 OA is new, add it to MOAMetadataProvider // String certBase64 = oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_PVP2X_CERTIFICATE); // if (MiscUtil.isNotEmpty(certBase64)) { // byte[] cert = Base64Utils.decode(certBase64, false); // String oaFriendlyName = oaParam.getFriendlyName(); // // // Logger.info("Loading metadata for: " + oaFriendlyName); // httpProvider = createNewHTTPMetaDataProvider( // metadataurl, // buildMetadataFilterChain(oaParam, metadataurl, cert), // oaFriendlyName); // // 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())); emitChangeEvent(); } 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 PVP-Authentication MetaDataProvider."); 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."); } } internalProvider = new ChainingMetadataProvider(); if (timer != null) timer.cancel(); } else { Logger.warn("ReInitalize MOAMetaDataProvider is not possible! MOA-ID Instance has to be restarted manualy"); } } @Deprecated /** * Load all PVP metadata from OA configuration * * This method is deprecated because OA metadata should be loaded dynamically * if the corresponding OA is requested. */ private void loadAllPVPMetadataFromKonfiguration() { ChainingMetadataProvider chainProvider = new ChainingMetadataProvider(); Logger.info("Loading metadata"); Map providersinuse = new HashMap(); try { Map allOAs = AuthConfigurationProviderFactory.getInstance().getConfigurationWithWildCard( MOAIDConfigurationConstants.PREFIX_MOAID_SERVICES + ".%." + MOAIDConfigurationConstants.SERVICE_UNIQUEIDENTIFIER); if (allOAs != null) { Iterator> oaInterator = allOAs.entrySet().iterator(); while (oaInterator.hasNext()) { Entry oaKeyPair = oaInterator.next(); IOAAuthParameters oaParam = AuthConfigurationProviderFactory.getInstance().getOnlineApplicationParameter(oaKeyPair.getValue()); if (oaParam != null) { String metadataurl = oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_PVP2X_URL); String oaFriendlyName = oaParam.getFriendlyName(); HTTPMetadataProvider httpProvider = null; try { String certBase64 = oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_PVP2X_CERTIFICATE); if (MiscUtil.isNotEmpty(certBase64) && MiscUtil.isNotEmpty(metadataurl)) { byte[] cert = Base64Utils.decode(certBase64, false); if (timer == null) timer = new Timer(true); Logger.info("Loading metadata for: " + oaFriendlyName); if (!providersinuse.containsKey(metadataurl)) { httpProvider = createNewHTTPMetaDataProvider( metadataurl, buildMetadataFilterChain(oaParam, metadataurl, cert), oaFriendlyName, timer); if (httpProvider != null) providersinuse.put(metadataurl, httpProvider); } else { Logger.info(metadataurl + " are already added."); } } else { Logger.info(oaFriendlyName + " 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(); } } } } } else Logger.info("No Online-Application configuration found. PVP 2.1 metadata provider initialization failed!"); try { chainProvider.setProviders(new ArrayList(providersinuse.values())); } catch (MetadataProviderException e) { Logger.error( "Failed to add Metadata (unhandled reason: " + e.getMessage(), e); } internalProvider = chainProvider; } catch (ConfigurationException e) { Logger.error("Access MOA-ID configuration FAILED.", e); } } private PVPMetadataFilterChain buildMetadataFilterChain(IOAAuthParameters oaParam, String metadataURL, byte[] certificate) throws CertificateException { PVPMetadataFilterChain filterChain = new PVPMetadataFilterChain(metadataURL, certificate); filterChain.getFilters().add(new SchemaValidationFilter()); if (oaParam.isInderfederationIDP()) { Logger.info("Online-Application is an interfederated IDP. Add addional Metadata policies"); filterChain.getFilters().add(new InterfederatedIDPPublicServiceFilter(metadataURL, oaParam.getBusinessService())); } return filterChain; } 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 entitiesID) throws MetadataProviderException { EntitiesDescriptor entitiesDesc = null; try { entitiesDesc = internalProvider.getEntitiesDescriptor(entitiesID); if (entitiesDesc == null) { Logger.debug("Can not find PVP metadata for entityID: " + entitiesID + " Start refreshing process ..."); if (refreshMetadataProvider(entitiesID)) return internalProvider.getEntitiesDescriptor(entitiesID); } } catch (MetadataProviderException e) { Logger.debug("Can not find PVP metadata for entityID: " + entitiesID + " Start refreshing process ..."); if (refreshMetadataProvider(entitiesID)) return internalProvider.getEntitiesDescriptor(entitiesID); } return entitiesDesc; } public EntityDescriptor getEntityDescriptor(String entityID) throws MetadataProviderException { EntityDescriptor entityDesc = null; try { entityDesc = internalProvider.getEntityDescriptor(entityID); if (entityDesc == null) { Logger.debug("Can not find PVP metadata for entityID: " + entityID + " Start refreshing process ..."); if (refreshMetadataProvider(entityID)) return internalProvider.getEntityDescriptor(entityID); } } catch (MetadataProviderException e) { Logger.debug("Can not find PVP metadata for entityID: " + entityID + " Start refreshing process ..."); if (refreshMetadataProvider(entityID)) return internalProvider.getEntityDescriptor(entityID); } // if (entityDesc != null) // lastAccess.put(entityID, new Date()); return entityDesc; } public List getRole(String entityID, QName roleName) throws MetadataProviderException { List result = internalProvider.getRole(entityID, roleName); // if (result != null) // lastAccess.put(entityID, new Date()); return result; } public RoleDescriptor getRole(String entityID, QName roleName, String supportedProtocol) throws MetadataProviderException { RoleDescriptor result = internalProvider.getRole(entityID, roleName, supportedProtocol); // if (result != null) // lastAccess.put(entityID, new Date()); return result; } /* (non-Javadoc) * @see org.opensaml.saml2.metadata.provider.ObservableMetadataProvider#getObservers() */ @Override public List getObservers() { return ((ChainingMetadataProvider) internalProvider).getObservers(); } protected void emitChangeEvent() { if ((getObservers() == null) || (getObservers().size() == 0)) { return; } List tempObserverList = new ArrayList(getObservers()); for (ObservableMetadataProvider.Observer observer : tempObserverList) if (observer != null) observer.onEvent(this); } }